隨筆 20250519 基于MAUI Blazor整合SQLite数据库与Star打印机的详细步骤

以下是基于MAUI Blazor整合SQLite数据库与Star打印机的详细步骤,包含必要的NuGet包引入及核心代码实现:


目錄結構

一、整合SQLite数据库​

​1. 安装NuGet包​
# SQLite核心库
Install-Package sqlite-net-pcl
# SQLite平台适配库(解决跨平台兼容性问题)
Install-Package sqlitepclraw.bundle_green
2. 数据库配置​
  • ​创建数据模型​​(如Order.cs):

    using SQLite;
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    
    namespace StarMauiPrinter.Models
    {
        public class Order
        {
            [PrimaryKey, AutoIncrement]
            public int Id { get; set; }
    
            // 注文番号
            public string OrderNumber { get; set; }
    
            // 注文日時
            public string OrderTime { get; set; }
    
            // 商品コード
            public string ProductCode { get; set; }
    
            // 商品名
            public string ProductName { get; set; }
    
            // 単価
            public decimal UnitPrice { get; set; }
    
            // 数量
            public int Quantity { get; set; }
    
            // 担当者
            public string ResponsiblePerson { get; set; }
    
            // 顧客名
            public string CustomerName { get; set; }
    
            // 支払方法
            public string PaymentMethod { get; set; }
    
            // 合計金額
            public decimal TotalAmount { get; set; }
    
            public override string ToString() =>
                $"[{OrderNumber}] {ProductName} x{Quantity} 総額:{TotalAmount:C}";
        }
    }

  • ​实现数据库服务类​​(DatabaseHelper.cs):

    using SQLite;
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    
    namespace StarMauiPrinter.Services
    {
        public class DatabaseHelper
        {
            private readonly SQLiteAsyncConnection _connection;
    
            public DatabaseHelper(string dbName = "app.db3")
            {
                var dbPath = Path.Combine(FileSystem.AppDataDirectory, dbName);
                Console.WriteLine($"Database Path: {dbPath}");
                _connection = new SQLiteAsyncConnection(dbPath);
            }
    
            /// <summary>
            /// テーブルを作成する(ジェネリック)
            /// </summary>
            public Task CreateTableAsync<T>() where T : new()
            {
                return _connection.CreateTableAsync<T>();
            }
    
            /// <summary>
            /// 全てのレコードを取得する
            /// </summary>
            public Task<List<T>> GetAllAsync<T>() where T : new()
            {
                return _connection.Table<T>().ToListAsync();
            }
    
            /// <summary>
            /// 主キーIDでレコードを取得する
            /// </summary>
            public Task<T> GetByIdAsync<T>(int id) where T : new()
            {
                return _connection.FindAsync<T>(id);
            }
    
            /// <summary>
            /// レコードを挿入または更新する
            /// </summary>
            public async Task<int> SaveAsync<T>(T entity) where T : class
            {
                var type = typeof(T);
                var idProp = type.GetProperty("Id");
                var id = (int?)idProp?.GetValue(entity);
    
                if (id == 0)
                {
                    return await _connection.InsertAsync(entity);
                }
    
                var updated = await _connection.UpdateAsync(entity);
                var result = updated > 0 ? id.Value : 0;
                return result;
            }
    
            /// <summary>
            /// レコードを挿入する
            /// </summary>
            public Task<int> InsertAsync<T>(T entity) where T : new()
            {
                return _connection.InsertAsync(entity);
            }
    
            /// <summary>
            /// レコードを更新する
            /// </summary>
            public Task<int> UpdateAsync<T>(T entity) where T : new()
            {
                return _connection.UpdateAsync(entity);
            }
    
            /// <summary>
            /// レコードを削除する
            /// </summary>
            public Task<int> DeleteAsync<T>(T entity) where T : new()
            {
                return _connection.DeleteAsync(entity);
            }
    
            /// <summary>
            /// SQL文を実行してデータを取得する(クエリ)
            /// </summary>
            public Task<List<T>> QueryAsync<T>(string sql, params object[] args) where T : new()
            {
                return _connection.QueryAsync<T>(sql, args);
            }
    
            /// <summary>
            /// SQL文を実行する(非クエリ)
            /// </summary>
            public Task<int> ExecuteAsync(string sql, params object[] args)
            {
                return _connection.ExecuteAsync(sql, args);
            }
    
            / <summary>
            / トランザクションを実行する
            / </summary>
            //public Task RunInTransactionAsync(Action<SQLiteConnection> action)
            //{
            //    return Task.Run(() =>
            //    {
            //        var syncConn = _connection.GetConnection();
            //        syncConn.RunInTransaction(action);
            //    });
            //}
        }
    }
    
  •  ​​定义類service類(OrderService.cs)

    using StarMauiPrinter.Models;
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    
    namespace StarMauiPrinter.Services
    {
        public class OrderService
        {
            private readonly DatabaseHelper _db;
    
            public OrderService(DatabaseHelper dbHelper)
            {
                try
                {
                    _db = dbHelper;
                    _db.CreateTableAsync<Order>().Wait();
                }
                catch (Exception ex)
                {
                    throw;
                }
            }
    
            public Task<List<Order>> GetAllAsync()
            {
                try
                {
                    return _db.GetAllAsync<Order>();
                }
                catch (Exception ex)
                {
                    throw;
                }
            }
    
            public Task<Order> GetByIdAsync(int id)
            {
                try
                {
                    return _db.GetByIdAsync<Order>(id);
                }
                catch (Exception ex)
                {
                    throw;
                }
            }
    
            public Task<int> SaveAsync(Order order)
            {
                try
                {
                    if (string.IsNullOrWhiteSpace(order.OrderNumber))
                    {
                        order.OrderNumber = $"ORD-{DateTime.Now:yyyyMMddHHmmss}-{Guid.NewGuid().ToString("N")[..4]}";
                    }
                    return _db.SaveAsync(order);
                }
                catch (Exception ex)
                {
                    throw;
                }
            }
    
            public Task<int> DeleteAsync(Order order)
            {
                try
                {
                    return _db.DeleteAsync(order);
                }
                catch (Exception ex)
                {
                    throw;
                }
            }
    
            public Task<List<Order>> QueryByCustomer(string customerName)
            {
                try
                {
                    return _db.QueryAsync<Order>("SELECT * FROM Order WHERE CustomerName = ?", customerName);
                }
                catch (Exception ex)
                {
                    throw;
                }
            }
        }
    }
