#経緯
Windows10
,Visual Studio 2019
, C#
, .NET Framework 4.7.2
, で、アプリケーションを作成しようと考えています。まだ、C#
に触り初めてから、2週間程度の初心者です。申し訳ありません。
Visual Studio 2019
に、NuGet
を用いて、System.Data.SQLite
をインストールしました。これにともない、System.Data.SQLite.EF6
, System.Data.SQLite.Generic
等も一緒にインストールされています。
画面には、Form1
だけがあります。また、次のような画像を用意しました。
この画像の大きさは、ヨコ 500 px タテ 300px です。このような画像の一部を切り取り、SQLite データベースに記録したいと考えています。
#ソースコード
Form1.cs
は、次のような内容になっています。
C#
1using System; 2using System.Collections.Generic; 3using System.ComponentModel; 4using System.Data; 5using System.Data.SQLite; 6using System.Data.SQLite.EF6; 7using System.Data.SQLite.Generic; 8using System.Data.SQLite.Linq; 9using System.Drawing; 10using System.Text; 11using System.Threading.Tasks; 12using System.Windows.Forms; 13 14namespace Grph_SQLite 15{ 16 public partial class Form1 : Form 17 { 18 Image im1, im2; 19 20 public Form1() 21 { 22 InitializeComponent(); 23 24 using (var conn = new SQLiteConnection("DataSource=data.db")) 25 { 26 conn.Open(); 27 var comm = conn.CreateCommand(); 28 string sb = "CREATE TABLE IF NOT EXISTS mydata " + 29 " (name TEXT NOT NULL, face BLOB)"; 30 comm.CommandText = sb; 31 comm.ExecuteNonQuery(); 32 conn.Close(); 33 } 34 35 #region 画像を切り取って、im1, im2 に格納し、表示する。 36 37 PictureBox pic = new PictureBox(); 38 pic.Location = new Point(10, 10); 39 pic.Size = new Size(500, 300); 40 pic.Image = Image.FromFile("data.png"); 41 Controls.Add(pic); 42 43 // 元のイメージを切り取って、im1 に貼り付ける 44 im1 = new Bitmap(300, 70); 45 Graphics g = Graphics.FromImage(im1); 46 Rectangle srcRect = new Rectangle(90, 80, 300, 70); 47 Rectangle desRect = new Rectangle(0, 0, 300, 70); 48 g.DrawImage(pic.Image, desRect, srcRect, GraphicsUnit.Pixel); 49 g.Dispose(); 50 51 PictureBox pic1 = new PictureBox(); 52 pic1.Location = new Point(10, 350); 53 pic1.Size = new Size(300, 70); 54 pic1.Image = im1; 55 Controls.Add(pic1); 56 57 // 元のイメージを切り取って、im2 に貼り付ける 58 im2 = new Bitmap(300, 70); 59 g = Graphics.FromImage(im2); 60 srcRect = new Rectangle(90, 180, 300, 70); 61 desRect = new Rectangle(0, 0, 300, 70); 62 g.DrawImage(pic.Image, desRect, srcRect, GraphicsUnit.Pixel); 63 g.Dispose(); 64 65 PictureBox pic2 = new PictureBox(); 66 pic2.Location = new Point(10, 450); 67 pic2.Size = new Size(300, 70); 68 pic2.Image = im2; 69 Controls.Add(pic2); 70 71 // 以上で、im1, im2 に画像データが格納されたことが確認できた。 72 73 #endregion 74 75 #region 画像をデータを、データベースに登録する。 76 77 using (var conn = new SQLiteConnection("DataSource=data.db")) 78 { 79 conn.Open(); 80 81 var comm = conn.CreateCommand(); 82 string str = "insert into mydata(name) values('visual studio1');"; 83 comm.CommandText = str; 84 comm.ExecuteNonQuery(); 85 86 str = "insert into mydata(name) values('visual studio2');"; 87 comm.CommandText = str; 88 comm.ExecuteNonQuery(); 89 90 // ここのところがわからない 91 str = "insert into mydata(name,face) values('visual studio3',@face);"; 92 ImageConverter converter = new ImageConverter(); 93 byte[] fdata = (byte[])converter.ConvertTo(im1, typeof(byte[])); 94 comm.Parameters["face"].Value = fdata; 95 comm.CommandText = str; 96 comm.ExecuteNonQuery(); 97 98 conn.Close(); 99 } 100 101 #endregion 102 } 103 } 104}
概略を説明すると、まず、データベースに接続します(ファイルが無ければ、新しく作ります)。次に、data.png を読み込み、pic.Image に格納し、画面にも表示させます。また、pic.Image の一部を切り取り、im1 と im2 に保存し、それぞれ画面に表示させます。
次にデータベースにレコードを登録します。mydata テーブルのうち、name については、 TEXT NOT NULL ですので、必ず登録しなければなりませんが、face BLOB については、登録しない場合もありえます。
レコードとして、「visual studio1」という name をもつデータと、「visual studio2」という name をもつデータの合計2つのデータが登録できたことは、DB Browser for SQLite を用いて、確認できました。
さて、その下のところで、name と face を両方一度に登録しようと考え、
C#
1// ここのところがわからない 2str = "insert into mydata(name,face) values('visual studio3',@face);"; 3ImageConverter converter = new ImageConverter(); 4byte[] fdata = (byte[])converter.ConvertTo(im1, typeof(byte[])); 5comm.Parameters["face"].Value = fdata; 6comm.CommandText = str; 7comm.ExecuteNonQuery();
と書きました。
#エラーメッセージ
この状態でビルドしても、エラー(コンパイル・エラー)は、ありません。しかし、実際に動かしてみると(F11で一行ずつ実行してみると)、
C#
1comm.Parameters["face"].Value = fdata;
のところで、次のようなランタイム・エラーが発生します。
System.ArgumentOutOfRangeException: 'インデックスが範囲を超えています。負でない値で、コレクションのサイズよりも小さくなければなりません。
パラメーター名:index
System.ArgumentOutOfRangeException
HResult=0x80131502
Message=インデックスが範囲を超えています。負でない値で、コレクションのサイズよりも小さくなければなりません。
パラメーター名:index
Source=mscorlib
スタック トレース:
場所 System.ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument argument, ExceptionResource resource)
場所 System.Data.SQLite.SQLiteParameterCollection.GetParameter(Int32 index)
場所 System.Data.SQLite.SQLiteParameterCollection.GetParameter(String parameterName)
場所 System.Data.SQLite.SQLiteParameterCollection.get_Item(String parameterName)
場所 Grph_SQLite.Form1..ctor() (C:\hoga\Grph_SQLite\Grph_SQLite\Form1.cs):行 94
場所 Grph_SQLite.Program.Main() (C:\hoga\Grph_SQLite\Grph_SQLite\Program.cs):行 19
#お願い
画像データを、データベースに正しく格納するためには、どのような命令を書けばよいのでしょうか。また、格納されたデータを、プログラムで読むためには、どのような命令を書けばよいのか、についても、合わせて教えていただければ幸いです。
どうか、よろしくお願いいたします。
#追記(解決方法)
以下のような方法で本件は解決しましたので、本文にも追記しておきます。
まず、本文で「ここがわからない」と書いた部分については、次のようにすると画像データを保存できることが確認できました。
C#
1// こうすればできます 2comm.CommandText = "insert into mydata(name,face) values ('visual studio3',@face);"; 3ImageConverter converter = new ImageConverter(); 4byte[] fdata = (byte[])converter.ConvertTo(im2, typeof(byte[])); 5var param = new SQLiteParameter("@face", System.Data.DbType.Binary); 6param.Value = fdata; 7comm.Parameters.Add(param); 8comm.ExecuteNonQuery();
つぎに、実際のプログラムでは、このような保存作業を、一度に 10000回程度実施する予定ですので、for 文を用いて、10000 回実施してみました。
C#
1 using (var conn = new SQLiteConnection("DataSource=data.db")) 2 { 3 conn.Open(); 4 SQLiteCommand cmd = conn.CreateCommand(); 5 ImageConverter converter = new ImageConverter(); 6 byte[] fdata; 7 SQLiteParameter param; 8 9 for (int i=0; i < 10000; i++) 10 { 11 /* 12 cmd.CommandText = "insert into mydata(name) values (@ID)"; 13 cmd.Parameters.Add(new SQLiteParameter("@ID", "visual studio4")); 14 cmd.ExecuteNonQuery(); 15 */ 16 17 cmd.CommandText = "insert into mydata(name,face) values ('visual studio5',@face);"; 18 fdata = (byte[])converter.ConvertTo(im2, typeof(byte[])); 19 param = new SQLiteParameter("@face", System.Data.DbType.Binary); 20 param.Value = fdata; 21 cmd.Parameters.Add(param); 22 cmd.ExecuteNonQuery(); 23 24 } 25 26 conn.Close(); 27 }
ところが、100000回、作業を行うのに5分から7分程度必要で、とても実用に堪えないことがわかりました。このことについては、あちこちのページで「トランザクション処理が原因で、非常に速度が遅くなる」と書いてありましたので、実際に作業の前後にトランザクションに関する命令を追加しました。
C#
1var ts = conn.BeginTransaction(); 2<途中の処理> 3ts.Commit();
この2行を追加したプログラムは、次のとおりです。
C#
1 using (var conn = new SQLiteConnection("DataSource=data.db")) 2 { 3 conn.Open(); 4 var ts = conn.BeginTransaction(); 5 6 SQLiteCommand cmd = conn.CreateCommand(); 7 ImageConverter converter = new ImageConverter(); 8 byte[] fdata; 9 SQLiteParameter param; 10 11 for (int i=0; i < 10000; i++) 12 { 13 /* 14 cmd.CommandText = "insert into mydata(name) values (@ID)"; 15 cmd.Parameters.Add(new SQLiteParameter("@ID", "visual studio4")); 16 cmd.ExecuteNonQuery(); 17 */ 18 19 cmd.CommandText = "insert into mydata(name,face) values ('visual studio5',@face);"; 20 fdata = (byte[])converter.ConvertTo(im2, typeof(byte[])); 21 param = new SQLiteParameter("@face", System.Data.DbType.Binary); 22 param.Value = fdata; 23 cmd.Parameters.Add(param); 24 cmd.ExecuteNonQuery(); 25 26 } 27 28 ts.Commit(); 29 conn.Close(); 30 }
今度は、作業は数秒で終了してしまいました。
以上、報告です。
回答2件
あなたの回答
tips
プレビュー
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2020/01/05 10:22
2020/01/05 14:58
2020/01/05 22:20