Spring.Net:オブジェクトマッピング(RowMapper)を使用して、データベースにクエリーされた結果セットを返します.


データベースにアクセスするとき、多くの場合、データベースから返される結果セット(List)は、C#コードで定義したエンティティタイプにどのように変換されますか?
Spring.Netは3組のインタフェースと依頼をコールバックに定義し、開発者はこれらのコールバック方法を自分で実現または定義する必要がある.
(1)IresultSetExtractor/ResultSetExtractorDelegate:開発者がデータを反復して結果オブジェクトを返すIDataReaderオブジェクトを渡します.
(2)IrowCallback/RowCallbackDelegate:現在のローの処理に使用できるIDataReaderオブジェクトを渡します.戻り値はvoidであるため、IrowCallbackインタフェースを実装する際にクラスをステータスとして実装するのが一般的であり(すなわち、実装クラスはコールバックの結果を保存するために独自のフィールドを定義する必要がある)、匿名委任を使用する場合、コールバックの結果をローカル変数で保存するのが一般的である.
(3)IrowMapper/RowMapper Delegate:現在のローを処理し、現在のローに対応するオブジェクトを返すためのIDataReaderオブジェクトを渡します.
今回は主に3つ目について説明します.
IrowMapperは、結果セットのローをオブジェクトにマッピングする方法に開発者を集中させることができます.フレームワークは、IDataReaderを使用して反復し、結果オブジェクトを保存するIListを作成します.
Daoレイヤのコードは次のとおりです.
 
//     AdoDaoSupport,
//AdoDaoSupport           ,       ,         
public class PersonDao:AdoDaoSupport
{ 
    public IList GetPersonInfoDetailsByPersonId(string personId)
        {
            //        
            IList result = new List();
            //           
            IDbParameters parameters = AdoTemplate.CreateDbParameters();
            //    
            parameters.AddWithValue("PersonId", new Guid(personId));
            //  QueryWithRowMapper       ,     
            result = AdoTemplate.QueryWithRowMapper(
                CommandType.StoredProcedure,"SP_GetPersonInfoByPersonId", new PersonMapper(), parameters);
            return result;
        }
}

PersonMapperの定義は次のとおりです.
 
データを行単位でエンティティークラスに変換
 
 public class PersonMapper : IRowMapper where T : Person, new()
    {
        T IRowMapper.MapRow(IDataReader dataReader, int rowNum)
        {
            T view = new T();
            if (DataReaderRowFilter.RowFilter(dataReader, "PersonId"))
                view.PersonId = dataReader.GetValueOrDefault("PersonId");
            if (DataReaderRowFilter.RowFilter(dataReader, "PersonName"))
                view.PersonName = dataReader.GetValueOrDefault("PersonName");
            if (DataReaderRowFilter.RowFilter(dataReader, "Age"))
                view.Age = dataReader.GetValueOrDefault("Age");

            return view;
        }
    }

DataReaderRowFilter.RowFilterの定義は次のとおりです.
 
DataReaderにカラムがあるかどうかを判断
 
 public class DataReaderRowFilter
    {
        public static bool RowFilter(IDataReader dataReader, string columnName)
        {
            dataReader.GetSchemaTable().DefaultView.RowFilter = string.Format("ColumnName='{0}'", columnName);
            return dataReader.GetSchemaTable().DefaultView.Count > 0;
        }
    }

GetValueOrDefaultは次のように定義されています.
 
 
 public static T GetValueOrDefault(this IDataReader reader, string columnName)
        {
            int index = reader.GetOrdinal(columnName);
            T returnValue = default(T);
            if (!reader.IsDBNull(index))
            {
                returnValue = (T)Convert.ChangeType(reader[columnName], typeof(T));

                //returnValue = (T)reader[columnName];
            }
            return returnValue;
        }

マッピングの論理が少なく、ローカル変数へのアクセスが必要な場合は、委任を使用すると便利です(公式ドキュメントを参照):
 
 
 public virtual IList GetCustomersWithDelegate()
        {
            return AdoTemplate.QueryWithRowMapperDelegate(CommandType.Text, cmdText,
                        delegate(IDataReader dataReader, int rowNum)
                            {
                                Customer customer = new Customer();
                                customer.Address = dataReader.GetString(0);
                                customer.City = dataReader.GetString(1);
                                customer.CompanyName = dataReader.GetString(2);
                                customer.ContactName = dataReader.GetString(3);
                                customer.ContactTitle = dataReader.GetString(4);
                                customer.Country = dataReader.GetString(5);
                                customer.Fax = dataReader.GetString(6);
                                customer.Id = dataReader.GetString(7);
                                customer.Phone = dataReader.GetString(8);
                                customer.PostalCode = dataReader.GetString(9);
                                customer.Region = dataReader.GetString(10);
                                return customer;
                            });
        }