​3. 注册服务​

MauiProgram.cs中注入数据库服务:

		// sqlite
        builder.Services.AddSingleton<DatabaseHelper>();
        builder.Services.AddSingleton<OrderService>();
        builder.Services.AddSingleton<Order>();


​二、整合Star打印机​

​1. 安装Star打印机SDK​

通过NuGet安装官方或第三方SDK(需根据具体型号选择):


using StarMicronics.StarIO;
using StarMicronics.StarIOExtension;
using StarMicronics.StarIODeviceSetting;

# 示例包(需确认具体型号)
Install-Package StarIO_Extension

​2. 实现跨平台打印服务​
  • ​定义打印幫助類​​(PrintService.cs):

    using System;
    using System.Text;
    using System.Threading.Tasks;
    using System.Data;
    using System.Collections.Generic;
    using StarMicronics.StarIO;
    using StarMicronics.StarIOExtension;
    using StarMicronics.StarIODeviceSetting;
    
    namespace StarMauiPrinter.Services
    {
        /// <summary>
        /// STARプリンター操作用サービスクラス
        /// </summary>
        internal class PrintService
        {
            // プリンター接続情報 デフォルトIPアドレス
            public string PrinterIp { get; set; } = "192.168.11.75";
    
            // デフォルトポート番号
            public int PrinterPort { get; set; } = 9100;
    
            // タイムアウト設定(ミリ秒)
            private const int Timeout = 10000;
    
            /// <summary>
            /// プリンター接続情報を指定可能なコンストラクター
            /// </summary>
            /// <param name="printerIp">IPアドレス</param>
            /// <param name="printerPort">ポート番号</param>
            public PrintService(string printerIp = "192.168.11.75", int printerPort = 9100)
            {
                PrinterIp = printerIp;
                PrinterPort = printerPort;
            }
    
            /// <summary>
            /// テスト用レシート印刷メソッド(非同期)
            /// </summary>
            /// <param name="portTimeout">接続タイムアウト時間</param>
            /// <returns>操作結果メッセージ</returns>
            public async Task<string> PrintTestReceipt(int portTimeout = Timeout)
            {
                string portName = $"TCP:{PrinterIp}";
                string portSettings = "Standard";
                IPort port = null;
                try
                {
                    // プリンターポートを取得
                    port = Factory.I.GetPort(portName, portSettings, portTimeout);
    
                    // 日本JIS X 9001規格に準拠した領収書コマンドを生成
                    byte[] command = GenerateJapaneseReceipt();
                    //byte[] command = GenerateJapaneseReceipt_StarPRNT();
    
                    // 非同期印刷実行
                    await Task.Run(() => port.WritePort(command, 0, (uint)command.Length));
    
                    return "✅ 印刷が正常に完了しました!";
                }
                catch (PortException pEx)
                {
                    return $"❌ ハードウェア接続異常:\n•{pEx.Message}";
                }
                catch (EncoderFallbackException)
                {
                    return "❌ 文字コード異常:Shift_JIS非対応文字検出";
                }
                catch (Exception ex)
                {
                    return $"❌ システム例外:{ex.GetType().Name}\n{ex.Message}";
                }
                finally
                {
                    if (port != null)
                    {
                        // StarMicronics SDK仕様に基づきリソースを解放
                        Factory.I.ReleasePort(port);
                        port = null;
                    }
                }
            }
    
            /// <summary>
            /// 日本の請求書仕様に準拠した印刷指示書を生成します
            /// </summary>
            private byte[] GenerateJapaneseReceipt()
            {
                // NO
                string no = "NO.026";
                // 時間
                string formattedDateTime = DateTime.Now.ToString("yy/MM/dd (HH:mm)");
                // 担当者名
                string representative = "祐希";
                // お客様名
                string customer = "みおん";
                // 総合計
                string total = "10,000";
                // お支払
                string payment = "10,000";
                // 支払い方法
                string paymentType = "現金";
                // お預り
                string deposit = "10,000";
                // お釣り
                string change = "0";
                // セット料金
                string setfee = "10,000";
                // 小計
                string subtotal = "5,000";
    
                // プリンターの初期化
                List<byte> commands = new List<byte>();
                // ESC @ プリンターリセット
                commands.AddRange(new byte[] { 0x1B, 0x40 });
    
                // 日本語Shift_JISエンコーディング設定
                Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);
                var shiftJis = Encoding.GetEncoding("Shift_JIS");
    
                commands.AddRange(new byte[] { 0x1B, 0x61, 0x01, 0x1D, 0x21, 0x11 });
                commands.AddRange(shiftJis.GetBytes(
                    $"  【御計算書】         {formattedDateTime} {no}\n"));
    
                // 担当者・顧客情報
                commands.AddRange(new byte[] { 0x1B, 0x61, 0x00 });
                commands.AddRange(shiftJis.GetBytes(
                    $"担当者:{representative}     お客様:{customer} 様\n"));
    
                // 明細情報
                commands.AddRange(new byte[] { 0x1B, 0x61, 0x02 });
                commands.AddRange(shiftJis.GetBytes(
                    $"[4-L] 初期インデックス 21:56~\n\n" +
                    $" 総合計:                               ¥{total}\n" +
                    $"-----------------------------------------------\n" +
                    $"                    お支払:     {paymentType} ¥{payment}\n" +
                    $"          (お預り:    ¥{deposit} お釣り:    ¥{change})\n"));
    
                // 料金詳細セクション
                commands.AddRange(new byte[] { 0x1B, 0x61, 0x02 });
                commands.AddRange(shiftJis.GetBytes(
                    $"                        セット料金:     {setfee}\n" +
                    $"                    ---------------------------\n" +
                    $"                             小計:      {subtotal}\n"));
    
                // 終了処理 自動用紙切断コマンド(ESC V 66)
                commands.AddRange(new byte[] { 0x1D, 0x56, 0x41 });
                return commands.ToArray();
            }
        }
    }

  • ​注册平台服务​​:
    MauiProgram.cs中添加:

            // PrintService
            builder.Services.AddSingleton<PrintService>();


