2014-11-05 6 views
15

Ich verwende Entity Framework 5.0 mit .net Framework 4.0 Code ersten Ansatz. Jetzt weiß ich, dass ich raw SQL in Entity Framework laufen kann durch folgendeAnonyme Art Ergebnis von SQL-Abfrage Ausführung Entität Framework

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

Es funktioniert perfekt, aber was ich will, ist anonym Ergebnisse zurück. Zum Beispiel möchte ich nur bestimmte Spalten von Student-Tabelle wie folgende

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

Es funktioniert nicht. es gibt Ausnahme

Der Datenleser ist nicht kompatibel mit dem angegebenen 'MyApp.DataContext.Student'. Ein Mitglied des Typs 'StudentId' hat keine entsprechende Spalte im Datenleser mit demselben Namen.

So habe ich dynamic Typ

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

es ist auch nicht funktioniert, ist es ein leeres Objekt zurückgibt. Keine Daten verfügbar.

Gibt es eine Möglichkeit, anonyme Ergebnisse von einer dynamischen SQL-Abfrage zu erhalten?

+0

Was Student-Klasse ist und was Schülertisch? – abatishchev

Antwort

23

Hier ist endgültige Lösung, die für mich in Ordnung war.

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

Die Lösung eignet sich am besten, wenn die Select-Abfrage von einer einzelnen Tabelle und einer einzelnen Entität abgerufen werden soll. Können wir dies improvisieren, um für die Select-Abfrage mit JOINS zu implementieren, wo wir mehrere Tabellen haben und an verschiedene TypeBuilder binden müssen. Gedanken? –

+0

Dies geht zweimal in die Datenbank – ms007

+0

Wie verwenden Sie das? Ich versuche, dies in Code zu verwenden, kann es aber nicht. Können Sie einige Möglichkeiten zum Anrufen zeigen? Vielen Dank!!! – TheGeekYouNeed

7

Sie den Code aus hier versuchen können, scrollen Sie nach unten und finden Sie die von Stankovski implementieren: http://www.codeproject.com/Articles/206416/Use-dynamic-type-in-Entity-Framework-SqlQuery

Nachdem Sie den Code in einer statischen Klasse kopieren, können Sie diese Funktion aufrufen zu bekommen, was Sie wollen:

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

danke, ich kämpfe, wo der Code genau, Controller Helfer Klasse? Es ist öffentlich statisch – John

+0

Sieht aus wie es gibt einen anderen Weg, der viel einfacher ist. Gehen Sie hierhin und schauen Sie sich den Post von ChristineBoersen an - https://github.com/aspnet/EntityFramework/issues/2344 – goroth

+0

wo gebe ich ChristineBoersen's Code ein? – Toolkit

1

Wenn Sie eine Einheit, und Sie wollen nur einige der Eigenschaften sichern Ihnen eine noch bessere Lösung mit Hilfe der Reflexion zu bekommen.

Dieser Code baut auf dem gleichen Beispiel wie in der obigen Antwort auf.

Zusätzlich können Sie einen Typ und ein Array von Feldern angeben, die Sie zurück erhalten möchten.

Das Ergebnis ist vom Typ 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); 
    }  
} 

Sie können es nennen diese Art und Weise:

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(); 

Eigentlich funktioniert es auf einfache Typen nur und es gibt keine Fehlerbehandlung.

+0

typeof (T) gibt null zurück, was soll ich tun? – asfandahmed1

+0

geändert von dynamisch zu Objekt in foreach (Objekt in Artikel) – ms007

8

Sie müssen Raw Sql dafür verwenden, das Entity-Framework SqlQuery<T> funktioniert nur für Objekte mit bekannten Typen.

hier ist die Methode, die ich verwenden:

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

Sie können es so nennen:

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

Danke für die Antwort. Anstelle von 'ExpandoObject' kann auch' Newnetsoft.Json.Linq.JObject' verwendet werden – ehsan88

Verwandte Themen