MauiとSQliteでdbにデータが保存できなかったdb編

まえがき(必読ではありません)

 MauiとSQLiteをつかってファイル管理のソフトを作ろうと思い、次の

.NET MAUI アプリで SQLite を使用してローカル データを格納する - Training | Microsoft Learn

- YouTube*1

で作成したソフトを改造してdbに画像を保存できるようにしようと考えた。
しかし、学習ページと動画を見ながらやっても、dbに画像が保存できない。
 また、ListViewに画像一覧を表示することもできなかった。

 そこで、TeratailとStackOverflow(英)に質問してみた。
  正直、このページよりも実際のやり取りを先述ページから見たほうがわかると思う。

しかし頭の中で整理するためここに一応記す。

概要

やりたいこと

  • ビルド後appファイルにdb.3ファイルを作成
  • file pickerで選択した画像をプレビューに表示
  • 保存押下でdbに画像が保存される

  • 「Get All GazouList」を押下することで「gazouByte.db3」に保存されたファイルの「Id」「ファイル名」「拡張子」がCollectionViewに表示される。

試したこと

  • GazouByteRepositry.AddNewGazouByte メソッドで例外処理が正しく行われていることを確認
  • 画像を選択し、保存ボタンを押した後、再度画像を選択せずに保存ボタンを押すとSystem.NullReferenceExceptionが発生することを確認
  • Table[("gazouByte")]が作成されていることを「DB Browser for SQlite」で確認

    DBbrowserでのdb

以下の環境で行った

  • 日付 2024/08

  • Visual Studio 2022 バージョン 17.10.4 PRE

  • Maui

  • CommunityToolkit MVVM
  • SQlite

実際の質問

原因

画像が保存できなかった原因

  • データーベースが二重に作成されており、画像の保存ができなかった。
  • MauiProgram.csの次の部分で既に画像dbが作成されているにもかかわらず、  MainViewModelで上書き作成されていたため正しいPathに画像が保存できなかった。
  •  

    namespace WorkReview
    {
        public static class MauiProgram
        {
            public static MauiApp CreateMauiApp()
            {
                var builder = MauiApp.CreateBuilder();
                builder
                    .UseMauiApp<App>()
                    .ConfigureFonts(fonts =>
                    {
                        fonts.AddFont("OpenSans-Regular.ttf", "OpenSansRegular");
                        fonts.AddFont("OpenSans-Semibold.ttf", "OpenSansSemibold");
                    });
                string dbPath = FileAccessHelper.GetLocalFilePath("gazouByte.db3");
                builder.Services.AddSingleton< GazouByteRepositry> (s => ActivatorUtilities.CreateInstance< GazouByteRepositry> (s, dbPath));//Appの部分にインスタンスとしてdbを作成

         ↑の部分

                return builder.Build();
            }
        }
    }

 

  •  

    namespace WorkReview.ViewModels
    {
        public partial class MainViewModel : ObservableObject
        {
            private GazouByteRepositry _gazouByteRepositry;

            public MainViewModel()
            {
                _gazouByteRepositry = new GazouByteRepositry("gazouByte.db3");

     

         ↑の部分で新しいインスタンスで上書きしている                   
             }

            [ObservableProperty]
            public string gazouName;

            [ObservableProperty]
            public string gazouPath;
            [ObservableProperty]
            public byte gazouBinary;
            [ObservableProperty]
            public string gazouExtension;


            public void SaveGazouToDataBase()//MainPageから渡された画像データをRepositryへ送る。
            {
                var gazouByte = new GazouByte
                {
                    GazouName = gazouName,
                    GazouBinary = gazouBinary,
                    GazouExtension = gazouExtension

                };

                _gazouByteRepositry.AddNewGazouByte(GazouName, GazouBinary, GazouExtension);
                
            }

        }
    }


解決法

MainViewModelの該当部を削除し、App.GazouByteRepo.メソッド名で直接dbPathへ処理を行うようにした。

  • namespace WorkReview.ViewModels;

    public partial class MainViewModel : ObservableObject
    {


        [ObservableProperty] 
        private string? statusMessage;

        [ObservableProperty] 
        private List<GazouByte>? gazouBytes; //MainPageのListView用のリスト。ListViewにはC#側にリストを作る必要がある。

        [ObservableProperty] 
        private ImageSource? userPreview; //MainPageのプレビュー用


        private string? gazouName; //ファイル名

        private byte? gazouBinary; //画像のバイナリデータ

        private string? gazouExtension; //拡張子情報

     


        public MainViewModel()
        { }

        [RelayCommand]
        private void OnGetAllGazou() //画像リストの取得ボタン
        {
            GazouBytes = App.GazouByteRepo.GetAllGazouBytes();
            StatusMessage = "got all file list"; StatusMessage = "got all file list";

        }

        [RelayCommand]
        private void OnFileSave()
        {
            if (gazouName == null) return;

            var gazouByte = new GazouByte
            {
                GazouName = gazouName!,
                GazouBinary = gazouBinary!, //!はNull出ないことを宣言

                GazouExtension = gazouExtension!,
            };

            App.GazouByteRepo.AddNewGazouByte(gazouByte);

            StatusMessage = "file saved";

        }

        [RelayCommand]
        private async Task OnFileSelect() //非同期でTaskとして結果を返す

        {

            try

            {

                var result = await FilePicker.PickAsync();

                if (result == null) return;


                var fileName = result.FileName;

                if (fileName.EndsWith("jpg", StringComparison.OrdinalIgnoreCase) ||

                    fileName.EndsWith("png", StringComparison.OrdinalIgnoreCase))

                {

                    using (var stream = await result.OpenReadAsync())

                    using (var memoryStream = new MemoryStream())

                    {

                        await stream.CopyToAsync(memoryStream);

                        gazouName = fileName;

                        gazouBinary = memoryStream.ToArray();

                        gazouExtension = result.ContentType;


                        var previewStream = new MemoryStream(memoryStream.ToArray());

                        UserPreview = ImageSource.FromStream(() => previewStream);

                    }

                }

                else

                {

                    StatusMessage = "Unsupported file type.";

                }

            }

            catch (Exception ex)

            {

                StatusMessage = $"Error selecting file: {ex.Message}";

            }

        }

    }

次回

「MauiとSQliteでdbにデータが保存できなかったListView編」へ続く

neattanaka.hatenablog.com

 

*1:Youtubeと.Net公式に追いながら学習できる動画もあります