​三、Blazor组件集成​

​1. 打印頁面Print.razor
@page "/print"
@inject Services.PrintService PrintService

<h3>StarPrintTest</h3>

<button class="btn btn-primary" @onclick="PrintTest">印刷</button>

<p>@message</p>

@code {
    private string message;

    private async Task PrintTest()
    {
        try
        {   
            // 印刷サービスを呼び出す
            message = await PrintService.PrintTestReceipt();
        }
        catch (Exception ex)
        {
            message = $"エラー: {ex.Message}";
        }
    }
}
​2. 數據操作頁面(Weather.razor)
@page "/weather"
@inject Services.OrderService OrderService
@using Microsoft.JSInterop
@inject IJSRuntime JS

<h1>Weather</h1>

<div style="display: flex; align-items: center; gap: 10px;">
    <label for="orderNumber">ID:</label>
    <input type="text" id="id" @bind="id" placeholder="IDを入力してください" />
    <button @onclick="SearchOrder">検索</button>
    <button @onclick="ShowAddForm">新規</button>
    <label style="color:red">
        @if (!string.IsNullOrEmpty(errorMessage))
        {
            @errorMessage
        }
    </label>

</div>

@if (orders == null)
{
    <p><em>Loading...</em></p>
}
else
{
    <table class="table">
        <thead>
            <tr>
                <th>ID</th>
                <th>订单番号</th>
                <th>注文時間</th>
                <th>商品番号</th>
                <th>商品名称</th>
                <th>単価</th>
                <th>件数</th>
                <th>担当者</th>
                <th>お客様</th>
                <th>支払い方法</th>
                <th>最終支払い金額</th>
                <th>操作</th>
            </tr>
        </thead>
        <tbody>
            @foreach (var forecast in orders)
            {
                <tr>
                    <td>@forecast.Id</td>
                    <td>@forecast.OrderNumber</td>
                    <td>@forecast.OrderTime</td>
                    <td>@forecast.ProductCode</td>
                    <td>@forecast.ProductName</td>
                    <td>@forecast.UnitPrice.ToString("N0") 円</td>
                    <td>@forecast.Quantity 件</td>
                    <td>@forecast.ResponsiblePerson</td>
                    <td>@forecast.CustomerName</td>
                    <td>@forecast.PaymentMethod</td>
                    <td>@forecast.TotalAmount.ToString("N0") 円</td>
                    <td>
                        <button @onclick="() => DeleteOrder(forecast)">削除</button>
                        <button @onclick="() => EditOrder(forecast)">変更</button>
                    </td>
                </tr>
            }
        </tbody>
    </table>
}

