Entityクラスの利用
既に照会した方法ではデータベースから取得した情報をDictionary<string, object>で保持していたが、それではobjectを必要に応じてキャストする必要があったりして扱いにくいので、テーブルの型を模したEntityクラスにデータベースから取得したデータを入れて保持する
- Entityクラスはデータベースの各テーブル毎に用意されたクラス
- テーブルのフィールドに対応するプロパティを実装する
- プロパティはフィールドのDbTypeと対になるC#型で宣言する
- プロパティ名はフィールド名をキャメリングした名称で宣言する
Entityクラスの実装例
- データベース・テーブル
CREATE TABLE IF NOT EXISTS zonebase ( template_id INTEGER NOT NULL, zone_id INTEGER NOT NULL, row_num INT NOT NULL DEFAULT 0, zone_name VARCHAR(12) NOT NULL DEFAULT '', regist_date TIMESTAMP NOT NULL DEFAULT (datetime( 'now', 'localtime')) ); CREATE UNIQUE INDEX IF NOT EXISTS index_zonebase_unique on zonebase( 'template_id', 'zone_id' ); CREATE INDEX IF NOT EXISTS index_zonebase on zonebase( 'row_num', 'zone_name' );
- C# Entityクラス
public class ZoneBaseEntity { public long templateId { get; set; } = 1; public long zoneId { get; set; } = 1; public int rowNum { get; set; } = 1; public string zoneName { get; set; } = string.Empty; public DateTime registDate { get; set; } = DateTime.Now; }
リフレクションを利用してEntityクラスにデータを入力する
リフレクションによるデータ入力の手順は下記のとおり:
- データベースから取得したデータのフィールド名をキャメリング
- リフレクションにより、キャメリングされたフィールド名および取得データ型と一致するEntityのプロパティに取得データを入力
キャメリングを行う関数の実装例
/// <summary>
/// スネークケース(_区切りの文字列)をキャメルケースにして返します
/// </summary>
/// <param name="str">変換前の文字列</param>
/// <returns>変換後の文字列</returns>
public static String ToCamelCase( string str )
{
string lowerCase = str.ToLower();
string[] item = lowerCase.Split( new char[] { '_' } );
StringBuilder builder = new StringBuilder( item[0] );
for( int _i = 1; _i < item.Length; _i++ )
{
builder.Append( Capitalize( item[_i] ) );
}
return builder.ToString();
}
/// <summary>
/// 引数の先頭文字のみを大文字にして返します
/// </summary>
/// <param name="str">変換前の文字列</param>
/// <returns>変換後の文字列</returns>
private static string Capitalize( string str ) {
if( string.IsNullOrEmpty( str ) )
{
return str;
}
return new StringBuilder( str.Length )
.Append( str.Substring( 0, 1 ).ToUpper() )
.Append( str.Substring( 1 ) )
.ToString();
}
- データベースのフィールド名は大文字小文字の区別がないため一般的にスネークケースで定義される
リフレクションを使ってプロパティへアクセスする関数の実装例
/// <summary>
/// Entityオブジェクトのプロパティに値をセットします
/// ネストしたプロパティ(例:info.entity.property)にも対応しています
/// </summary>
/// <param name="entity">値をセットするエンティティオブジェクト</param>
/// <param name="nestedPropertyName">値をセットするプロパティを表す文字列</param>
/// <param name="value">セットする値</param>
public static void SetProperty( object entity, string nestedPropertyName, object value )
{
if( Convert.IsDBNull( value ) )
{
return;
}
string[] propertyNames = nestedPropertyName.Split( new char[] { '.' } );
object currentEntity = entity;
for( int i=0; i<propertyNames.Length; i++ )
{
if( i == propertyNames.Length - 1 )
{
/* ネストしたプロパティの末端プロパティなら、そのオブジェクトに値をセットします */
string propertyName = propertyNames[i];
currentEntity.GetType().GetProperty( propertyName ).SetValue( currentEntity, value );
}
else {
/* ネストしたプロパティの末端でなければそのオブジェクトを処理対象のオブジェクトに設定します
* 例:info.entity.property の entityなら、entityオブジェクトを処理対象のオブジェクトに設定します
*/
string propertyName = propertyNames[i];
currentEntity = currentEntity.GetType().GetProperty( propertyName ).GetValue( currentEntity );
}
}
}
使用例
DbCommand cmd = conn.CreateCommand();
cmd.CommandText = "select * from sampletable";
DbDataReader reader = cmd.ExecuteReader();
List<ZoneBaseEntity> entityList = new List<ZoneBaseEntity>();
while( reader .Read() )
{
ZoneBaseEntity entity = new ZoneBaseEntity();
for( int i = 0; i < reader.FieldCount; i++ )
{
string fieldName = ToCamelCase( reader.GetName( i ) );
object obj = reader.GetFieldValue<object>( i );
SetProperty( entity, fieldName, obj );
}
entityList.Add( entity );
}
cmd.Dispose();
reader.Close();
conn.Close();
return entityList;
参考:Entityのプロパティをキャメルケースでなくパスカルケースにする場合の実装例
/// <summary>
/// スネークケース(_区切りの文字列)をパスカルケースにして返します
/// </summary>
/// <param name="str">変換前の文字列</param>
/// <returns>変換後の文字列</returns>
public static String toPascalCase( string str ) {
string lowerCase = str.ToLower();
string[] item = lowerCase.Split( new char[] { '_' } );
StringBuilder builder = new StringBuilder();
for( int _i = 0; _i < item.Length; _i++ ) {
if( item[_i].Contains( '.' ) ) {
string[] sItem = item[_i].Split( new char[] { '.' } );
for( int _s = 0; _s < sItem.Length; _s++ ){
if( 0 < _s ) {
builder.Append( "." );
}
builder.Append( StringUtils.capitalize( sItem[_s] ) );
}
}
else {
builder.Append( StringUtils.capitalize( item[_i] ) );
}
}
return builder.ToString();
}