Проблема с использованием LINQ to SQL с одним DataContext для каждого атомарного действия

StackOverflow https://stackoverflow.com/questions/259524

Вопрос

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

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

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

Это было полезно?

Решение

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

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

Смотрите также: Множественный/одиночный экземпляр Linq to SQL DataContext и LINQ to SQL – где находится ваш DataContext?.

Другие советы

Вам необходимо либо:

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

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

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

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

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

Указание 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;

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

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» сохраняется как переменная-член, а затем устанавливается прямо перед попаданием в базу данных.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top