2014-11-05 43 views
15

我正在使用实体框架5.0与.net框架4.0代码的第一种方法。现在我知道,我可以通过以下来自sql查询执行实体框架的匿名类型结果

var students = Context.Database.SqlQuery<Student>("select * from student").ToList(); 

它的工作完美,但我要的是返回匿名运行结果在实体框架原始的SQL。例如,我只想要学生表中的特定列如下

var students = Context.Database.SqlQuery<Student>("select FirstName from student").ToList(); 

它不工作。它给出例外

数据读取器与指定的'MyApp.DataContext.Student'不兼容。类型为'StudentId'的成员在数据读取器中没有相应的列,名称相同。

所以我试图dynamic

var students = Context.Database.SqlQuery<dynamic>("select FirstName from student").ToList(); 

它也不能正常工作,它返回一个空的对象。没有可用的数据。

有没有办法从动态SQL查询中获取匿名类型结果?

+0

什么是学生类的,什么是学生的表? – abatishchev 2014-11-05 05:10:55

回答

23

这是最后的解决方案,适合我。

public static System.Collections.IEnumerable DynamicSqlQuery(this Database database, string sql, params object[] parameters) 
     { 
      TypeBuilder builder = createTypeBuilder(
        "MyDynamicAssembly", "MyDynamicModule", "MyDynamicType"); 

      using (System.Data.IDbCommand command = database.Connection.CreateCommand()) 
      { 
       try 
       { 
        database.Connection.Open(); 
        command.CommandText = sql; 
        command.CommandTimeout = command.Connection.ConnectionTimeout; 
        foreach (var param in parameters) 
        { 
         command.Parameters.Add(param); 
        } 

        using (System.Data.IDataReader reader = command.ExecuteReader()) 
        { 
         var schema = reader.GetSchemaTable(); 

         foreach (System.Data.DataRow row in schema.Rows) 
         { 
          string name = (string)row["ColumnName"]; 
          //var a=row.ItemArray.Select(d=>d.) 
          Type 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(); 
        command.Parameters.Clear(); 
       } 
      } 

      Type resultType = builder.CreateType(); 

      return database.SqlQuery(resultType, sql, parameters); 
     } 

     private 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; 
     } 

     private 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); 
     }  
+0

如果选择查询要从单个表和单个实体中检索,则解决方案最适合。我们是否可以即兴创建这个实现用于JOINS的Select查询,我们将有多个表并需要绑定到不同的TypeBuilder。思考? – 2015-08-25 15:14:38

+0

这两次到数据库 – ms007 2015-11-14 19:19:26

+0

你如何使用它?我试图在代码中使用它,但我无法。你可以通过示例方式打电话吗?谢谢!!! – TheGeekYouNeed 2016-01-20 01:02:04

7

您可以尝试从这里的代码,然后找到stankovski的实现: http://www.codeproject.com/Articles/206416/Use-dynamic-type-in-Entity-Framework-SqlQuery

代码复制到一个静态类后,你可以调用这个函数来得到你想要的东西:

var students = Context.Database.DynamicSqlQuery("select FirstName from student").ToList() 
+1

谢谢,我正努力把代码完全放在哪里,控制器帮手类?随着它是公共静态 – John 2015-05-28 04:58:36

+0

看起来有另一种方法是更简单。去这里看看ChristineBoersen的帖子 - https://github.com/aspnet/EntityFramework/issues/2344 – goroth 2016-10-15 23:41:52

+0

我在哪里放置了ChristineBoersen的代码? – Toolkit 2017-01-21 12:47:01

1

如果你有一个实体,你只想要一些属性,你可以在反射的帮助下得到更好的解决方案。

此代码与上面的答案构建在相同的样本上。

除此之外,您可以指定要返回的字段的类型和数组。

结果是IEnumerable类型。

public static class DatabaseExtension 
{ 
    public static IEnumerable<T> DynamicSqlQuery<T>(this Database database, string[] fields, string sql, params object[] parameters) where T : new() 
    { 
     var type = typeof (T); 

     var builder = CreateTypeBuilder("MyDynamicAssembly", "MyDynamicModule", "MyDynamicType"); 

     foreach (var field in fields) 
     { 
      var prop = type.GetProperty(field); 
      var propertyType = prop.PropertyType; 
      CreateAutoImplementedProperty(builder, field, propertyType); 
     } 

     var resultType = builder.CreateType(); 

     var items = database.SqlQuery(resultType, sql, parameters); 
     foreach (object item in items) 
     { 
      var obj = new T(); 
      var itemType = item.GetType(); 
      foreach (var prop in itemType.GetProperties(BindingFlags.Instance | BindingFlags.Public)) 
      { 
       var name = prop.Name; 
       var value = prop.GetValue(item, null); 
       type.GetProperty(name).SetValue(obj, value); 
      } 
      yield return obj; 
     } 
    } 

    private 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; 
    } 

    private 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, 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); 
    }  
} 

你可以这样调用它:

var fields = new[]{ "Id", "FirstName", "LastName" }; 
var sql = string.Format("SELECT {0} FROM People WHERE Id = @id", string.Join(", ", fields)); 

var person = db.Database.DynamicSqlQuery<People>(fields, sql, new SqlParameter("id", id)) 
    .FirstOrDefault(); 

其实它的工作原理上只有简单的类型并没有错误处理。

+0

typeof(T)返回null,我该怎么办? – asfandahmed1 2016-03-01 08:37:12

+0

从动态更改为对象 foreach(项目中的对象项) – ms007 2016-09-06 09:01:27

8

您必须使用原始Sql,实体框架SqlQuery<T>只适用于已知类型的对象。

这里是我的方法使用:

public static IEnumerable<dynamic> DynamicListFromSql(this DbContext db, string Sql, Dictionary<string, object> Params) 
{ 
    using (var cmd = db.Database.Connection.CreateCommand()) 
    { 
     cmd.CommandText = Sql; 
     if (cmd.Connection.State != ConnectionState.Open) { cmd.Connection.Open(); } 

     foreach (KeyValuePair<string, object> p in Params) 
     { 
      DbParameter dbParameter = cmd.CreateParameter(); 
      dbParameter.ParameterName = p.Key; 
      dbParameter.Value = p.Value; 
      cmd.Parameters.Add(dbParameter); 
     } 

     using (var dataReader = cmd.ExecuteReader()) 
     { 
      while (dataReader.Read()) 
      { 
       var row = new ExpandoObject() as IDictionary<string, object>; 
       for (var fieldCount = 0; fieldCount < dataReader.FieldCount; fieldCount++) 
       { 
        row.Add(dataReader.GetName(fieldCount), dataReader[fieldCount]); 
       } 
       yield return row; 
      } 
     } 
    } 
} 

你可以这样调用:

List<dynamic> results = DynamicListFromSql(myDb,"select * from table where [email protected] and [email protected]", new Dictionary<string, object> { { "a", true }, { "b", false } }).ToList(); 
+1

感谢您的回应。也可以使用'Newtonsoft.Json.Linq.JObject'而不是'ExpandoObject' – ehsan88 2017-12-03 15:28:34