Linq 如何分配IQueryable的属性值<;T>;?
我首先使用实体框架4.1代码。在我的实体中,我有三个日期/时间属性:Linq 如何分配IQueryable的属性值<;T>;?,linq,entity-framework,entity,expression-trees,iqueryable,Linq,Entity Framework,Entity,Expression Trees,Iqueryable,我首先使用实体框架4.1代码。在我的实体中,我有三个日期/时间属性: public class MyEntity { [Key] public Id { get; set; } public DateTime FromDate { get; set; } public DateTime ToDate { get; set; } [NotMapped] public DateTime? QueryDate { get; set; }
public class MyEntity
{
[Key]
public Id { get; set; }
public DateTime FromDate { get; set; }
public DateTime ToDate { get; set; }
[NotMapped]
public DateTime? QueryDate { get; set; }
// and some other fields, of course
}
在数据库中,我总是填充从/到日期。我使用一个简单的where子句查询它们。但是在结果集中,我想包括我查询的日期。我需要坚持这一点,以便其他一些业务逻辑能够工作
我正在研究一种扩展方法来实现这一点,但我遇到了一些问题:
public static IQueryable<T> WhereDateInRange<T>(this IQueryable<T> queryable, DateTime queryDate) where T : MyEntity
{
// this part works fine
var newQueryable = queryable.Where(e => e.FromDate <= queryDate &&
e.ToDate >= queryDate);
// in theory, this is what I want to do
newQueryable = newQueryable.Select(e =>
{
e.QueryDate = queryDate;
return e;
});
return newQueryable;
}
所以问题是,我如何转换表达式树,我已经必须包含这个额外的属性赋值?我已经研究了IQueryable.Expression和IQueryable.Provider.CreateQuery,其中有一个解决方案。是否可以将赋值表达式附加到现有表达式树?我对表达式树方法不太熟悉,无法理解这一点。有什么想法吗
示例用法
澄清一下,目标是能够执行如下操作:
SELECT *, @QueryDate as QueryDate
FROM MyEntities
WHERE @QueryDate BETWEEN FromDate AND ToDate
var entity = dataContext.Set<MyEntity>()
.WhereDateInRange(DateTime.Now)
.FirstOrDefault();
var entity=dataContext.Set()
.WhereDateInRange(DateTime.Now)
.FirstOrDefault();
并将DateTime.Now保存到结果行的QueryDate中,而不会从数据库查询返回多行。(对于IEnumerable解决方案,在FirstOrDefault选择我们想要的行之前,会返回多行。)
另一个想法
我可以继续将QueryDate映射为一个真实的字段,并将其DatabaseGeneratedOption设置为Computed。但是,我需要某种方法将“@QueryDate as QueryDate”注入EF的select语句创建的SQL中。因为它是计算出来的,所以EF不会在更新或插入期间尝试提供值。那么,我如何将自定义SQL注入select语句呢?不,我认为没有解决方案。确实,您可以修改表达式树,但您将得到与linq查询完全相同的异常,因为该查询实际上是您将在表达式树中构建的查询。问题不在表达式树中,而在映射中。EF无法将
QueryData
映射到结果。此外,您正在尝试进行投影。无法对映射的实体进行投影,并且无法从方法返回匿名类型
您当然可以执行您提到的选择,但您无法将其映射到您的实体。您必须为此创建一个新类型:
var query = from x in context.MyData
where x.FromDate <= queryDate && x.ToDate >= queryDate
select new MyDateWrapper
{
MyData = x,
QueryDate = queryDate
};
var query=来自context.MyData中的x
其中x.FromDate=queryDate
选择新建MyDateWrapper
{
MyData=x,
QueryDate=QueryDate
};
拉迪斯拉夫是绝对正确的。但是,由于您显然希望回答问题的第二部分,下面是如何使用Assign。不过,这对EF不起作用
using System;
using System.Linq;
using System.Linq.Expressions;
namespace SO5639951
{
static class Program
{
static void Main()
{
AdventureWorks2008Entities c = new AdventureWorks2008Entities();
var data = c.Addresses.Select(p => p);
ParameterExpression value = Expression.Parameter(typeof(Address), "value");
ParameterExpression result = Expression.Parameter(typeof(Address), "result");
BlockExpression block = Expression.Block(
new[] { result },
Expression.Assign(Expression.Property(value, "AddressLine1"), Expression.Constant("X")),
Expression.Assign(result, value)
);
LambdaExpression lambdaExpression = Expression.Lambda<Func<Address, Address>>(block, value);
MethodCallExpression methodCallExpression =
Expression.Call(
typeof(Queryable),
"Select",
new[]{ typeof(Address),typeof(Address) } ,
new[] { data.Expression, Expression.Quote(lambdaExpression) });
var data2 = data.Provider.CreateQuery<Address>(methodCallExpression);
string result1 = data.ToList()[0].AddressLine1;
string result2 = data2.ToList()[0].AddressLine1;
}
}
}
使用系统;
使用System.Linq;
使用System.Linq.Expressions;
名称空间SO5639951
{
静态类程序
{
静态void Main()
{
AdventureWorks2008Entities c=新的AdventureWorks2008Entities();
var data=c.Addresses.Select(p=>p);
ParameterExpression值=Expression.Parameter(类型(地址),“值”);
ParameterExpression结果=Expression.Parameter(类型(地址),“结果”);
BlockExpression block=Expression.block(
新[]{result},
Expression.Assign(Expression.Property(值,“AddressLine1”)、Expression.Constant(“X”),
表达式.赋值(结果、值)
);
LambdaExpression LambdaExpression=Expression.Lambda(块,值);
MethodCallExpression MethodCallExpression=
表情,打电话(
类型(可查询),
“选择”,
新[]{typeof(地址),typeof(地址)},
新[]{data.Expression,Expression.Quote(lambdaExpression)};
var data2=data.Provider.CreateQuery(methodCallExpression);
字符串result1=data.ToList()[0].AddressLine1;
字符串result2=data2.ToList()[0].AddressLine1;
}
}
}
更新1
这里是经过一些调整后的相同代码。我读过上面代码中EF阻塞的“Block”表达式,以绝对清晰地证明EF不支持的是“Assign”表达式。请注意,Assign原则上适用于泛型表达式树,EF提供程序不支持Assign
using System;
using System.Linq;
using System.Linq.Expressions;
namespace SO5639951
{
static class Program
{
static void Main()
{
AdventureWorks2008Entities c = new AdventureWorks2008Entities();
IQueryable<Address> originalData = c.Addresses.AsQueryable();
Type anonType = new { a = new Address(), b = "" }.GetType();
ParameterExpression assignParameter = Expression.Parameter(typeof(Address), "value");
var assignExpression = Expression.New(
anonType.GetConstructor(new[] { typeof(Address), typeof(string) }),
assignParameter,
Expression.Assign(Expression.Property(assignParameter, "AddressLine1"), Expression.Constant("X")));
LambdaExpression lambdaAssignExpression = Expression.Lambda(assignExpression, assignParameter);
var assignData = originalData.Provider.CreateQuery(CreateSelectMethodCall(originalData, lambdaAssignExpression));
ParameterExpression selectParameter = Expression.Parameter(anonType, "value");
var selectExpression = Expression.Property(selectParameter, "a");
LambdaExpression lambdaSelectExpression = Expression.Lambda(selectExpression, selectParameter);
IQueryable<Address> finalData = assignData.Provider.CreateQuery<Address>(CreateSelectMethodCall(assignData, lambdaSelectExpression));
string result = finalData.ToList()[0].AddressLine1;
}
static MethodCallExpression CreateSelectMethodCall(IQueryable query, LambdaExpression expression)
{
Type[] typeArgs = new[] { query.ElementType, expression.Body.Type };
return Expression.Call(
typeof(Queryable),
"Select",
typeArgs,
new[] { query.Expression, Expression.Quote(expression) });
}
}
}
使用系统;
使用System.Linq;
使用System.Linq.Expressions;
名称空间SO5639951
{
静态类程序
{
静态void Main()
{
AdventureWorks2008Entities c=新的AdventureWorks2008Entities();
IQueryable originalData=c.Addresses.AsQueryable();
键入anonType=new{a=newaddress(),b=”“}.GetType();
ParameterExpression assignParameter=表达式.参数(类型(地址),“值”);
var assignExpression=Expression.New(
anonType.GetConstructor(新的[]{typeof(地址),typeof(字符串)}),
赋值参数,
Expression.Assign(Expression.Property(assignParameter,“AddressLine1”)、Expression.Constant(“X”));
LambdaExpression lambdassignexpression=Expression.Lambda(assignExpression,assignParameter);
var assignData=originalData.Provider.CreateQuery(CreateSelectMethodCall(originalData,lambdassignExpression));
ParameterExpression selectParameter=Expression.Parameter(输入“值”);
var selectExpression=Expression.Property(selectParameter,“a”);
LambdaExpression lambdaSelectExpression=Expression.Lambda(selectExpression,selectParameter);
IQueryable finalData=assignData.Provider.CreateQuery(CreateSelectMethodCall(assignData,lambdaSelectExpression));
字符串结果
public class MyEntity
{
private DateTime? _queryDate;
[ThreadStatic]
internal static DateTime TempQueryDate;
[NotMapped]
public DateTime? QueryDate
{
get
{
if (_queryDate == null)
_queryDate = TempQueryDate;
return _queryDate;
}
}
...
}
public static IQueryable<T> WhereDateInRange<T>(this IQueryable<T> queryable, DateTime queryDate) where T : MyEntity
{
MyEntity.TempQueryDate = queryDate;
return queryable.Where(e => e.FromDate <= queryDate && e.ToDate >= queryDate);
}
configuration.CreateMap(typeof(MyEntity), typeof(MyEntity))
.ForMember(nameof(Entity.QueryDate), opt.MapFrom(src => DateTime.Now));
queryable.ProjectTo<MyEntity>();