<div class="modal fade @(showEditForm ? "show d-block" : "")" tabindex="-1" role="dialog" style="background-color: rgba(0,0,0,0.5);">
    <div class="modal-dialog" role="document">
        <div class="modal-content">
            <div class="modal-header">
                <h5 class="modal-title">@((newOrder?.Id ?? 0) == 0 ? "新規注文" : "注文の編集")</h5>
                <button type="button" class="btn-close" aria-label="Close" @onclick="CancelEdit"></button>
            </div>
            <div class="modal-body">
                <EditForm Model="newOrder" OnValidSubmit="SaveOrder">
                    <DataAnnotationsValidator />
                    <ValidationSummary />

                    <div class="mb-2">
                        <label>注文番号</label>
                        <InputText class="form-control" @bind-Value="newOrder.OrderNumber" />
                    </div>
                    <div class="mb-2">
                        <label>注文時間</label>
                        <InputText class="form-control" @bind-Value="newOrder.OrderTime" @bind-Value:format="yyyy-MM-dd" />
                        @* <InputDate @bind-Value="newOrder.OrderTime" /> *@
                        @* <InputText type="datetime-local" class="form-control" @bind-Value="newOrder.OrderTime" @bind-Value:format="yyyy/MM/dd HH:mm" /> *@
                    </div>
                    <div class="mb-2">
                        <label>商品番号</label>
                        <InputText class="form-control" @bind-Value="newOrder.ProductCode" />
                    </div>
                    <div class="mb-2">
                        <label>商品名称</label>
                        <InputText class="form-control" @bind-Value="newOrder.ProductName" />
                    </div>
                    <div class="mb-2">
                        <label>単価</label>
                        <InputNumber class="form-control" @bind-Value="newOrder.UnitPrice" />
                    </div>
                    <div class="mb-2">
                        <label>件数</label>
                        <InputNumber class="form-control" @bind-Value="newOrder.Quantity" />
                    </div>
                    <div class="mb-2">
                        <label>担当者</label>
                        <InputText class="form-control" @bind-Value="newOrder.ResponsiblePerson" />
                    </div>
                    <div class="mb-2">
                        <label>お客様</label>
                        <InputText class="form-control" @bind-Value="newOrder.CustomerName" />
                    </div>
                    <div class="mb-2">
                        <label>支払い方法</label>
                        <InputText class="form-control" @bind-Value="newOrder.PaymentMethod" />
                    </div>
                    <div class="mb-2">
                        <label>最終支払い金額</label>
                        <InputNumber class="form-control" @bind-Value="newOrder.TotalAmount" />
                    </div>

                    <div class="modal-footer">
                        <button type="submit" class="btn btn-primary">保存</button>
                        <button type="button" class="btn btn-secondary" @onclick="CancelEdit">取消</button>
                    </div>
                </EditForm>
            </div>
        </div>
    </div>
