使用静态 Func<> 投影单个记录
Projecting a single record with a static Func<>
我一直在使用一种模式从 Entity Framework 投影到业务领域视图。我正在嵌套它,即从另一个投影中调用一个投影。它适用于集合,但我不知道如何使用相同的模式来投影单个实体。
对于集合,我的代码类似于:
public class PersonView
{
public int Id {get;private set;}
public string FullName { get; set; }
public static Expression<Func<Person, PersonView>> Projector = p => new PersonView {
Id = p.PersonId,
FullName = p.FirstName + " " + p.LastName
};
}
//...
context.People.Select(PersonView.Projector).ToList(); // returns a list of PersonViews
如果我创建一个包含 1 元素的列表,或者以其他方式使用 LINQ 发挥创意,我可以让它工作,但如果可能的话我更喜欢更简洁的解决方案。
// convert single element to list, then project it. Works, but is messy
var orderDetails = context.Orders.Where(...)
.Select(o => new {
Id = o.Id,
Date = o.Date,
PersonView = new [] { o.Person }.AsQueryable().Select(PersonView.Projector).FirstOrDefault()
}).FirstOrDefault();
我想要类似的东西(以下不起作用,因为 linq to entities 无法调用 Func<>):
public class PersonView
{
public int Id {get;private set;}
public string FullName { get; set; }
public static Func<Person, PersonView> ProjectorFn = p => new PersonView {
Id = p.PersonId,
FullName = p.FirstName + " " + p.LastName
};
public static Expression<Func<Person, PersonView>> ProjectorExpr = p => ProjectorFn(p);
}
var orderDetails = context.Orders.Where(...)
.Select(o => new {
Id = o.Id,
Date = o.Date,
PersonView = PersonView.ProjectorFn(o.Person)
}).FirstOrDefault();
//...
var peopleWithOrders = context.People.Where(p => p.Orders.Any())
.Select(PersonView.ProjectorExpr);
有什么建议吗?
您需要继续使用直接表达式树,但还要将其编译为普通委托:
public static readonly Func<Person, PersonView> ProjectorFunc = Projector.Compile();
问题的本质是你投影中的下面一行
PersonView = PersonView.ProjectorFn(o.Person)
无法转换为商店查询,因为 ProjectorFn
不再是 Expression
而是通用委托 (Func<Person, PersonView>
)。
现在,您真正想要的是使用 PersonView.Projector
字段中包含的原始表达式,但显然您不能,因为它不能被调用(不编译委托)因此不能 return 你想要的 PersonView
类型。
LinqKit 旨在使用其自己的 Invoke()
扩展方法解决此问题,该方法在让您的代码编译的同时,将确保您的表达式被替换回其原始形式。
要启用拦截,您必须使用扩展实体集的 AsExpandable()
方法:
using LinqKit.Extensions;
var orderDetails = context.Orders
.AsExpandable()
.Where(...)
.Select(o => new {
Id = o.Id,
Date = o.Date,
PersonView = PersonView.Projector.Invoke(o.Person)
})
.FirstOrDefault();
我一直在使用一种模式从 Entity Framework 投影到业务领域视图。我正在嵌套它,即从另一个投影中调用一个投影。它适用于集合,但我不知道如何使用相同的模式来投影单个实体。
对于集合,我的代码类似于:
public class PersonView
{
public int Id {get;private set;}
public string FullName { get; set; }
public static Expression<Func<Person, PersonView>> Projector = p => new PersonView {
Id = p.PersonId,
FullName = p.FirstName + " " + p.LastName
};
}
//...
context.People.Select(PersonView.Projector).ToList(); // returns a list of PersonViews
如果我创建一个包含 1 元素的列表,或者以其他方式使用 LINQ 发挥创意,我可以让它工作,但如果可能的话我更喜欢更简洁的解决方案。
// convert single element to list, then project it. Works, but is messy
var orderDetails = context.Orders.Where(...)
.Select(o => new {
Id = o.Id,
Date = o.Date,
PersonView = new [] { o.Person }.AsQueryable().Select(PersonView.Projector).FirstOrDefault()
}).FirstOrDefault();
我想要类似的东西(以下不起作用,因为 linq to entities 无法调用 Func<>):
public class PersonView
{
public int Id {get;private set;}
public string FullName { get; set; }
public static Func<Person, PersonView> ProjectorFn = p => new PersonView {
Id = p.PersonId,
FullName = p.FirstName + " " + p.LastName
};
public static Expression<Func<Person, PersonView>> ProjectorExpr = p => ProjectorFn(p);
}
var orderDetails = context.Orders.Where(...)
.Select(o => new {
Id = o.Id,
Date = o.Date,
PersonView = PersonView.ProjectorFn(o.Person)
}).FirstOrDefault();
//...
var peopleWithOrders = context.People.Where(p => p.Orders.Any())
.Select(PersonView.ProjectorExpr);
有什么建议吗?
您需要继续使用直接表达式树,但还要将其编译为普通委托:
public static readonly Func<Person, PersonView> ProjectorFunc = Projector.Compile();
问题的本质是你投影中的下面一行
PersonView = PersonView.ProjectorFn(o.Person)
无法转换为商店查询,因为 ProjectorFn
不再是 Expression
而是通用委托 (Func<Person, PersonView>
)。
现在,您真正想要的是使用 PersonView.Projector
字段中包含的原始表达式,但显然您不能,因为它不能被调用(不编译委托)因此不能 return 你想要的 PersonView
类型。
LinqKit 旨在使用其自己的 Invoke()
扩展方法解决此问题,该方法在让您的代码编译的同时,将确保您的表达式被替换回其原始形式。
要启用拦截,您必须使用扩展实体集的 AsExpandable()
方法:
using LinqKit.Extensions;
var orderDetails = context.Orders
.AsExpandable()
.Where(...)
.Select(o => new {
Id = o.Id,
Date = o.Date,
PersonView = PersonView.Projector.Invoke(o.Person)
})
.FirstOrDefault();