質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.39%
.NET MAUI

.NET MAUIは、「.NET Multi-platform App UI」の略。単一コードで複数のプラットフォームに対応するクロスプラットフォームフレームワークです。Xamarin.Formsの進化系とされており、XAMLやMVVM に加え、MVUもサポートされています。

C#

C#はマルチパラダイムプログラミング言語の1つで、命令形・宣言型・関数型・ジェネリック型・コンポーネント指向・オブジェクティブ指向のプログラミング開発すべてに対応しています。

SQLite

SQLiteはリレーショナルデータベース管理システムの1つで、サーバーではなくライブラリとして使用されている。

Visual Studio

Microsoft Visual StudioはMicrosoftによる統合開発環境(IDE)です。多種多様なプログラミング言語に対応しています。

XAML

XAML(Extensible Application Markup Language)はWPF、Silverlight、Windows PhoneそしてWindows Store appsでユーザーインターフェースを定義するために使われるXML言語です。

Q&A

解決済

1回答

389閲覧

MauiにてSQliteのデータベースに画像が保存されません。

neat

総合スコア1

.NET MAUI

.NET MAUIは、「.NET Multi-platform App UI」の略。単一コードで複数のプラットフォームに対応するクロスプラットフォームフレームワークです。Xamarin.Formsの進化系とされており、XAMLやMVVM に加え、MVUもサポートされています。

C#

C#はマルチパラダイムプログラミング言語の1つで、命令形・宣言型・関数型・ジェネリック型・コンポーネント指向・オブジェクティブ指向のプログラミング開発すべてに対応しています。

SQLite

SQLiteはリレーショナルデータベース管理システムの1つで、サーバーではなくライブラリとして使用されている。

Visual Studio

Microsoft Visual StudioはMicrosoftによる統合開発環境(IDE)です。多種多様なプログラミング言語に対応しています。

XAML

XAML(Extensible Application Markup Language)はWPF、Silverlight、Windows PhoneそしてWindows Store appsでユーザーインターフェースを定義するために使われるXML言語です。

1グッド

0クリップ

投稿2024/08/12 06:02

問題の概要

実現したいこと

ファイルピッカーで選択した画像を保存ボタンを押下して画像のバイナリデータを「gazouByte.db3」に保存する。
「Get All GazouList」を押下してdb内に存在する画像の「Id」「ファイル名」「拡張子」をCollectionViewを用いて「MainPage」に表示する。

問題点

「gazouByte.db3」にバイナリデータが保存されない。
「Get All GazouList」を押下しても「got all file list」しか表示されず、「Id」「ファイル名」「拡張子」が表示されない。
※Table[("gazouByte")]は作成されていることを「DB Browser for SQlite」で確認しています。イメージ説明
[1]: https://i.sstatic.net/4aow2gVL.png

再現手順

問題が発生する具体的な手順。

  1. VisualStudio2022にてWindowsMachineを用いてデバッグを開始する。
  2. 「ファイルを選択する」を押してファイルピッカーの起動を行う。そこから画像(jpg/png)を選択。
  3. 「ファイルを選択する」の下に画像のプレビューを確認
  4. 「ファイルを保存する」を押し、赤字で「file saved」が表示されることを確認。
  5. 「DB Browser for SQlite」でデータ閲覧を行う。テーブルは作成されているが、データが保存されていない。

コード

