Создание стека в Entity Framework - PullRequest
1 голос
/ 18 марта 2009

Один тип объекта в моей модели (назовем его E1) должен иметь возможность обрабатывать свои отношения с другим типом объекта (E2) как стек. Взаимно, что другой объект должен иметь возможность видеть все связанные объекты первого типа, где E2 находится на вершине стека, и отдельно каждый случай, когда E2 находится в стеке для E1.

Это звучит не так ясно для меня, поэтому позвольте мне попробовать и продемонстрировать:

E1 лица: foo {stack: quux , plugh , glurp }, bar {stack : plugh , glurp }, baz {stack: quux , plugh }

E2 лица: quux {top: null; в: foo , baz }, plugh {top: baz ; в: foo , bar , baz }, glurp {top: bar ; в: foo , bar }

Сейчас у меня есть таблица базы данных, в которой есть столбцы для ключей как к E1, так и к E2, а также int для хранения позиции E2 в стеке. Entity Framework рассматривает эту таблицу как свою собственную сущность, а не как часть отношений между E1 и E2, что усложняет запросы и просто приводит к некоторому простому уродливому коду.

Я знаю, что я делаю это неправильно, но возможно ли сделать это правильно? И если да, то как?

Ответы [ 2 ]

1 голос
/ 18 марта 2009

Поскольку это довольно экзотическая ситуация, для этого нет встроенной поддержки. Но вы можете заставить его выглядеть красиво.

Пока у вас есть нечто подобное, если предположить, что ваша таблица соединений называется E1E2.

public partial class E1
{
   public Guid Id { get; set; }
   public IQueryable<E1E2> Stack { get; }
}

public partial class E2
{
   public Guid Id { get; set; }
   public IQueryable<E1E2> In { get; }
}

public partial class E1E2
{
   public E1 E1 { get; set; }
   public E2 E2 { get; set; }
   public Int32 Position { get; set; }
}

Как раз я не могу придумать лучшего решения для сопоставления этого с базой данных. Чтобы сделать использование максимально разумным, просто добавьте некоторые свойства и методы к сущностям. Это легко, потому что объект создает частичные классы.

Расширить класс E1 следующим образом.

public partial class E1
{
   public IQueryable<E2> NiceStack
   {
      get { return this.Stack.Select(s => s.E2).OrderBy(s => s.Position); }
   }

   public void Push(E2 e2)
   {
      this.Stack.Add(
         new E1E2
         {
            E2 = e2,
            Position = this.Stack.Max(s => s.Position) + 1
         });
   }

   public E2 Pop()
   {
      return this.Stack.
         Where(s => s.Position == this.Stack.Max(s => s.Position).
         Select(s => s.E2).
         Single();
   }
}

Расширить класс E2 следующим образом.

public partial class E2
{
   public IQueryable<E1> NiceIn
   {
      get { return this.In.Select(i => i.E1); }
   }

   public IQueryable<E1> NiceTop
   {
      get
      {
         return this.In.
            Where(i => i.Position == i.E1.Stack.Max(s => s.Position)).
            Select(i => i.E1);
      }
   }
}

Конец, ты здесь. Теперь должно быть возможно написать довольно хороший код вокруг этой сущности. Возможно, в коде есть некоторые ошибки, но идея должна быть ясной. Я пропустил код, чтобы гарантировать, что связанные свойства загружаются при доступе. Кроме того, вы можете сделать оригинальную собственность приватной и скрыть ее снаружи. Возможно, вам не следует включать свойство NiceStack, потому что это разрешает произвольный доступ. Или, может быть, вы хотите добавить больше расширений - возможно, сделать NiceTop доступным для записи, помещая экземпляр E2 в стек экземпляра E1, вставленного в NiceTop экземпляра E2. Но идея остается прежней.

Вызов Single () не будет работать с обычной Entity Framework; вместо этого используйте ToList (). Single (), чтобы переключиться на LINQ to Object, или используйте First (), но first не сохраняет семантику ровно одного.

1 голос
/ 18 марта 2009

Итак, если мы назовем вашу таблицу отношений R, каждый E1 «содержит» несколько Rs (это стек, упорядоченный по R.position), и каждый E2 содержит несколько Rs двумя способами: те, у которых R.position == R.e1. сверху и те, где это не держится).

  • Это правильно?
  • Как вы следите за вершиной? Обновляете ли вы все R для E1 при изменении стека (например, e1.top является постоянным) или сохраняете его в каждом E1?
  • Имеет ли смысл связывать Rs (давая им «следующий указатель», а не «позицию»)
  • Вам нужен произвольный доступ к стекам?
  • Действительно ли E2 нужно знать о случаях, когда они не на вершине?
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...