Проблема использования LINQ to SQL с одним DataContext на атомарное действие - PullRequest
1 голос
/ 03 ноября 2008

Я начал использовать Linq to SQL в (битовой DDD) системе, которая выглядит (слишком упрощенно) так:

public class SomeEntity // Imagine this is a fully mapped linq2sql class.
{
    public Guid SomeEntityId { get; set; }
    public AnotherEntity Relation { get; set; }
}

public class AnotherEntity // Imagine this is a fully mapped linq2sql class.
{
    public Guid AnotherEntityId { get; set; }
}

public interface IRepository<TId, TEntity>
{
    Entity Get(TId id);
}

public class SomeEntityRepository : IRepository<Guid, SomeEntity>
{
    public SomeEntity Get(Guid id)
    {
        SomeEntity someEntity = null;
        using (DataContext context = new DataContext())
        {
            someEntity = (
                from e in context.SomeEntity
                where e.SomeEntityId == id
                select e).SingleOrDefault<SomeEntity>();
        }

        return someEntity;
    }
}

Теперь у меня проблема. Когда я пытаюсь использовать SomeEntityRepository, как это

public static class Program
{
    public static void Main(string[] args)
    {
        IRepository<Guid, SomeEntity> someEntityRepository = new SomeEntityRepository();
        SomeEntity someEntity = someEntityRepository.Get(new Guid("98011F24-6A3D-4f42-8567-4BEF07117F59"));
        Console.WriteLine(someEntity.SomeEntityId);
        Console.WriteLine(someEntity.Relation.AnotherEntityId);
    }
 }

все работает хорошо, пока программа не дойдет до последней WriteLine, потому что она выдает ObjectDisposedException, потому что DataContext больше не существует.

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

  • Отойдите от шаблона хранилища и используйте новый DataContext для каждой элементарной части работы.
    • Я действительно не хотел бы этого делать. Причина в том, что я не хочу, чтобы приложения были осведомлены о хранилище. Другая причина в том, что я не думаю, что было бы неплохо сделать linq2sql с видимым COM-содержимым.
    • Кроме того, я думаю, что выполнение context.SubmitChanges(), вероятно, совершит гораздо больше, чем я намеревался.
  • Указание DataLoadOptions для выборки связанных элементов.
    • Поскольку я хочу, чтобы мой уровень бизнес-логики в некоторых случаях просто отвечал с некоторыми объектами, я не знаю, какие подчиненные свойства им нужно использовать.
  • Отключение отложенной загрузки / отложенной загрузки для всех свойств.
    • Не вариант, потому что таблиц довольно много и они тесно связаны между собой. Это может привести к большому количеству ненужного трафика и загрузке базы данных.
  • В некоторых постах в интернете говорилось, что использование .Single () должно помочь.
    • Видимо, это не так ...

Есть ли способ решить эту проблему?

Кстати: мы решили использовать Linq t0 SQL, потому что это относительно легкое решение ORM и входит в состав .NET Framework и Visual Studio. Если .NET Entity Framework будет лучше вписываться в этот шаблон, возможно, будет возможность перейти на него. (Мы еще не так далеко в реализации.)

Ответы [ 5 ]

4 голосов
/ 03 ноября 2008

Rick Strahl имеет хорошую статью об управлении жизненным циклом DataContext здесь: http://www.west -wind.com / weblog / posts / 246222.aspx .

В принципе, атомарный подход к действию хорош в теории, но вам нужно будет хранить свой DataContext, чтобы иметь возможность отслеживать изменения (и извлекать дочерние элементы) в ваших объектах данных.

См. Также: Несколько / один экземпляр Linq to SQL DataContext и LINQ to SQL - где находится ваш DataContext? .

1 голос
/ 03 ноября 2008

Я не уверен, что вам придется отказаться от репозитория, если вы работаете с атомарными единицами работы. Я использую оба, хотя я признаю, что выбрасывал оптимистичные проверки параллелизма, так как они все равно не работают в слоях (без использования временной метки или какого-либо другого требуемого соглашения). В итоге я получаю репозиторий, который использует DataContext и выбрасывает его, когда все готово.

Это часть несвязанного примера Silverlight, но первые три части показывают, как я использую шаблон репозитория с одноразовым контекстом LINQ to SQL, FWIW: http://www.dimebrain.com/2008/09/linq-wcf-silver.html

1 голос
/ 03 ноября 2008

Вы должны либо:

1) Оставьте контекст открытым, потому что вы еще не решили, какие данные еще будут использоваться (иначе, Lazy Loading).

или 2) Извлечь больше данных о начальной загрузке, если вы знаете, что вам понадобится это другое свойство.

Объяснение последнего: здесь

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

Это то, что я делаю, и до сих пор это работало очень хорошо.

1) Сделайте DataContext переменной-членом в вашем хранилище. Да, это означает, что в вашем хранилище должен быть реализован IDisposable, и его нельзя оставлять открытым ... может быть, вы хотите этого избежать, но я не нашел это неудобным.

2) Добавьте несколько методов в ваш репозиторий, например:

public SomeEntityRepository WithSomethingElseTheCallerMightNeed()
{
 dlo.LoadWith<SomeEntity>(se => se.RelatedEntities);
 return this; //so you can do method chaining
}

Тогда ваш абонент выглядит так:

SomeEntity someEntity = someEntityRepository.WithSomethingElseTheCallerMightNeed().Get(new Guid("98011F24-6A3D-4f42-8567-4BEF07117F59"));

Вам просто нужно убедиться, что когда ваш репозиторий попадает в базу данных, он использует параметры загрузки данных, указанные в этих вспомогательных методах ... в моем случае «dlo» сохраняется как переменная-член, а затем устанавливается прямо перед нажатием ДБ.

0 голосов
/ 03 ноября 2008

Указание DataLoadOptions для выборки связанных элементов. Поскольку я хочу, чтобы мой уровень бизнес-логики просто отвечал с некоторыми объектами в некоторых случаях, я не знаю, какие субсвойства им нужно использовать.

Если вызывающей стороне предоставляется соединение, необходимое для использования свойства .Relation, тогда вызывающая сторона может также указать DataLoadOptions.

DataLoadOptions loadOptions = new DataLoadOptions();
loadOptions.LoadWith<Entity>(e => e.Relation);
SomeEntity someEntity = someEntityRepository
  .Get(new Guid("98011F24-6A3D-4f42-8567-4BEF07117F59"),
  loadOptions);

//

using (DataContext context = new DataContext())
{
  context.LoadOptions = loadOptions;
...