1

这个问题主要是关于 LINQ 和可能的协方差。

我的两个实体实现了IDatedItem接口。我想联合,然后对这些进行排序,以枚举为一个列表。我必须在枚举时保留实体特定的属性。

为了举例说明,我尝试的一种方法是:

Context.Table1.Cast<IDatedItem>().
Union(Context.Table2.Cast<IDatedItem>()).
SortBy(i => i.Date).
ForEach(u => CustomRenderSelector(u, u is Table1));

在尝试以各种方式执行此操作时,我遇到了各种错误。

  • LINQ to Entities 仅支持转换 EDM 基元或枚举类型。
  • 无法处理类型“.IDatedItem[]”,没有已知的到值层的映射
  • 无法创建“IDatedItem”类型的常量值。只有原始类型
  • 等等

大图:

  • 此处显示的 IDatedItem 接口是实际共享属性的简化。
  • 在实践中,表在联合之前被过滤。
  • 特定于实体的属性将按顺序在网页中呈现。
  • 在并行功能中,它们将被序列化为 JSON 结果层次结构。
  • 我也希望能够对结果执行 LINQ 聚合操作。
4

2 回答 2

1

这需要比评论提供的更多空间。另一方面,这并不是一个真正的答案,因为真的没有令人满意的答案。

要使 aUnion成功,两个集合必须具有相同的类型(或者具有到常见类型的内在转换,这就是协变的意义所在)。

因此,获得正确 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>().

因此,获得 a 的唯一方法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 中完成。您可以对结果使用后续聚合函数。

于 2013-08-24T11:57:13.450 回答
0

这是工作代码。我的解决方案是确保所有数据都是本地的,以防止 LINQ-to-EF 尝试做所有它知道它不能做的事情,这会导致许多不清楚的错误。然后可以在泛型 Union 上进行简单的类型声明。

这意味着,除了 LINQ-to-EF 的烦恼之外,这里的主要问题实际上是LINQ Union 不同类型的副本——动态转换为接口?.

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

}
于 2013-08-25T00:35:24.470 回答