using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Data; using System.Reflection; using System.Reflection.Emit; namespace SqlSugar { /// /// ** description:IDataReader Entity Builder /// ** author:sunkaixuan /// ** date:2017/4/2 /// ** qq:610262374 /// public partial class IDataReaderEntityBuilder { private SqlSugarClient Context = null; private IDataReaderEntityBuilder DynamicBuilder; private IDataRecord DataRecord; private List ReaderKeys { get; set; } private IDataReaderEntityBuilder() { } public IDataReaderEntityBuilder(SqlSugarClient context, IDataRecord dataRecord) { this.Context = context; this.DataRecord = dataRecord; this.DynamicBuilder = new IDataReaderEntityBuilder(); this.ReaderKeys = new List(); } #region fields private static readonly MethodInfo isDBNullMethod = typeof(IDataRecord).GetMethod("IsDBNull", new Type[] { typeof(int) }); private static readonly MethodInfo getValueMethod = typeof(IDataRecord).GetMethod("get_Item", new Type[] { typeof(int) }); private static readonly MethodInfo getBoolean = typeof(IDataRecord).GetMethod("GetBoolean", new Type[] { typeof(int) }); private static readonly MethodInfo getByte = typeof(IDataRecord).GetMethod("GetByte", new Type[] { typeof(int) }); private static readonly MethodInfo getDateTime = typeof(IDataRecord).GetMethod("GetDateTime", new Type[] { typeof(int) }); private static readonly MethodInfo getDecimal = typeof(IDataRecord).GetMethod("GetDecimal", new Type[] { typeof(int) }); private static readonly MethodInfo getDouble = typeof(IDataRecord).GetMethod("GetDouble", new Type[] { typeof(int) }); private static readonly MethodInfo getFloat = typeof(IDataRecord).GetMethod("GetFloat", new Type[] { typeof(int) }); private static readonly MethodInfo getGuid = typeof(IDataRecord).GetMethod("GetGuid", new Type[] { typeof(int) }); private static readonly MethodInfo getInt16 = typeof(IDataRecord).GetMethod("GetInt16", new Type[] { typeof(int) }); private static readonly MethodInfo getInt32 = typeof(IDataRecord).GetMethod("GetInt32", new Type[] { typeof(int) }); private static readonly MethodInfo getInt64 = typeof(IDataRecord).GetMethod("GetInt64", new Type[] { typeof(int) }); private static readonly MethodInfo getString = typeof(IDataRecord).GetMethod("GetString", new Type[] { typeof(int) }); private static readonly MethodInfo getEnum = typeof(IDataRecordExtensions).GetMethod("getEnum"); private static readonly MethodInfo getConvertFloat = typeof(IDataRecordExtensions).GetMethod("GetConvertFloat"); private static readonly MethodInfo getConvertBoolean = typeof(IDataRecordExtensions).GetMethod("GetConvertBoolean"); private static readonly MethodInfo getConvertByte = typeof(IDataRecordExtensions).GetMethod("GetConvertByte"); private static readonly MethodInfo getConvertChar = typeof(IDataRecordExtensions).GetMethod("GetConvertChar"); private static readonly MethodInfo getConvertDateTime = typeof(IDataRecordExtensions).GetMethod("GetConvertDateTime"); private static readonly MethodInfo getConvertDecimal = typeof(IDataRecordExtensions).GetMethod("GetConvertDecimal"); private static readonly MethodInfo getConvertDouble = typeof(IDataRecordExtensions).GetMethod("GetConvertDouble"); private static readonly MethodInfo getConvertGuid = typeof(IDataRecordExtensions).GetMethod("GetConvertGuid"); private static readonly MethodInfo getConvertInt16 = typeof(IDataRecordExtensions).GetMethod("GetConvertInt16"); private static readonly MethodInfo getConvertInt32 = typeof(IDataRecordExtensions).GetMethod("GetConvertInt32"); private static readonly MethodInfo getConvetInt64 = typeof(IDataRecordExtensions).GetMethod("GetConvetInt64"); private static readonly MethodInfo getConvertEnum_Null = typeof(IDataRecordExtensions).GetMethod("GetConvertEnum_Null"); private static readonly MethodInfo getOtherNull = typeof(IDataRecordExtensions).GetMethod("GetOtherNull"); private static readonly MethodInfo getOther = typeof(IDataRecordExtensions).GetMethod("GetOther"); private static readonly MethodInfo getEntity = typeof(IDataRecordExtensions).GetMethod("GetEntity", new Type[] { typeof(SqlSugarClient) }); private delegate T Load(IDataRecord dataRecord); private Load handler; #endregion public T Build(IDataRecord dataRecord) { return handler(dataRecord); } public IDataReaderEntityBuilder CreateBuilder(Type type) { for (int i = 0; i < this.DataRecord.FieldCount; i++) { this.ReaderKeys.Add(this.DataRecord.GetName(i)); } DynamicMethod method = new DynamicMethod("SqlSugarEntity", type, new Type[] { typeof(IDataRecord) }, type, true); ILGenerator generator = method.GetILGenerator(); LocalBuilder result = generator.DeclareLocal(type); generator.Emit(OpCodes.Newobj, type.GetConstructor(Type.EmptyTypes)); generator.Emit(OpCodes.Stloc, result); var mappingColumns = Context.MappingColumns.Where(it => it.EntityName.Equals(type.Name, StringComparison.CurrentCultureIgnoreCase)).ToList(); var properties = type.GetProperties(); foreach (var propertyInfo in properties) { string fileName = propertyInfo.Name; if (mappingColumns != null) { var mappInfo = mappingColumns.SingleOrDefault(it => it.EntityName.Equals(propertyInfo.Name)); if (mappInfo != null) { fileName = mappInfo.DbColumnName; } } if (Context.IgnoreComumns != null && Context.IgnoreComumns.Any(it => it.EntityPropertyName.Equals(propertyInfo.Name, StringComparison.CurrentCultureIgnoreCase) && it.EntityName.Equals(type.Name, StringComparison.CurrentCultureIgnoreCase))) { continue; } if (propertyInfo != null && propertyInfo.GetSetMethod() != null) { if (propertyInfo.PropertyType.IsClass()) { BindClass(generator, result, propertyInfo); } else { if (this.ReaderKeys.Any(it => it.Equals(fileName, StringComparison.CurrentCultureIgnoreCase))) { BindField(generator, result, propertyInfo, fileName); } } } } generator.Emit(OpCodes.Ldloc, result); generator.Emit(OpCodes.Ret); DynamicBuilder.handler = (Load)method.CreateDelegate(typeof(Load)); return DynamicBuilder; } private void BindClass(ILGenerator generator, LocalBuilder result, PropertyInfo propertyInfo) { } private void BindField(ILGenerator generator, LocalBuilder result, PropertyInfo propertyInfo, string fileName) { int i = DataRecord.GetOrdinal(fileName); Label endIfLabel = generator.DefineLabel(); generator.Emit(OpCodes.Ldarg_0); generator.Emit(OpCodes.Ldc_I4, i); generator.Emit(OpCodes.Callvirt, isDBNullMethod); generator.Emit(OpCodes.Brtrue, endIfLabel); generator.Emit(OpCodes.Ldloc, result); generator.Emit(OpCodes.Ldarg_0); generator.Emit(OpCodes.Ldc_I4, i); BindMethod(generator, propertyInfo, i); generator.Emit(OpCodes.Callvirt, propertyInfo.GetSetMethod()); generator.MarkLabel(endIfLabel); } private void BindMethod(ILGenerator generator, PropertyInfo bindProperty, int ordinal) { var isNullableType = false; var bindType = PubMethod.GetUnderType(bindProperty, ref isNullableType); string dbTypeName = DataRecord.GetDataTypeName(ordinal); string propertyName = bindProperty.Name; var bind = Context.Database.DbBind; List guidThrow = bind.GuidThrow; List intThrow = bind.IntThrow; List stringThrow = bind.StringThrow; List decimalThrow = bind.DecimalThrow; List doubleThrow = bind.DoubleThrow; List dateThrow = bind.DateThrow; List shortThrow = bind.ShortThrow; MethodInfo method = null; var transformedPropertyName = bind.ChangeDBTypeToCSharpType(dbTypeName); var objTypeName = bindType.Name.ToLower(); var isEnum = bindType.IsEnum; if (isEnum) { transformedPropertyName = "enum"; } else if (transformedPropertyName.IsIn("byte[]", "other", "object") || dbTypeName.Contains("hierarchyid")) { generator.Emit(OpCodes.Call, getValueMethod); generator.Emit(OpCodes.Unbox_Any, bindProperty.PropertyType); return; } if (isNullableType) { switch (transformedPropertyName) { case "int": CheckType(intThrow, objTypeName, transformedPropertyName, propertyName); var isNotInt = objTypeName != "int32"; if (isNotInt) method = getOtherNull.MakeGenericMethod(bindType); else method = getConvertInt32; break; case "bool": if (objTypeName != "bool" && objTypeName != "boolean") method = getOtherNull.MakeGenericMethod(bindType); else method = getConvertBoolean; break; case "string": CheckType(stringThrow, objTypeName, transformedPropertyName, propertyName); method = getString; break; case "dateTime": CheckType(dateThrow, objTypeName, transformedPropertyName, propertyName); if (objTypeName != "datetime") method = getOtherNull.MakeGenericMethod(bindType); else method = getConvertDateTime; break; case "decimal": CheckType(decimalThrow, objTypeName, transformedPropertyName, propertyName); var isNotDecimal = objTypeName != "decimal"; if (isNotDecimal) method = getOtherNull.MakeGenericMethod(bindType); else method = getConvertDecimal; break; case "double": CheckType(doubleThrow, objTypeName, transformedPropertyName, propertyName); var isNotDouble = objTypeName != "double"; if (isNotDouble) method = getOtherNull.MakeGenericMethod(bindType); else method = getConvertDouble; break; case "guid": CheckType(guidThrow, objTypeName, transformedPropertyName, propertyName); if (objTypeName != "guid") method = getOtherNull.MakeGenericMethod(bindType); else method = getConvertGuid; break; case "byte": method = getConvertByte; break; case "enum": method = getConvertEnum_Null.MakeGenericMethod(bindType); break; case "short": CheckType(shortThrow, objTypeName, transformedPropertyName, propertyName); var isNotShort = objTypeName != "int16" && objTypeName != "short"; if (isNotShort) method = getOtherNull.MakeGenericMethod(bindType); else method = getConvertInt16; break; default: method = getOtherNull.MakeGenericMethod(bindType); break; } generator.Emit(OpCodes.Call, method); } else { switch (transformedPropertyName) { case "int": CheckType(intThrow, objTypeName, transformedPropertyName, propertyName); var isNotInt = objTypeName != "int32"; if (isNotInt) method = getOther.MakeGenericMethod(bindType); else method = getInt32; break; case "bool": if (objTypeName != "bool" && objTypeName != "boolean") method = getOther.MakeGenericMethod(bindType); else method = getBoolean; break; case "string": CheckType(stringThrow, objTypeName, transformedPropertyName, propertyName); method = getString; break; case "dateTime": CheckType(dateThrow, objTypeName, transformedPropertyName, propertyName); if (objTypeName != "datetime") method = getOther.MakeGenericMethod(bindType); else method = getDateTime; break; case "decimal": CheckType(decimalThrow, objTypeName, transformedPropertyName, propertyName); var isNotDecimal = objTypeName != "decimal"; if (isNotDecimal) method = getOther.MakeGenericMethod(bindType); else method = getDecimal; break; case "double": CheckType(doubleThrow, objTypeName, transformedPropertyName, propertyName); var isNotDouble = objTypeName != "double"; if (isNotDouble) method = getOther.MakeGenericMethod(bindType); else method = getDouble; break; case "guid": CheckType(guidThrow, objTypeName, transformedPropertyName, propertyName); if (objTypeName != "guid") method = getOther.MakeGenericMethod(bindType); else method = getGuid; break; case "byte": method = getByte; break; case "enum": method = getEnum; break; case "short": CheckType(shortThrow, objTypeName, transformedPropertyName, propertyName); var isNotShort = objTypeName != "int16" && objTypeName != "short"; if (isNotShort) method = getOther.MakeGenericMethod(bindType); else method = getInt16; break; default: method = getOther.MakeGenericMethod(bindType); break; ; } generator.Emit(OpCodes.Call, method); if (method == getValueMethod) { generator.Emit(OpCodes.Unbox_Any, bindProperty.PropertyType); } } } private void CheckType(List errorTypes, string objType, string transformedPropertyName, string propertyName) { var isAny = errorTypes.Contains(objType); if (isAny) { throw new SqlSugarException(string.Format("{0} can't convert {1} to {2}", propertyName, transformedPropertyName, objType)); } } } }