来自sql查询执行实体框架的匿名类型结果

Man*_*iya 30 c# sql entity-framework anonymous-types

我使用实体框架5.0与.net framework 4.0代码的第一种方法.现在我知道我可以通过以下方式在实体框架中运行原始sql

var students = Context.Database.SqlQuery<Student>("select * from student").ToList();
Run Code Online (Sandbox Code Playgroud)

它工作得很好,但我想要的是返回匿名结果.例如,我只想要学生表中的特定列,如下所示

var students = Context.Database.SqlQuery<Student>("select FirstName from student").ToList();
Run Code Online (Sandbox Code Playgroud)

它不起作用.它给了例外

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

所以我尝试过dynamic类型

var students = Context.Database.SqlQuery<dynamic>("select FirstName from student").ToList();
Run Code Online (Sandbox Code Playgroud)

它也不起作用,它返回一个空对象.没有数据可用.

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

Man*_*iya 27

这是最终解决方案,对我来说很好.

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);
        }    
Run Code Online (Sandbox Code Playgroud)

  • 你怎么用这个?我试图在代码中使用它,但不能。你能展示调用的示例方法吗?谢谢!!! (2认同)

Cht*_*lek 23

您必须使用原始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;
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

你可以这样称呼它:

List<dynamic> results = DynamicListFromSql(myDb,"select * from table where a=@a and b=@b", new Dictionary<string, object> { { "a", true }, { "b", false } }).ToList();
Run Code Online (Sandbox Code Playgroud)

  • 答案看起来像 https://github.com/aspnet/EntityFrameworkCore/issues/2344#issuecomment-172641417 (3认同)
  • 可能要添加的更新是“dbParameter.Value = p.Value == null”?DBNull.Value : p.Value;`以防您传递空值 (3认同)
  • 感谢您的回复。也可以使用`Newtonsoft.Json.Linq.JObject`代替`ExpandoObject` (2认同)

Huy*_*ham 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()
Run Code Online (Sandbox Code Playgroud)

  • 看起来还有另一种更简单的方法。去这里看看 ChristineBoersen 的帖子——https://github.com/aspnet/EntityFramework/issues/2344 (2认同)