2014-11-05 58 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