这个问题主要是关于LINQ和可能的协方差。
我的两个实体实现了 IDatedItem 接口。我想联合,然后对它们进行排序,以列举为单个列表。我必须在枚举时保留特定于实体的属性。
通过举例说明,我尝试过的一种方法是:
Context.Table1.Cast<IDatedItem>().
Union(Context.Table2.Cast<IDatedItem>()).
SortBy(i => i.Date).
ForEach(u => CustomRenderSelector(u, u is Table1));
在试图以各种方式做到这一点时,我遇到了各种各样的错误。
更大的图景:
答案 0 :(得分:1)
这需要比评论提供更多的空间。另一方面,这不是一个真正的答案,因为真的没有令人满意的答案。
要使Union
成功,两个集合必须具有相同的类型(或者具有对常见类型的内在转换,这就是协方差的含义)。
因此,获得正确联盟的第一步可能是:
Context.Table1.Select(t1 => new {
A = t1.PropA,
B = t1.PropB,
Date = t1.Date
})
.Union(
Context.Table1.Select(t2 => new {
A = t2.PropC,
B = t2.PropD,
Date = t2.Date
}))
.OrderBy(x => x.Date)
.ToList();
将两个表都投影到同一个匿名类型。不幸的是,由于匿名类型,您无法执行.Cast<IDatedItem>()
。
因此,获取List<IDatedItem>
的唯一方法是定义一个实现IDatedItem
的类型并将两个表都投影到该类型:
Context.Table1.Select(t1 => new DateItem {
A = t1.PropA,
B = t1.PropB,
Date = t1.Date
})
.Union(
Context.Table1.Select(t2 => new DateItem {
A = t2.PropC,
B = t2.PropD,
Date = t2.Date
}))
.OrderBy(item => item.Date)
.AsEnumerable()
.Cast<IDatedItem>()
其中(我认为)非常详细。但只要EF不支持在linq查询中转换为接口,就可以了。
顺便说一句,与我在评论中所说的相反,排序将在SQL中完成。并且您可以在结果上使用后续聚合函数。
答案 1 :(得分:0)
这是工作代码。我的解决方案是确保所有数据都是本地的,以防止LINQ-to-EF尝试执行它知道不会导致许多不明错误的所有方式。然后,通用联盟上的简单类型声明可以保留。
这意味着,除了LINQ-to-EF的烦恼之外,这里的主要问题实际上是LINQ Union different types - dynamically casting to an interface?的重复。
public virtual ActionResult Index() {
return View(StatusBoard().OrderBy(s => s.Status));
}
private IEnumerable<DefensiveSituationBoardMember> StatusBoard() {
DateTime now = DateTime.UtcNow;
DateTime historicalCutoff = now.AddDays(-1);
IEnumerable<Member> activeMembers = Context.Members.Where(n => !n.Disabled).ToList();
// IncomingAttack and Reinforcements both implement IDefensiveActivity
IEnumerable<IncomingAttack> harm = Context.IncomingAttacks.Where(n => n.IsOngoingThreat || n.ArrivalOn > historicalCutoff).ToList();
IEnumerable<Reinforcements> help = Context.Reinforcements.Where(n => !n.Returned.HasValue || n.Returned > historicalCutoff).ToList();
// Here's the relevant fix
IEnumerable<IDefensiveActivity> visibleActivity = help.Union<IDefensiveActivity>(harm);
return from member in activeMembers
join harmEntry in harm on member equals harmEntry.DestinationMember into harmGroup
join activityEntry in visibleActivity on member equals activityEntry.DestinationMember into activityGroup
select new DefensiveSituationBoardMember {
Member = member,
Posture = harmGroup.Max(i => (DefensivePostures?)i.Posture),
Activity = activityGroup.OrderBy(a => a.ArrivalOn)
};
}