2013-08-24 2 views
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 с результатами.
+0

Почему бы не «класс»? Я думаю, что все сопоставления выполняются автоматически, поэтому нам не нужно заботиться о классе или интерфейсе. –

+0

Вы имеете в виду классовое наследование, а не интерфейс, например, реализацию EF в таблице для каждого типа? Ну, вообще говоря, этот интерфейс действительно плохое первичное представление сущностей. Вы можете спросить, в чем проблема. Интерфейс, который мы обсуждаем, специфичен для одного набора представлений в моем приложении. Так как класс в C# может иметь только один непосредственный базовый класс, это означает, что я не мог решить такую ​​проблему для других представлений одинаково. – shannon

+0

Вы можете сделать это только в том случае, если вы выполняете 'AsEnumerable' перед трансляцией. Но тогда вы не можете «Union», если вы сначала не проецируете один класс, а Sort - на клиентскую сторону. Между скалой и твердым местом ... –

ответ

1

Для этого требуется больше пространства, чем предлагает комментарий. С другой стороны, на самом деле это не ответ, потому что на самом деле нет удовлетворительного ответа.

Для успешного выполнения задачи обе коллекции должны иметь один и тот же тип (или иметь встроенные преобразования для общих типов, вот что такое ковариация).

Так первый идти на получение правильного союза может быть:

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. И вы можете использовать последующие агрегатные функции для результата.

+0

Герт, спасибо для информации. Я дал ему +1. Думаю, я смогу выполнить письмо по моему вопросу; Я упомянул, что я в порядке с большей частью этой клиентской стороны. Причина состоит в том, что, требуя доступа к несходным свойствам через объединение, SQL должен был бы возвращать пустые столбцы для них (в соответствии с реализацией класса E-таблицы по таблице иерархии), или SQL должен был бы возвращать некоторый иерархический формат, такой как XML, что, конечно же, EF не анализирует. Это делает его действительно нечетким, что означает * оптимальный способ сделать это. – shannon

+0

У меня есть рабочее решение. Опубликовано ниже, комментарии приветствуются. – shannon

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

} 
Смежные вопросы