【译】将IDataRecord自动填充到实体的扩展方法
生活随笔
收集整理的這篇文章主要介紹了
【译】将IDataRecord自动填充到实体的扩展方法
小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.
Mapper:
Mapper的核心功能是創(chuàng)建一個委托函數(shù)并映射到一個目標類的實例。此委托是使用表達式樹創(chuàng)建的一個lambda表達式。 在這個函數(shù)中有一個雙重循環(huán),從 DataRecord 獲取字段并和從實體類中獲取的屬性名稱比較從而填充實體實例。 所以第一個要求就是在使用這個 Mapper?時,DataReader的字段名必須匹配將要填充類的屬性名且要填充的屬性是可寫的。 對于每個映射屬性檢查源和目標類型,不管他們是否為空,不管他們是否需要轉(zhuǎn)換。 我們需要的所有信息中存在 SchemaTable,但是 IDataReader 不處理 null 值。 /// <summary> /// 從提供的 DataRecord 對象創(chuàng)建新委托實例。 /// </summary> /// <param name="RecordInstance">表示一個 DataRecord 實例</param> /// <returns>從提供的 DataRecord 對象創(chuàng)建新委托實例。</returns> /// <remarks></remarks> private static Func<Record, Target> GetInstanceCreator(Record RecordInstance) {List<MemberBinding> Bindings = new List<MemberBinding>();Type TargetType = typeof(Target);Type SourceType = typeof(Record);ParameterExpression SourceInstance = Expression.Parameter(SourceType, "SourceInstance");MethodInfo GetSourcePropertyMethodExpression = SourceType.GetProperty("Item", new Type[] { typeof(int) }).GetGetMethod();DataTable SchemaTable = ((IDataReader)RecordInstance).GetSchemaTable();//通過在目標屬性和字段在記錄中的循環(huán)檢查哪些是匹配的for (int i = 0; i <= RecordInstance.FieldCount - 1; i++){foreach (PropertyInfo TargetProperty in TargetType.GetProperties(BindingFlags.Public | BindingFlags.Instance)) {//如果屬性名和字段名稱是一樣的if (TargetProperty.Name.ToLower() == RecordInstance.GetName(i).ToLower() && TargetProperty.CanWrite) {//獲取 RecordField 的類型Type RecordFieldType = RecordInstance.GetFieldType(i);//RecordField 可空類型檢查if ((bool)(SchemaTable.Rows[i]["AllowDBNull"]) == true && RecordFieldType.IsValueType) {RecordFieldType = typeof(Nullable<>).MakeGenericType(RecordFieldType);}//為 RecordField 創(chuàng)建一個表達式Expression RecordFieldExpression = Expression.Call(SourceInstance, GetSourcePropertyMethodExpression, Expression.Constant(i, typeof(int)));//獲取一個表示 SourceValue 的表達式Expression SourceValueExpression = GetSourceValueExpression(RecordFieldType, RecordFieldExpression);Type TargetPropertyType = TargetProperty.PropertyType;//從 RecordField 到 TargetProperty 類型的值轉(zhuǎn)換Expression ConvertedRecordFieldExpression = GetConvertedRecordFieldExpression(RecordFieldType, SourceValueExpression, TargetPropertyType);MethodInfo TargetPropertySetter = TargetProperty.GetSetMethod();//為屬性創(chuàng)建綁定var BindExpression = Expression.Bind(TargetPropertySetter, ConvertedRecordFieldExpression);//將綁定添加到綁定列表 Bindings.Add(BindExpression);}}}//創(chuàng)建 Target 的新實例并綁定到 DataRecordMemberInitExpression Body = Expression.MemberInit(Expression.New(TargetType), Bindings);return Expression.Lambda<Func<Record, Target>>(Body, SourceInstance).Compile(); }現(xiàn)在我們需要從 IDataReader 創(chuàng)建一個?sourceproperty。
/// <summary> /// 獲取表示 RecordField 真實值的表達式。 /// </summary> /// <param name="RecordFieldType">表示 RecordField 的類型。</param> /// <param name="RecordFieldExpression">表示 RecordField 的表達式。</param> /// <returns>表示 SourceValue 的表達式。</returns> private static Expression GetSourceValueExpression(Type RecordFieldType, Expression RecordFieldExpression) {//首先從 RecordField 取消裝箱值,以便我們可以使用它UnaryExpression UnboxedRecordFieldExpression = Expression.Convert(RecordFieldExpression, RecordFieldType);//獲取一個檢查 SourceField 為 null 值的表達式UnaryExpression NullCheckExpression = Expression.IsFalse(Expression.TypeIs(RecordFieldExpression, typeof(DBNull)));ParameterExpression Value = Expression.Variable(RecordFieldType, "Value");//獲取一個設置 TargetProperty 值的表達式Expression SourceValueExpression = Expression.Block(new ParameterExpression[] { Value }, Expression.IfThenElse(NullCheckExpression, Expression.Assign(Value, UnboxedRecordFieldExpression),Expression.Assign(Value, Expression.Constant(GetDefaultValue(RecordFieldType), RecordFieldType))), Expression.Convert(Value, RecordFieldType));return SourceValueExpression; }現(xiàn)在把源轉(zhuǎn)換到目標屬性。
如果它們相同,只需要在裝箱之前將源對象分配給目標屬性。如果他們不同我們還需要將源對象強制轉(zhuǎn)換為目標類型。
還有一個特殊情況,需要處理這里。沒有操作符為原始類型轉(zhuǎn)換為字符串。所以如果我們試試這個函數(shù)將拋出異常。這是通過調(diào)用?ToString?方法處理源。
/// <summary> /// Gets an expression representing the recordField converted to the TargetPropertyType /// </summary> /// <param name="RecordFieldType">The Type of the RecordField</param> /// <param name="UnboxedRecordFieldExpression">An Expression representing the Unboxed RecordField value</param> /// <param name="TargetPropertyType">The Type of the TargetProperty</param> /// <returns></returns> private static Expression GetConvertedRecordFieldExpression(Type RecordFieldType, Expression UnboxedRecordFieldExpression, Type TargetPropertyType) {Expression ConvertedRecordFieldExpression = default(Expression);if (object.ReferenceEquals(TargetPropertyType, RecordFieldType)){//Just assign the unboxed expressionConvertedRecordFieldExpression = UnboxedRecordFieldExpression;}else if (object.ReferenceEquals(TargetPropertyType, typeof(string))){//There are no casts from primitive types to String.//And Expression.Convert Method (Expression, Type, MethodInfo) only works with static methods.ConvertedRecordFieldExpression = Expression.Call(UnboxedRecordFieldExpression, RecordFieldType.GetMethod("ToString", Type.EmptyTypes));}else{//Using Expression.Convert works wherever you can make an explicit or implicit cast.//But it casts OR unboxes an object, therefore the double cast. First unbox to the SourceType and then cast to the TargetType//It also doesn't convert a numerical type to a String or date, this will throw an exception.ConvertedRecordFieldExpression = Expression.Convert(UnboxedRecordFieldExpression, TargetPropertyType);}return ConvertedRecordFieldExpression; }為了使用其更快,我們將使用緩存
/// <summary> /// A Singleton construct that returns a precompiled delegate if it exists, otherwise it will create one /// </summary> /// <param name="RecordInstance"></param> /// <returns></returns> static internal Func<Record, Target> GetCreator(Record RecordInstance) {if (_Creator == null){lock (SyncRoot){if (_Creator == null){//Get Creator on first access_Creator = GetInstanceCreator(RecordInstance);}}}return _Creator; }?使用示例:
公有方法包括兩個簡單的擴展方法,所以?IDataRecord?所以使用映射器是很容易的。
/// <summary> /// ExtensionMethod that creates a List<Target> from the supplied IDataReader /// </summary> /// <param name="Reader"></param> /// <returns></returns> public static List<Target> ToList<Target>(this IDataReader Reader) where Target : class, new() {List<Target> List = new List<Target>();while (Reader.Read()){List.Add(CreateInstance<Target>(Reader));}return List; }/// <summary> /// ExtensionMethod that Creates an instance<Target>) from a DataRecord. /// </summary> /// <param name="Record">The DataRecord containing the values to set on new instance</param> /// <returns>An instance of Target class</returns> public static Target CreateInstance<Target>( this IDataRecord Record) where Target : class, new() {return (Mapper<IDataRecord, Target>.GetCreator(Record))(Record); } 可以像這樣使用它:Reader.CreateInstance<MyClassInstance> 或Reader.ToList<MyClass>()總結(jié)
以上是生活随笔為你收集整理的【译】将IDataRecord自动填充到实体的扩展方法的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 公司间交易学习笔记---概述
- 下一篇: maven配置ojdbc14