EF 6.x和EF Core实现返回dynamic类型
前言
未曾想需要直接返回dynamic,多次尝试未能实现,最终还是在stackoverflow上找到了解决方案,特此备忘录。
public static dynamic SqlQuery(this Database database, string sql, params object[] parameters) { TypeBuilder builder = CreateTypeBuilder( "DynamicAssembly", "DynamicModule", "DynamicType"); using (var cmd = database.Connection.CreateCommand()) { try { cmd.CommandText = sql; if (cmd.Connection.State != ConnectionState.Open) { cmd.Connection.Open(); } cmd.CommandTimeout = cmd.Connection.ConnectionTimeout; foreach (var param in parameters) { cmd.Parameters.Add(param); } using (IDataReader reader = cmd.ExecuteReader()) { var schema = reader.GetSchemaTable(); foreach (DataRow row in schema.Rows) { var name = (string)row["ColumnName"]; var type = (Type)row["DataType"]; if (type != typeof(string) && (bool)row.ItemArray[schema.Columns.IndexOf("AllowDbNull")]) { type = typeof(Nullable<>).MakeGenericType(type); } CreateAutoImplementedProperty(builder, name, type); } } } finally { database.Connection.Close(); cmd.Parameters.Clear(); } } var resultType = builder.CreateType(); return database.SqlQuery(resultType, sql, parameters); } public static TypeBuilder CreateTypeBuilder( string assemblyName, string moduleName, string typeName) { TypeBuilder typeBuilder = AppDomain .CurrentDomain .DefineDynamicAssembly(new AssemblyName(assemblyName), AssemblyBuilderAccess.Run) .DefineDynamicModule(moduleName) .DefineType(typeName, TypeAttributes.Public); typeBuilder.DefineDefaultConstructor(MethodAttributes.Public); return typeBuilder; } public static void CreateAutoImplementedProperty( TypeBuilder builder, string propertyName, Type propertyType) { const string PrivateFieldPrefix = "m_"; const string GetterPrefix = "get_"; const string SetterPrefix = "set_"; // Generate the field. FieldBuilder fieldBuilder = builder.DefineField( string.Concat(PrivateFieldPrefix, propertyName), propertyType, FieldAttributes.Private); // Generate the property PropertyBuilder propertyBuilder = builder.DefineProperty( propertyName, System.Reflection.PropertyAttributes.HasDefault, propertyType, null); // Property getter and setter attributes. MethodAttributes propertyMethodAttributes = MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.HideBySig; // Define the getter method. MethodBuilder getterMethod = builder.DefineMethod( string.Concat(GetterPrefix, propertyName), propertyMethodAttributes, propertyType, Type.EmptyTypes); // Emit the IL code. // ldarg.0 // ldfld,_field // ret ILGenerator getterILCode = getterMethod.GetILGenerator(); getterILCode.Emit(OpCodes.Ldarg_0); getterILCode.Emit(OpCodes.Ldfld, fieldBuilder); getterILCode.Emit(OpCodes.Ret); // Define the setter method. MethodBuilder setterMethod = builder.DefineMethod( string.Concat(SetterPrefix, propertyName), propertyMethodAttributes, null, new Type[] { propertyType }); // Emit the IL code. // ldarg.0 // ldarg.1 // stfld,_field // ret ILGenerator setterILCode = setterMethod.GetILGenerator(); setterILCode.Emit(OpCodes.Ldarg_0); setterILCode.Emit(OpCodes.Ldarg_1); setterILCode.Emit(OpCodes.Stfld, fieldBuilder); setterILCode.Emit(OpCodes.Ret); propertyBuilder.SetGetMethod(getterMethod); propertyBuilder.SetSetMethod(setterMethod); }
using (var ctx = new EfDbContext()) { ctx.Database.Log = Console.WriteLine; dynamic queryResult = ctx.Database.SqlQuery("select o.*,c.Name from dbo.Orders as o left join dbo.Customers as c on o.CustomerId = c.Id"); var ordersJson = JsonConvert.SerializeObject(queryResult); var orders = JsonConvert.DeserializeObject<List<Order>>(ordersJson); };
你所看到的并非事物本身,而是经过诠释后所赋予的意义