来自 sql 查询执行实体框架的匿名类型结果
Posted
技术标签:
【中文标题】来自 sql 查询执行实体框架的匿名类型结果【英文标题】:Anonymous type result from sql query execution entity framework 【发布时间】:2015-01-01 04:28:16 【问题描述】:我正在使用实体框架 5.0 和 .net 框架 4.0 代码优先方法。现在我知道我可以通过以下方式在实体框架中运行原始 sql
var students = Context.Database.SqlQuery<Student>("select * from student").ToList();
它运行良好,但我想要的是返回匿名结果。例如,我只想要学生表中的特定列,如下所示
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 查询中获取匿名类型结果?
【问题讨论】:
什么是Student类,什么是Student表? 【参考方案1】:您必须为此使用原始 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 a=@a and b=@b", new Dictionary<string, object> "a", true , "b", false ).ToList();
【讨论】:
感谢您的回复。也可以使用Newtonsoft.Json.Linq.JObject
而不是ExpandoObject
答案看起来像github.com/aspnet/EntityFrameworkCore/issues/…
如果您传递空值,可能要添加的更新是dbParameter.Value = p.Value == null ? DBNull.Value : p.Value;
很好的解决方案!一个小的更正 - 由于这是一种扩展方法,我认为使用示例需要是 "List这是对我来说很好的最终解决方案。
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);
【讨论】:
如果选择查询是从单个表和单个实体中检索,则该解决方案最适合。我们能否即兴发挥这一点来实现带有 JOINS 的 Select 查询,其中我们将有多个表并且需要绑定到不同的 TypeBuilder。想法? 这两次进入数据库 你如何使用这个?我正在尝试在代码中使用它,但无法。你能展示打电话的示例吗?谢谢!!! 嗨@Manish,请分享示例项目。 OP 是关于SqlQuery
的问题。这不使用该功能。【参考方案3】:
您可以从这里尝试代码,向下滚动并找到 stankovski 的实现: http://www.codeproject.com/Articles/206416/Use-dynamic-type-in-Entity-Framework-SqlQuery
将代码复制到静态类后,可以调用这个函数得到你想要的:
var students = Context.Database.DynamicSqlQuery("select FirstName from student").ToList()
【讨论】:
谢谢,我正在为将代码准确放在哪里而苦苦挣扎,控制器助手类?它是公共静态的 看起来还有另一种更简单的方法。去这里看看ChristineBoersen的帖子——github.com/aspnet/EntityFramework/issues/2344 @goroth 请注意您引用的是 EF 核心。【参考方案4】:如果你有一个实体并且你只想要一些属性,你可以在反射的帮助下得到一个更好的解决方案。
此代码建立在与上述答案相同的示例之上。
除此之外,您还可以指定要返回的类型和字段数组。
结果是 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();
实际上它只适用于简单类型,没有错误处理。
【讨论】:
typeof(T) 返回null,我该怎么办? 在foreach中从动态改为对象(item中的object item)【参考方案5】:我就是这样用的
ORM类:
public class ORMBase<T, TContext> : IORM<T>
where T : class
where TContext : DbContext, IDisposable, new()
方法:
public IList<TResult> GetSqlQuery<TResult>(string sql, params object[] sqlParams)
using (TContext con = new TContext())
return Enumerable.ToList(con.Database.SqlQuery<TResult>(sql, sqlParams));
最后使用
public class ResimORM : ORMBase<Resim, mdlOgrenciKulup>, IDisposable
public void Dispose() GC.SuppressFinalize(this);
ResimORM RE_ORM = new ResimORM();
List<Resim> a = RE_ORM.GetSqlQuery<Resim>(sql,null).ToList();
int b = RE_ORM.GetSqlQuery<int>(sql,null).FirstOrDefault();
【讨论】:
【参考方案6】:我知道它可能有点晚,但如果你想执行标量函数并始终将其转换为统一类型,你可以使用这样的方法。
只需使用此forceStringCast参数将sql标量函数结果强制转换为字符串,然后根据需要使用此字符串。
public static Task<T> ScalarFunction<T>(DbContext db, string sql, bool forceStringCast = false, Dictionary<string, object> parameters = null)
string cmdText;
if (forceStringCast)
cmdText = $@"SELECT CAST(sql(string.Join(",",
parameters.Keys.Select(p => "@" + p).ToList()) AS VARCHAR(MAX)));";
else
cmdText =
$@"SELECT sql(string.Join(",",
parameters.Keys.Select(p => "@" + p).ToList()));";
return db.Database.SqlQuery<T>(cmdText,parameters.Select(p => new SqlParameter(p.Key, p.Value)).ToArray()).FirstOrDefaultAsync();
【讨论】:
【参考方案7】:这是我的解决方案:
DbContext.Database.Connection.Open();
var cmd = DbContext.Database.Connection.CreateCommand();
cmd.CommandText = @"Select * from Table where Param1 = @Param1 and Param2 = @Param2";
cmd.Parameters.Add(new SqlParameter("Param1", Mahdi));
cmd.Parameters.Add(new SqlParameter("Param2", 20));
List<Dictionary<string, object>> items = new List<Dictionary<string, object>>();
var reader = cmd.ExecuteReader();
while (reader.Read())
Dictionary<string, object> item = new Dictionary<string, object>();
for (int i = 0; i < reader.FieldCount; i++)
item[reader.GetName(i)] = (reader[i]);
items.Add(item);
return Request.CreateResponse(HttpStatusCode.OK, items);
希望对大家有所帮助;)
【讨论】:
这没有回答问题,并且已经给出了唯一正确的答案。 (并且请不要对多个问题发布相同的答案)。 亲爱的 Gert Arnold, 这是针对所提问题的正确、简短和最佳的解决方案。再检查一次;) 问题是关于SqlQuery
,然后,这个答案已经给出了。以上是关于来自 sql 查询执行实体框架的匿名类型结果的主要内容,如果未能解决你的问题,请参考以下文章