【译】将IDataRecord自动填充到实体的扩展方法
生活随笔
收集整理的這篇文章主要介紹了
【译】将IDataRecord自动填充到实体的扩展方法
小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.
Mapper:
Mapper的核心功能是創(chuàng)建一個(gè)委托函數(shù)并映射到一個(gè)目標(biāo)類的實(shí)例。此委托是使用表達(dá)式樹(shù)創(chuàng)建的一個(gè)lambda表達(dá)式。 在這個(gè)函數(shù)中有一個(gè)雙重循環(huán),從 DataRecord 獲取字段并和從實(shí)體類中獲取的屬性名稱比較從而填充實(shí)體實(shí)例。 所以第一個(gè)要求就是在使用這個(gè) Mapper?時(shí),DataReader的字段名必須匹配將要填充類的屬性名且要填充的屬性是可寫的。 對(duì)于每個(gè)映射屬性檢查源和目標(biāo)類型,不管他們是否為空,不管他們是否需要轉(zhuǎn)換。 我們需要的所有信息中存在 SchemaTable,但是 IDataReader 不處理 null 值。 /// <summary> /// 從提供的 DataRecord 對(duì)象創(chuàng)建新委托實(shí)例。 /// </summary> /// <param name="RecordInstance">表示一個(gè) DataRecord 實(shí)例</param> /// <returns>從提供的 DataRecord 對(duì)象創(chuàng)建新委托實(shí)例。</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();//通過(guò)在目標(biāo)屬性和字段在記錄中的循環(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)建一個(gè)表達(dá)式Expression RecordFieldExpression = Expression.Call(SourceInstance, GetSourcePropertyMethodExpression, Expression.Constant(i, typeof(int)));//獲取一個(gè)表示 SourceValue 的表達(dá)式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 的新實(shí)例并綁定到 DataRecordMemberInitExpression Body = Expression.MemberInit(Expression.New(TargetType), Bindings);return Expression.Lambda<Func<Record, Target>>(Body, SourceInstance).Compile(); }現(xiàn)在我們需要從 IDataReader 創(chuàng)建一個(gè)?sourceproperty。
/// <summary> /// 獲取表示 RecordField 真實(shí)值的表達(dá)式。 /// </summary> /// <param name="RecordFieldType">表示 RecordField 的類型。</param> /// <param name="RecordFieldExpression">表示 RecordField 的表達(dá)式。</param> /// <returns>表示 SourceValue 的表達(dá)式。</returns> private static Expression GetSourceValueExpression(Type RecordFieldType, Expression RecordFieldExpression) {//首先從 RecordField 取消裝箱值,以便我們可以使用它UnaryExpression UnboxedRecordFieldExpression = Expression.Convert(RecordFieldExpression, RecordFieldType);//獲取一個(gè)檢查 SourceField 為 null 值的表達(dá)式UnaryExpression NullCheckExpression = Expression.IsFalse(Expression.TypeIs(RecordFieldExpression, typeof(DBNull)));ParameterExpression Value = Expression.Variable(RecordFieldType, "Value");//獲取一個(gè)設(shè)置 TargetProperty 值的表達(dá)式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)換到目標(biāo)屬性。
如果它們相同,只需要在裝箱之前將源對(duì)象分配給目標(biāo)屬性。如果他們不同我們還需要將源對(duì)象強(qiáng)制轉(zhuǎn)換為目標(biāo)類型。
還有一個(gè)特殊情況,需要處理這里。沒(méi)有操作符為原始類型轉(zhuǎn)換為字符串。所以如果我們?cè)囋囘@個(gè)函數(shù)將拋出異常。這是通過(guò)調(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; }?使用示例:
公有方法包括兩個(gè)簡(jiǎn)單的擴(kuò)展方法,所以?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自动填充到实体的扩展方法的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 公司间交易学习笔记---概述
- 下一篇: maven配置ojdbc14