</div>


@code {
    private List<Models.Order> orders;
    private string id = "";
    private string errorMessage = "";
    private bool showEditForm = false;

    private Models.Order newOrder = new Models.Order();

    protected override async Task OnInitializedAsync()
    {
        await LoadDataAsync();
    }

    private async Task LoadDataAsync()
    {
        orders = await OrderService.GetAllAsync();
    }

    private async Task DeleteOrder(Models.Order order)
    {
        var isConfirmed = await JS.InvokeAsync<bool>("confirm", "本当に削除しますか?");
        if (isConfirmed)
        {
            clearMassg();
            await OrderService.DeleteAsync(order);
            await LoadDataAsync();
        }

    }

    private void clearMassg()
    {
        errorMessage = string.Empty;
    }

    private async Task SearchOrder()
    {
        clearMassg();

        if (!string.IsNullOrEmpty(id))
        {
            if (!int.TryParse(id, out int outid))
            {
                errorMessage = "IDは数字でなければなりません";
                id = string.Empty;
                return;
            }

            var order = await OrderService.GetByIdAsync(Convert.ToInt32(id));
            if (order != null)
            {
                orders = new List<Models.Order> { order };
            }
            else
            {
                orders = new List<Models.Order>();
            }
        }
        else
        {
            await LoadDataAsync();
        }
    }

    private void ShowAddForm()
    {
        clearMassg();
        newOrder = new Models.Order();
        showEditForm = true;
    }

    private void EditOrder(Models.Order order)
    {
        clearMassg();
        newOrder = new Models.Order
            {
                Id = order.Id,
                OrderNumber = order.OrderNumber,
                OrderTime = order.OrderTime,
                ProductCode = order.ProductCode,
                ProductName = order.ProductName,
                UnitPrice = order.UnitPrice,
                Quantity = order.Quantity,
                ResponsiblePerson = order.ResponsiblePerson,
                CustomerName = order.CustomerName,
                PaymentMethod = order.PaymentMethod,
                TotalAmount = order.TotalAmount
            };

        showEditForm = true;
    }

    private async Task SaveOrder()
    {
        await OrderService.SaveAsync(newOrder);
        showEditForm = false;
        newOrder = new Models.Order();
        await LoadDataAsync();
    }

    private void CancelEdit()
    {
        clearMassg();
        showEditForm = false;
    }
}


​关键注意事项​

  1. ​数据库事务​​:在批量操作时使用BeginTransaction提升性能。
  2. ​打印机兼容性​​:不同型号Star打印机需调整指令格式(如ESC/POS指令集)。
  3. ​异步处理​​:避免在UI线程执行耗时操作(如数据库查询、打印任务)。
  4. ​错误处理​​:捕获SQLite和打印机连接中的异常,确保应用稳定性。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值