使用静态Func<>投影单个记录

时间:2016-12-22 22:51:42

标签: c# entity-framework linq projection

我一直在使用模式从实体框架投影到业务域视图。我正在嵌套它,即从另一个投影中调用一个投影。它适用于集合,但我无法弄清楚如何使用相同的模式来投射单个实体。

对于集合,我的代码类似于:

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不能调用Func&lt;&gt;):

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);

有什么建议吗?

2 个答案:

答案 0 :(得分:1)

问题的实质是投影中的以下行

PersonView = PersonView.ProjectorFn(o.Person)

无法转换为商店查询,因为ProjectorFn不再是Expression,而是通用代理(Func<Person, PersonView>)。

现在,您真正想要的是使用PersonView.Projector字段中包含的原始表达式,但显然您无法调用它(不编译委托),因此无法返回您想要的{ {1}}输入。

LinqKit旨在使用自己的PersonView扩展方法解决此问题,该方法在编译代码时会确保将表达式替换回原始格式。

要启用拦截,您必须使用扩展实体集的Invoke()方法:

AsExpandable()

More on LinqKit

答案 1 :(得分:0)

您需要继续使用直接表达式树,但也要将其编译为普通委托:

public static readonly Func<Person, PersonView> ProjectorFunc = Projector.Compile();