我有一个代码优先的EF数据库,其中对象具有跟踪历史记录的“状态”。他们实现了这样的事情:
public class Example
{
public Example()
{
this.Statuses = new HashSet<Status>();
}
public Guid Id { get; set; }
public virtual ICollection<Status> Statuses { get; set; }
}
public class Status
{
public Guid Id { get; set; }
public DateTimeOffset SetOn { get; set; }
public string SetBy { get; set; }
}
我们在代码中有一些实例,我们需要获得最旧或最新的状态。目前我们一直在使用链式linq表达式,如下所示:
var setBy = example.Statuses.OrderByDescending(s => s.SetOn).FirstOrDefault().SetBy;
我认为如果我们可以通过扩展来做一些更具可读性,因为获得最新或最旧的状态只是它是按升序还是降序排序。
如果我们已经从数据库中获得了结果,那么像这样的简单扩展方法可以使用linq-to-objects:
public static Status Newest(this IQueryable<Status> items)
{
return items.OrderByDescending(s => s.SetOn).FirstOrDefault();
}
但是,如果我在代表我们数据库的IQueryable上运行它,这不起作用,因为EF无法将其转换为商店表达式。例如,如果下面的“repository”是表示SQL后端中的示例的IQueryable<Example>
,则以下内容将失败:
var date = DateTimeOffset.Parse("4/1/2018");
var query = repository.Where(ex => ex.Statuses.Newest().SetOn > date).FirstOrDefault();
有没有办法可以将它重构为可以转换为商店表达式的扩展方法或表达式?
答案 0 :(得分:0)
这可以通过LINQKit来完成,方法是定义一个返回示例中状态的表达式,然后将IQueryable与LINQKit的Expandable进行包装。使用上面的类,我可以做类似的事情
private Expression<Func<Example, Status>> Newest =
e => e.Statuses.OrderByDescending(s => s.SetOn).FirstOrDefault();
并像调用它一样
var results = from example in repository.AsExpandable()
select new
{
Example = example,
LatestStatus = Newest.Invoke(example)
};