Стремительная загрузка Linq to SQL Entities в самоссылающуюся таблицу - PullRequest
7 голосов
/ 21 августа 2009

У меня есть 2 связанных вопроса Linq to SQL. Посмотрите изображение ниже, чтобы увидеть, как выглядит моя модель.

Вопрос 1

Я пытаюсь понять, как стремиться загрузить поле User.AddedByUser в мой класс / таблицу User. Это поле создается из отношения в поле User.AddedByUserId. Таблица ссылается на себя, и я пытаюсь выяснить, как заставить Linq to SQL с нетерпением загрузить свойство User.AddedByUser, т. Е. Всякий раз, когда любая сущность User загружается / выбирается, она также должна извлекать User.AddedByUser и User.ChangedByUser. Однако я понимаю, что это может стать рекурсивной проблемой ...

Обновление 1.1:

Я пытался использовать DataLoadOptions следующим образом:

var options = new DataLoadOptions();
options.LoadWith<User>(u => u.ChangedByUser);
options.LoadWith<User>(u => u.AddedByUser);

db = new ModelDataContext(connectionString);
db.LoadOptions = options;

Но это не работает, я получаю следующее исключение в строке 2:

System.InvalidOperationException occurred
  Message="Cycles not allowed in LoadOptions LoadWith type graph."
  Source="System.Data.Linq"
  StackTrace:
       at System.Data.Linq.DataLoadOptions.ValidateTypeGraphAcyclic()
       at System.Data.Linq.DataLoadOptions.Preload(MemberInfo association)
       at System.Data.Linq.DataLoadOptions.LoadWith[T](Expression`1 expression)
       at i3t.KpCosting.Service.Library.Repositories.UserRepository..ctor(String connectionString) in C:\Development\KP Costing\Trunk\Code\i3t.KpCosting.Service.Library\Repositories\UserRepository.cs:line 15
  InnerException:

Исключение самоочевидно - граф объектов не может быть циклическим. Кроме того, если предположить, что в строке 2 не возникло исключение, я уверен, что в строке 3 это произойдет, поскольку они являются дублирующими ключами.

Обновление 1.2 :

Следующее также не работает (не используется в сочетании с Обновление 1.1 выше):

var query = from u in db.Users
            select new User()
            {
                Id = u.Id,
                // other fields removed for brevityy
                AddedByUser = u.AddedByUser,
                ChangedByUser = u.ChangedByUser,

            };
return query.ToList();

Выдает следующее, не требующее пояснений исключение:

System.NotSupportedException occurred
Message="Explicit construction of entity type 'i3t.KpCosting.Shared.Model.User' in query is not allowed."

Теперь я ДЕЙСТВИТЕЛЬНО не знаю, как решить эту проблему. Пожалуйста, помогите!

Вопрос 2

В каждой другой таблице в моей БД и, следовательно, в модели Linq to SQL, у меня есть два поля: Entity.ChangedByUser (связано с Entity.ChangedByUserId внешним ключом / отношением) и Entity.AddedByUser (связано с Entity.AddedByUserId внешним ключом / отношения)

Как мне заставить Linq to SQL легко загрузить эти поля для меня? Нужно ли выполнять простое объединение в моих запросах? Или есть какой-то другой способ?

Linq to SQL активно загружается в самоссылающуюся таблицу http://img245.imageshack.us/img245/5631/linqtosql.jpg

Ответы [ 3 ]

4 голосов
/ 09 сентября 2009

Любой тип циклов просто не разрешен . Поскольку LoadWith<T> или AssociateWith<T> применяются ко всем типам в контексте, нет внутреннего способа предотвратить бесконечный цикл. Точнее говоря, он просто запутался в том, как создать SQL, поскольку SQL Server не имеет CONNECT BY и CTE , которые уже давно прошли то, что Linq может генерировать автоматически с помощью предоставленной инфраструктуры.

Лучший вариант, доступный вам, - это вручную выполнить присоединение уровня 1 к пользовательской таблице для обоих дочерних и анонимного типа, чтобы вернуть их. Извините, это не чистое / простое решение, но это действительно все, что пока доступно с Linq.

3 голосов
/ 08 сентября 2009

Может быть, вы могли бы попытаться сделать шаг назад и посмотреть, что вы хотите сделать с отношением? Я предполагаю, что вы хотите отобразить эту информацию пользователю, например, "изменено Iain Galloway 8 часов назад".

Может ли что-то вроде следующей работы? : -

var users = from u in db.Users
            select new
            {
              /* other stuff... */
              AddedTimestamp = u.AddedTimestamp,
              AddedDescription = u.AddedByUser.FullName,
              ChangedTimestamp = u.ChangedTimestamp,
              ChangedDescription = u.ChangedByUser.FullName
            };

Я использовал анонимный тип для (имо) ясности. Вы можете добавить эти свойства к своему типу пользователя, если хотите.

Что касается вашего второго вопроса, ваш обычный LoadWith (x => x.AddedByUser) и т. Д. Должен работать просто отлично - хотя я предпочитаю хранить строку описания непосредственно в базе данных - вы получаете компромисс между Ваше описание обновляется при изменении ChangedByUser.FullName и необходимости делать что-то сложное и, возможно, нелогичное, если ChangedByUser удаляется (например, ON DELETE CASCADE или имеет дело с нулевым ChangedByUser в вашем коде).

0 голосов
/ 08 сентября 2009

Не уверен, что есть решение этой проблемы с Linq to Sql. Если вы используете Sql Server 2005, вы можете определить (рекурсивно-подобный) Stored Procecdure, который использует общие табличные выражения, чтобы получить желаемый результат, а затем выполнить его, используя DataContext.ExecuteQuery.

...