前提
CSVデータをPostgreSQLデータベースに読み込み、その内容をDataGridに表示するプログラムです。
データの読み込みはImport CSV
ボタンで行います。
データは
1,AA,"{1,2,3}"
のようなCSV形式で、データの後半"{1,2,3}"をTest_Listと呼んでいます。
問題
DBが空の状態でデータを読み込むとTest_Listの内容が表示されません。
なぜか一度DBにデータが読み込まれた状態で一旦終了し再起動することで
初めてTest_Listの内容が表示されます。
実現したいこと
たとえDBが空の状態でデータを読み込んでも(一回目の起動で)
正しくTest_Listの内容を表示できるようにしたいです。
コード
MainWindow.xaml.cs
C#
1using Npgsql; 2using System; 3using System.Collections.Generic; 4using System.Data; 5using System.Linq; 6using System.Text; 7using System.Text.RegularExpressions; 8using System.Windows; 9 10namespace WpfApp1 11{ 12 public partial class MainWindow : Window 13 { 14 public DataTable dataTable { get; } = new DataTable(); 15 public MainWindow() 16 { 17 using (var conn = new NpgsqlConnection("Server=localhost; Port=5432; Database=postgres;User Id=postgres;Password=postgres;")) 18 { 19 conn.Open(); 20 using (var command = conn.CreateCommand()) 21 { 22 StringBuilder sb = new StringBuilder(); 23 24 sb.Clear(); 25 sb.Append("CREATE TABLE IF NOT EXISTS public.tblcat ("); 26 sb.Append(" num INTEGER NOT NULL"); 27 sb.Append(" , name VARCHAR(20) NOT NULL"); 28 sb.Append(" , test VARCHAR(40)"); 29 sb.Append(" , PRIMARY KEY (num, name)"); 30 sb.Append(")"); 31 32 command.CommandText = sb.ToString(); 33 command.ExecuteNonQuery(); 34 } 35 conn.Close(); 36 } 37 CreateDataTable(); 38 InitializeComponent(); 39 } 40 41 private void CreateDataTable() 42 { 43 DataColumnCollection columns = dataTable.Columns; 44 45 if (!columns.Contains(nameof(CatModel.Num))) 46 { 47 DataColumn dataColumn_Num = dataTable.Columns.Add(nameof(CatModel.Num), typeof(int)); 48 } 49 50 if (!columns.Contains(nameof(CatModel.Name))) 51 { 52 DataColumn dataColumn_Name = dataTable.Columns.Add(nameof(CatModel.Name), typeof(string)); 53 } 54 55 using (var context = new PgDbContext()) 56 { 57 var tblCat = context.Cats; 58 IQueryable<Cat> result; 59 result = from x in tblCat 60 select x; 61 62 int columnCount = 0; 63 64 foreach (Cat cat in result.ToList()) 65 { 66 var numbers = Regex.Match(cat.Test_0001, @"\{(?<numbers>[\d+,]+)\}").Groups["numbers"].Value; 67 double?[] test_list = numbers.Split(',') 68 .Select(x => String.IsNullOrEmpty(x) ? null : (double?)double.Parse(x)) 69 .ToArray<double?>(); 70 columnCount = test_list.Length; 71 } 72 73 for (int i = 0; i < columnCount; i++) 74 { 75 if (!columns.Contains($"Test_{i + 1}")) 76 { 77 dataTable.Columns.Add(new DataColumn($"Test_{i + 1}", typeof(int))); 78 } 79 } // ここにブレークポイントを貼る 80 81 foreach (Cat cat in result.ToList()) 82 { 83 var row = dataTable.NewRow(); 84 row[0] = cat.Num; 85 row[1] = cat.Name; 86 87 var numbers = Regex.Match(cat.Test_0001, @"\{(?<numbers>[\d+,]+)\}").Groups["numbers"].Value; 88 double?[] test_list = numbers.Split(',') 89 .Select(x => String.IsNullOrEmpty(x) ? null : (double?)double.Parse(x)) 90 .ToArray<double?>(); 91 columnCount = test_list.Length; 92 93 for (int i = 0; i < columnCount; i++) 94 { 95 row[2 + i] = (test_list[i] == null) ? DBNull.Value : test_list[i]; 96 } 97 dataTable.Rows.Add(row); 98 } // ここにブレークポイントを貼る 99 } 100 } 101 102 private void imp_button_Click(object sender, RoutedEventArgs e) 103 { 104 List<Cat> list = new List<Cat>() { new Cat() { Num=1, Name="AA", Test_0001="{1,2,3}"} }; 105 using (var context = new PgDbContext()) 106 { 107 var table = context.Cats; 108 foreach (Cat cat in list) 109 { 110 if (table.SingleOrDefault(x => x.Num == cat.Num) == null) 111 { 112 context.Cats.Add(cat); 113 context.SaveChanges(); 114 } 115 } 116 } 117 CreateDataTable(); 118 } 119 } 120}
MainWindow.xaml
XAML
1<Window x:Class="WpfApp1.MainWindow" 2 x:Name="mainWindow" 3 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 4 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 5 xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 6 xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 7 mc:Ignorable="d" 8 Title="List" Height="350" Width="750" 9 BorderThickness="1"> 10 11 <Grid Width="700" Height="700"> 12 <DataGrid AutoGenerateColumns="True" ItemsSource="{Binding dataTable, ElementName=mainWindow}" 13 HorizontalAlignment="Left" Margin="10,10,10,10"/> 14 <Button x:Name="imp_button" Content="Import CSV" HorizontalAlignment="Left" Margin="250,273,0,0" VerticalAlignment="Top" Width="75" Height="30" Click="imp_button_Click"/> 15 </Grid> 16</Window>
Cat.cs
C#
1using System; 2using System.ComponentModel.DataAnnotations; 3using System.ComponentModel.DataAnnotations.Schema; 4 5namespace WpfApp1 6{ 7 [Table("tblcat")] 8 class Cat 9 { 10 [Key] 11 [DatabaseGenerated(DatabaseGeneratedOption.None)] 12 [Column("num", Order = 2)] 13 public int Num { get; set; } 14 [Key] 15 [DatabaseGenerated(DatabaseGeneratedOption.None)] 16 [Column("name", Order = 1)] 17 public String Name { get; set; } 18#nullable enable 19 [Column("test")] 20 public string? Test_0001 { get; set; } 21#nullable disable 22 } 23}
CatModel.cs
C#
1using System.Collections.ObjectModel; 2 3namespace WpfApp1 4{ 5 public class CatModel 6 { 7 public CatModel(int Num, string Name, ObservableCollection<double?> Test_List) 8 { 9 this.IsChecked = false; 10 this.Num = Num; 11 this.Name = Name; 12 this.Test_List = Test_List; 13 } 14 public bool IsChecked { get; set; } 15 public int Num { get; set; } 16 public string Name { get; set; } 17 public ObservableCollection<double?> Test_List { get; set; } 18 } 19}
PgDbContext.cs
C#
1using Npgsql; 2using System.Data.Entity; 3 4namespace WpfApp1 5{ 6 class PgDbContext : DbContext 7 { 8 private const string ConnectionString = "Server=localhost;User ID=postgres;Password=postgres;Database=postgres;port=5432"; 9 public PgDbContext() : base(new NpgsqlConnection(ConnectionString), true) { } 10 public DbSet<Cat> Cats { get; set; } 11 protected override void OnModelCreating(DbModelBuilder modelBuilder) 12 { 13 modelBuilder.HasDefaultSchema("public"); 14 Database.SetInitializer<PgDbContext>(null); 15 } 16 } 17}
試したこと
CreateDataTable()の呼び出し位置をInitializeComponent();の前に移動しました。
Test_Listの型をListからObservableCollectionに変更しました。
調べてみたところ、dataTableの列は正しく追加されて5つになっているにもかかわらず、DataGridは最初の2つの列{Num}, {Name}しか表示されず、残り3つの列{Test_1}, {Test_2}, {Test_3}は表示されていないようです。
dataTable.Columns.Add(new DataColumn($"Test_{i + 1}", typeof(int)));の2行下の"}"にブレークポイントを貼り、DBを空にした状態で一回目に起動すると、自動変数タブのthis.dataTable.Columns.Results_Viewの[0][1][2][3][4]がそれぞれ{Num}, {Name}, {Test_1}, {Test_2}, {Test_3}と正しく入っていることが確認できました。
同様に、
dataTable.Rows.Add(row);の直後の"}"にブレークポイントを貼り、DBを空にした状態で一回目に起動すると、自動変数タブのthis.dataTable.Rows.Results_View.[0].ItemArrayの[0][1][2][3][4]がそれぞれ1, "AA", 1, 2, 3と正しく入っていることが確認できました。
しかし、そのままContinueすると、{Num}, {Name}の列は正しく表示されるのですが、Test_Listの部分の{Test_1}, {Test_2}, {Test_3}は表示されません。
つまり、dataTableには行・列ともに正しくデータが入っているにもかかわらず、Test_Listの部分がDataGridに表示されません。
これは何が原因と考えられますか?
どのように確認すればよいですか?
補足
Windows10
C# .NET Core5.0
VisualStudio2019
PostgreSQLのDB名、ユーザー名、パスワードは共にpostgres
スキーマはpublic
今回使ったTest_Listデータは"{1,2,3}"のように3つしかありませんが、
最終的には"{1,2,3,...,4998,4999,5000}"のように5,000列入力する予定です。
PostgreSQLの列数の上限は1,600列までであり、
通常のCSV形式"1,2,3,...,4998,4999,5000"では上限を超えてしまい、エラーになります。
そこで、Test_Listを1つの配列型"{1,2,3,...,4998,4999,5000}"で定義することで、
PostgreSQL上の列数は1つ(Excelで言うところのセル1つ)のみになり、上限を超えずに済みます。
その代わり、DataTable側で読み込む際に分解してやる必要がある訳です。
また、DataTableを導入した理由は、
実行時に任意の数の列を追加するためです。
回答2件
あなたの回答
tips
プレビュー