我需要从IQueryble中动态选择一些列。
示例:
IQueryable<Car> query = (from a in _dbContext.Table1
select new Car
{
Prop1 = a.Prop1,
Prop2 = a.Prop2,
Prop3 = a.Prop3
});
我需要做类似的事情:
var lambda = new Test().CreateNewStatement<Car>("Prop1,Prop2");
我正在使用该功能:
public Func<T, T> CreateNewStatement<T>(string fields)
{
var xParameter = Expression.Parameter(typeof(T), "o");
var xNew = Expression.New(typeof(T));
var bindings = fields.Split(',').Select(o => o.Trim())
.Select(o =>
{
var mi = CustomGetType<T>(o);
var xOriginal = Expression.Property(xParameter, mi);
return Expression.Bind(mi, xOriginal);
}
);
var xInit = Expression.MemberInit(xNew, bindings);
var lambda = Expression.Lambda<Func<T, T>>(xInit, xParameter);
return lambda.Compile();
}
和电话:
var lambda = new Test().CreateNewStatement<Car>("Prop1");
var ax = query.Select(lambda);
var result = ax.ToList();
但在数据库中,查询错误:
SELECT
[Limit1].[C1] AS [C1],
[Limit1].[Prop1] AS [Prop1],
[Limit1].[Prop2] AS [Prop2],
[Limit1].[Prop3] AS [Prop3],
FROM ( ...
数据库中的正确搜索应该是:
SELECT
[Limit1].[C1] AS [C1],
[Limit1].[Prop1] AS [Prop1]
FROM ( ...
我需要操纵IQueryable&lt; T>在数据库中搜索之前。
答案 0 :(得分:2)
您遇到的问题是您已将Func<User, User>
传递给Select
。这实质上意味着该方法在内存中执行,而不是在数据库中执行。
实体框架无法从已编译的函数生成SQL代码。
当你这样写:
var users = Users.Select(u => new User { a = 1 });
您正在将Expression<Func<User, User>>
实体框架 CAN 转换为SQL。
所以,你需要改变的是你的功能的结束。而不是:
var lambda = Expression.Lambda<Func<T, T>>(xInit, xParameter);
return lambda.Compile()
你想简单地做
return Expression.Lambda<Func<T, T>>(xInit, xParameter);
并将函数的返回类型更改为Expression<Func<T,T>>
<强> 无论其 强>
无论出于何种原因,EntityFramework 都不会允许您构造作为实体的对象。 Linq-To-Sql 允许这样做,并且运行上述更改的代码确实有效,并且只选择一列。
基本上这意味着我们需要更改表达式以返回一个类 not 实体框架使用的实体。
所以,而不是写这个:
var users = Users.Select(u => new User { a = 1 });
我们需要写下这个:
var users = Users.Select(u => new NotAnEntityUser { a = 1 });
然后我们需要更改方法以返回NotAnEntityUser
而不是User
。
public class Test
{
public Expression<Func<TEntityType, TModelType>> CreateNewStatement<TEntityType, TModelType>(string fields)
{
var xParameter = Expression.Parameter(typeof (TEntityType), "o");
var xNew = Expression.New(typeof (TModelType));
var bindings = fields.Split(',').Select(o => o.Trim())
.Select(paramName =>
{
var xOriginal = Expression.Property(xParameter, typeof(TEntityType).GetProperty(paramName));
return Expression.Bind(typeof(TModelType).GetProperty(paramName), xOriginal);
}
);
var xInit = Expression.MemberInit(xNew, bindings);
var lambda = Expression.Lambda<Func<TEntityType, TModelType>>(xInit, xParameter);
return lambda;
}
}
定义我们的实体和模型:
public class User
{
public virtual string Id { get; set; }
public virtual string UserName { get; set; }
public virtual string Salt { get; set; }
public static implicit operator User(NotAnEntityUser o)
{
return new User { Id = o.Id, UserName = o.UserName, Salt = o.Salt };
}
}
public class NotAnEntityUser
{
public virtual string Id { get; set; }
public virtual string UserName { get; set; }
public virtual string Salt { get; set; }
}
然后让你写:
var lam = new Test().CreateNewStatement<User, NotAnEntityUser>("Id");
var c = new MyContext().Users.Select(lam).AsEnumerable().Select(u => (User)u).ToList();
事实上,由此产生的SQL是:
SELECT
1 AS [C1],
[Extent1].[Id] AS [Id]
FROM [dbo].[User] AS [Extent1]
您也可以使用像automapper等工具,而不是编写隐式强制转换操作符。
使用automapper,您最终得到的结果如下:
var c = AutoMapper.Map<List<User>>(new MyContext().Users.Select(lam).ToList());