社内の既存システムによって作られているmdbファイルに接続してデータの読込がしたかった
mdbとの接続
接続にはOleDbConnection、OleDbCommand 、OleDbDataReader とかを使うらしい
1.OleDbConnectionの準備
string provider = "Microsoft.Jet.OLEDB.4.0;";
string dataSource = "mdbファイルのパス";
OleDbConnection connection = new OleDbConnection("Provider = " + provider + ";Data Source = " + dataSource + ";");
OleDbConnectionコンストラクタの引数はConnectionStringで、接続先の情報とかを指定する
providerはデータベースを制御するデータベース・エンジンの種類のことらしい
mdbファイル ⇨ "Provider=Microsoft.Jet.OLEDB.4.0"
accdbファイル ⇨ "Microsoft.Ace.OLEDB.12.0"
でOK
dataSourceは接続するmdbファイルのパスを指定すればOK
2.OleDbCommandの準備
string queryString = "SQL文";
OleDbCommand command = new OleDbCommand(queryString,connection);
OleDbCommandコンストラクタは第一引数にstringで記述したSQL文、第二引数にOleDbConnectionを指定する
3.接続して読み込む
connection.Open();
var dataReader = command.ExecuteReader();
OleDbConnectionのOpen()で接続する
OleDbCommandのExecuteReader()でデータをOleDbDataReader型に読み込む
4.データを整形する
そのままDataTableへ
var table = new DataTable();
table.Load(dataReader);
データをそのままDataTableにするならDataTableのLoad()でOleDbDataReaderを指定するだけでOK
WPFのDataGridとかに表示させるだけなら多分これが一番簡単
一つづつ取り出す
while (dataReader.Read())
{
dataReader.GetValue(ordinal)
}
OleDbDataReaderのRead()でレコードを読み込む
こいつは次のレコードがなくなればfalseを返すのでwhileの条件に入れている
OleDbDataReaderのGetValue()でデータが取得できる
引数には列番号をintで指定する
列とか関係なく全部取得するなら
while (dataReader.Read())
{
for(int i =0; i< reader.FieldCount; i++)
{
Console.WriteLine(reader.GetValue(i));
}
}
でOK
ただ、これだと整形しづらい
例えばこんなDBがあるとして
ID | 名前 | 年齢 | 性別 |
---|---|---|---|
1 | 鈴木 健太 | 23 | 男 |
2 | 中村 明 | 26 | 男 |
3 | 山下 久美 | 21 | 女 |
こんなクラスのコレクションにデータを入れたい時
public class Person
{
public string Name { get; set; } = "";
public int Age { get; set; }
public string Gender { get; set; } = "";
}
mdbへの接続から一連の流れも含めて書いてみる
(WPFでReactivePropertyを使って実装)
Model
public class Model
{
public ReactiveCollection<Person> PersonList { get; set; } = new ReactiveCollection<Person>();
public void ConnectTomdb(string path)
{
string provider = "Microsoft.Jet.OLEDB.4.0;";
string queryString = "SELECT 名前,年齢,性別 FROM テーブル1";
OleDbConnection connection = new OleDbConnection("Provider = " + provider + ";Data Source = " + path + ";");
OleDbCommand command = new OleDbCommand(queryString, connection);
try
{
connection.Open();
var dataReader = command.ExecuteReader();
PersonList.Clear();
foreach (var person in DataReaderConvertToClass(dataReader))
{
PersonList.Add(person);
}
}
catch (Exception e)
{
MessageBox.Show(e.Message);
}
finally
{
connection.Close();
}
}
private IEnumerable<Person> DataReaderConvertToClass(OleDbDataReader dataReader)
{
while (dataReader.Read())
{
var person = new Person();
var properties = person.GetType().GetProperties();
for (int i = 0; i < properties.Count(); i++)
{
var data = dataReader.GetValue(i);
if (!data.GetType().Equals(DBNull.Value.GetType())) properties[i].SetValue(person, data);
//これでもOK
//if (!string.IsNullOrWhiteSpace(data.ToString())) properties[i].SetValue(person, data);
}
yield return person;
}
}
}
public class Person
{
public string Name { get; set; } = "";
public int Age { get; set; }
public string Gender { get; set; } = "";
}
OleDbDataReaderはDataReaderConvertToClassメソッドに渡して読み込み、
そこでPersonクラスを作って読み込んだ値をプロパティに詰めてPersonクラスをIEnumerableで返している
返ってきた値はPersonListにAddしている
Personクラスの各プロパティは、インスタンスにGetType()してGetProperties()すると取得できる
OleDbDataReaderのGetValue()でもindexを指定するのでfor文を使って各プロパティにSetValue()している
※GetProperties()すると上から順にプロパティを取得するので、DBの取得する列に合わせてプロパティを設定する必要が有るので注意
ちなみに、DBの値が空白だとGetValue()した時にSystem.DBNullが返ってきて、それをSetValueすると例外がでるので回避しとくこと
ViewModel
public class ViewModel
{
public Model _Model { get; set; } = new AccessDBModel();
public ReactiveProperty<string> _Path { get; set; } = new ReactiveProperty<string>();
public ReactiveCommand C_getDB { get; } = new ReactiveCommand();
public ViewModel()
{
C_getDB = _Path
.Select(x => !string.IsNullOrWhiteSpace(x))
.ToReactiveCommand();
C_getDB.Subscribe(_ => _Model.ConnectTomdb(_Path.Value));
}
}
とても単純
_PathはmdbファイルのパスでTextBoxとかにバインド
C_getDB はデータを取得するコマンドで、_Pathが空白出ない時のみ有効にしている
コマンドではModelのConnectTomdbを呼び出し、_Pathの値を渡している
View
<DataGrid x:Name="dataGrid" ItemsSource="{Binding _Model.PersonList}" HeadersVisibility="Column" >
<DataGrid.CellStyle>
<Style TargetType="DataGridCell">
<Setter Property="TextBlock.TextAlignment" Value="Center"/>
</Style>
</DataGrid.CellStyle>
</DataGrid>
<TextBox Text="{Binding _Path.Value, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
<Button Content="Load" Command="{Binding testCommand, Mode=OneWay}"/>
自動生成に任せるならこんな感じ
Collectionにしてバインドすると自分で列を指定していろいろカスタムすることもできる
また、DataTriggerを設定したら特定の値のある行に色をつけたりとかもできる
おわりに
読み込むだけならかなり簡単
とりあえず書き込みをすることはないので必要になれば調べるかも
同時接続とかは検証してない(・・;