using WorkReview.Models; using System.Collections.Generic; using Microsoft.Maui.Controls; using WorkReview.ViewModels; using System.Runtime.CompilerServices; namespace WorkReview.Views { public partial class MainPage : ContentPage { public MainPage() { InitializeComponent(); BindingContext = new MainViewModel(); } public void OnGetAllGazouClicked(object sender, EventArgs args) { List<GazouByte> gazouByte = App.GazouByteRepo.GetAllGazouBytes(); statusMessage.Text = "got all file list"; } public void OnFileSaveClicked(object sender, EventArgs args) { (BindingContext as MainViewModel).SaveGazouToDataBase(); //BindingVontextのインスタンスをもちいて.Save~メソッドを実行している statusMessage.Text = "file saved"; } public async void OnFileSelectClicked(object sender, EventArgs args) { try { var result = await FilePicker.PickAsync(); var fileExtension = result.ContentType; var fileName = result.FileName; if (result != null) { if (result.FileName.EndsWith("jpg", StringComparison.OrdinalIgnoreCase) || result.FileName.EndsWith("png", StringComparison.OrdinalIgnoreCase)) { using (var stream = await result.OpenReadAsync()) { using (var memoryStream = new MemoryStream())///using(var)はUsingステートメント,{}内の処理が終わったときにリソースが開放される { await stream.CopyToAsync(memoryStream); //memoryStreamに保存されたデータをViewModelの該当箇所へ送る (BindingContext as MainViewModel).gazouExtension = fileExtension; (BindingContext as MainViewModel).gazouName = fileName; (BindingContext as MainViewModel).gazouBinary = memoryStream.ToArray(); var previewStream = new MemoryStream(memoryStream.ToArray()); userPreview.Source = ImageSource.FromStream(() => previewStream); } } } else { statusMessage.Text = "Unsupported file type."; } } } catch (Exception ex) { statusMessage.Text = $"Error selecting file: {ex.Message}"; } } } }

Repositryはこちら

using SQLite; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using WorkReview.Models; namespace WorkReview.Models { public class GazouByteRepositry { string _gazoudbPath; public string GazouStatusMessage { get; set; } private SQLiteConnection gazouconn; public GazouByteRepositry(string gazoudbPath) { _gazoudbPath = gazoudbPath; } private void Init() //dbに接続後テーブル作成 create table when connect db.3 { if (gazouconn is not null) return; gazouconn = new SQLiteConnection(_gazoudbPath); gazouconn.CreateTable<GazouByte>(); } public void AddNewGazouByte(string gazouName, byte[] gazouBinary, string gazouExtension)//画像データをDBへ挿入 add image data to db.3 { ArgumentNullException.ThrowIfNull(gazouName, nameof(gazouExtension)); ArgumentNullException.ThrowIfNull(gazouBinary, nameof(gazouBinary)); ArgumentNullException.ThrowIfNull(gazouName, nameof(gazouName)); int gazouResult = 0; try { Init(); gazouResult = gazouconn.Insert(new GazouByte { GazouName = gazouName, GazouBinary = gazouBinary, GazouExtension = gazouExtension }); Console.WriteLine($"Insert result: {gazouResult}"); GazouStatusMessage = string.Format("{0} record(s) added (GazouName: {1})", gazouResult, gazouName); } catch (Exception ex) { GazouStatusMessage = string.Format("Failed to add {0}. Error: {1}", gazouName, ex.Message); } } public List<GazouByte> GetAllGazouBytes()//テーブルを取得 get table then create list for controll-view .MainPage { try { Init(); return gazouconn.Table<GazouByte>().ToList(); } catch (Exception ex) { GazouStatusMessage = string.Format("Failed to retrieve data .{0}", ex.Message); } return new List<GazouByte>(); } } }

テーブルはこちら

using SQLite; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using CommunityToolkit; namespace WorkReview.Models; [Table("gazouByte")] public class GazouByte { [PrimaryKey, AutoIncrement] public int Id { get; set; } public string GazouName { get; set; } public byte[] GazouBinary { get; set; } //画像のバイナリ用 Binary fo image to db.3 public string GazouExtension { get; set; } }

期待している動作

  1. デバッグを開始した段階でテーブル「"gazouByte"」がappdataに作成される。
  2. 「ファイルを選択する」を押すことによってファイルピッカーが起動、そこから画像ファイルを選択。
  3. ファイルピッカーが閉じると「ファイルを保存する」の下に選択した画像がプレビュー表示される。
  4. その後、「ファイルを保存する」によってMemoryStreamの画像が「gazouByte.db3」へ保存され、「Get All GazouList」を押下することで「gazouByte.db3」に保存されたファイルの「Id」「ファイル名」「拡張子」がCollectionViewに表示される。

試したこと

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

エラーメッセージやログ

  
画像は保存されませんでしたが、エラーメッセージが出ることはありませんでした。

環境

Windows 11 Home (23H2)
VisualStudio Community 2022 preview 17.11.0 Preview 4.0
Microsoft.Maui.Controls 8.0.80
Microsoft .NET SDK 8.0.107(x64)
SQlitePCLRaw.bundle_green 2.1.9
sqlite-net-pcl 1.9.172
CommunityToolkit.Mvvm 8.2.2

補足情報

コードの全体は次のURLの「WorkReview/WorkReview/」の中にあります

https://github.com/rasukuosu/WorkReview.git

TN8001👍を押しています

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

TN8001

2024/08/12 10:18

[c# - MauiにてSQliteのデータベースに画像が保存されません。 - スタック・オーバーフロー](https://ja.stackoverflow.com/questions/99871) > やむを得ず複数のサイトに質問を投稿された場合は、質問内容にマルチポストをする理由を書き、他のサイトの投稿へのリンクを貼ってください。 また、解決した際には必ずteratail及びすべての投稿に解決した旨と、どのように解決したかを記載してください。 [ヘルプ|他のサービスにも質問投稿をしたい(してしまった)](https://teratail.com/help#posted-otherservice) 削除しろと言っているのではありません。 回答もコメントもつかない以上、わたしでも同様にすると思います。 ただマナーとして上記は守っていただきたいです^^
neat

2024/08/13 14:20

ご指摘ありがとうございます。m(_ _)m 指摘していただいた投稿を削除しました。 次回以降は同様のことがないように努力します。
guest

回答1

0

ベストアンサー

「gazouByte.db3」にバイナリデータが保存されない。

例外を握り潰さないでください(少なくともDebug.WriteLine等で分かるようにしないと、まともにデバッグもできません)

ファイルはできるのにレコードが追加されないということは、gazouconn.Insertが失敗しているんでしょう。

提示コードだけでは原因は見えませんが、GazouByteRepositryが2つできています。
MainViewModelが使うGazouByteRepositryは、_gazoudbPathが(フルパスでなく)ファイル名だけで開けずに例外を出しています。

とりあえずこうでもすればデータは入ります(どうすべきかはわかりませんが)

cs

1public partial class MainViewModel : ObservableObject 2{ 3 private GazouByteRepositry _gazouByteRepositry; 4 5 public MainViewModel() 6 { 7 //_gazouByteRepositry = new GazouByteRepositry("gazouByte.db3"); 8 _gazouByteRepositry = App.GazouByteRepo; 9 }

何故ファイルだけはできるかというと、↓に入ったときは(フルパスが入っている)App.GazouByteRepoを直接使っているためです。

cs

1public void OnGetAllGazouClicked(object sender, EventArgs args) 2{ 3 List<GazouByte> gazouByte = App.GazouByteRepo.GetAllGazouBytes(); 4 statusMessage.Text = "got all file list"; 5}

「Get All GazouList」を押下しても「got all file list」しか表示されず、「Id」「ファイル名」「拡張子」が表示されない。

CollectionViewに何も入れていないので当然です。MainViewModelにコレクションを用意してバインドしてください。

バインドについてはこの辺りを確認してください。
データ バインディングの基礎 - .NET MAUI | Microsoft Learn
データ バインディングと MVVM - .NET MAUI | Microsoft Learn

Toolkitの使い方はこの辺りを確認してください。
MVVM Toolkit の概要 - Community Toolkits for .NET | Microsoft Learn

xml:MainPage.xaml

1<?xml version="1.0" encoding="utf-8" ?> 2<ContentPage 3 x:Class="WorkReview.Views.MainPage" 4 xmlns="http://schemas.microsoft.com/dotnet/2021/maui" 5 xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" 6 xmlns:models="clr-namespace:WorkReview.Models" 7 xmlns:viewModels="clr-namespace:WorkReview.ViewModels" 8 Title="MainPage" 9 x:DataType="viewModels:MainViewModel"> 10 <ContentPage.BindingContext> 11 <viewModels:MainViewModel /> 12 </ContentPage.BindingContext> 13 <Grid 14 Padding="5" 15 ColumnSpacing="10" 16 RowDefinitions="Auto,Auto,Auto,Auto,Auto,*" 17 RowSpacing="10"> 18 <Label Text="{Binding StatusMessage}" TextColor="Red" /> 19 <Button Grid.Row="1" Command="{Binding GetAllGazouCommand}" Text="Get All GazouList" /> 20 <CollectionView Grid.Row="2" ItemsSource="{Binding GazouBytes}"> 21 <CollectionView.ItemTemplate> 22 <DataTemplate x:DataType="models:GazouByte"> 23 <Grid ColumnDefinitions="*,2*,*"> 24 <Label Text="{Binding Id}" TextColor="{StaticResource Primary}" /> 25 <Label Grid.Column="1" Text="{Binding GazouName}" TextColor="{StaticResource Primary}" /> 26 <Label Grid.Column="2" Text="{Binding GazouExtension}" TextColor="{StaticResource Primary}" /> 27 </Grid> 28 </DataTemplate> 29 </CollectionView.ItemTemplate> 30 </CollectionView> 31 <Button Grid.Row="3" Command="{Binding FileSaveCommand}" Text="ファイルを保存する" /> 32 <Button Grid.Row="4" Command="{Binding FileSelectCommand}" Text="ファイルを選択する" /> 33 <Image Grid.Row="5" Source="{Binding UserPreview}" /> 34 </Grid> 35</ContentPage>

cs:MainPage.xaml.cs

1namespace WorkReview.Views; 2 3public partial class MainPage : ContentPage 4{ 5 public MainPage() => InitializeComponent(); 6}

cs:MainViewModel.cs

1using CommunityToolkit.Mvvm.ComponentModel; 2using CommunityToolkit.Mvvm.Input; 3using WorkReview.Models; 4 5namespace WorkReview.ViewModels; 6 7public partial class MainViewModel : ObservableObject 8{ 9 [ObservableProperty] private string? statusMessage; 10 [ObservableProperty] private List<GazouByte>? gazouBytes; 11 [ObservableProperty] private ImageSource? userPreview; 12 13 private string? gazouName; 14 private byte[]? gazouBinary; 15 private string? gazouExtension; 16 17 public MainViewModel() { } 18 19 [RelayCommand] 20 private void OnGetAllGazou() 21 { 22 GazouBytes = App.GazouByteRepo.GetAllGazouBytes(); 23 StatusMessage = "got all file list"; 24 } 25 26 [RelayCommand] 27 private void OnFileSave() 28 { 29 if (gazouName == null) return; 30 31 var gazouByte = new GazouByte 32 { 33 GazouName = gazouName, 34 GazouBinary = gazouBinary!, 35 GazouExtension = gazouExtension!, 36 }; 37 App.GazouByteRepo.AddNewGazouByte(gazouByte); 38 StatusMessage = "file saved"; 39 } 40 41 [RelayCommand] 42 private async Task OnFileSelect() 43 { 44 try 45 { 46 var result = await FilePicker.PickAsync(); 47 if (result == null) return; 48 49 var fileName = result.FileName; 50 if (fileName.EndsWith("jpg", StringComparison.OrdinalIgnoreCase) || 51 fileName.EndsWith("png", StringComparison.OrdinalIgnoreCase)) 52 { 53 using (var stream = await result.OpenReadAsync()) 54 using (var memoryStream = new MemoryStream()) 55 { 56 await stream.CopyToAsync(memoryStream); 57 gazouName = fileName; 58 gazouBinary = memoryStream.ToArray(); 59 gazouExtension = result.ContentType; 60 61 var previewStream = new MemoryStream(memoryStream.ToArray()); 62 UserPreview = ImageSource.FromStream(() => previewStream); 63 } 64 } 65 else 66 { 67 StatusMessage = "Unsupported file type."; 68 } 69 } 70 catch (Exception ex) 71 { 72 StatusMessage = $"Error selecting file: {ex.Message}"; 73 } 74 } 75} 76

cs:GazouByteRepositry.cs

1using SQLite; 2using System.Diagnostics; 3 4namespace WorkReview.Models; 5 6public class GazouByteRepositry 7{ 8 private string gazoudbPath; 9 10 public GazouByteRepositry(string gazoudbPath) 11 { 12 this.gazoudbPath = gazoudbPath; 13 14 using var gazouconn = new SQLiteConnection(gazoudbPath); 15 gazouconn.CreateTable<GazouByte>(); 16 } 17 18 public void AddNewGazouByte(GazouByte gazouByte) 19 { 20 ArgumentNullException.ThrowIfNull(gazouByte); 21 22 try 23 { 24 using var gazouconn = new SQLiteConnection(gazoudbPath); 25 var gazouResult = gazouconn.Insert(gazouByte); 26 27 Debug.WriteLine($"Insert result: {gazouResult}"); 28 Debug.WriteLine($"{gazouResult} record(s) added (GazouName: {gazouByte.GazouName})"); 29 } 30 catch (Exception ex) 31 { 32 Debug.WriteLine($"Failed to add {gazouByte.GazouName}. Error: {ex.Message}"); 33 } 34 } 35 36 public List<GazouByte> GetAllGazouBytes() 37 { 38 try 39 { 40 using var gazouconn = new SQLiteConnection(gazoudbPath); 41 return gazouconn.Table<GazouByte>().ToList(); 42 } 43 catch (Exception ex) 44 { 45 Debug.WriteLine($"Failed to retrieve data .{ex.Message}"); 46 } 47 return []; 48 } 49}

アプリ動画


例外処理はもっと慎重に考えてもらいたい(リカバリーできるものだけ個別にキャッチ等)が、とりあえず形は変えずに最低限メッセージだけは出した。
「ViewModelでダイアログ出すのはどうなんだ?」ってのはあるが本題ではないので^^;
ほかにも気になる点はあるが、あまり変えても何なんでこのくらいで...

投稿2024/08/12 10:17

編集2024/08/14 09:47
TN8001

総合スコア9722

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

neat

2024/08/25 09:43

TN8001さん回答ありがとうございます。 解決しました。 おかげさまで丁寧な回答からエラー理由もわかり、大変勉強になりました。 またここから新しい機能を追加したりできそうです。 本当にありがとうございました。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.39%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問