В репозиториях на основе LINQ to SQL должны ли мы создавать новый DataContext в каждом методе?

Например:

class repository {

     private DataContext db = new DataContext();

     public IQueryable<Blah> someMethod(int id){
         return from b in db.Blah ... select b;
}

     public IQueryable<Blah> someMethod2(int id){
         return from b in db.Blah ... select b;
}

     public IQueryable<Blah> someMethod3(int id){
         return from b in db.Blah ... select b;
}

}

OR

Должны ли мы создавать новый DataContext ВНУТРИ каждого из этих методов?

Я думаю, что у нас возникают некоторые ошибки при увеличении пользовательской нагрузки из-за того, что у нас есть только ОДИН DataContext для каждого экземпляра репозитория, это точное предположение?


person Ryan    schedule 22.12.2009    source источник
comment
Аналогичный вопрос, заданный в контексте Entity Framework, но все же заслуживающий внимания, поскольку DataContext и ObjectContext выполняют очень похожие задачи: stackoverflow.com/questions / 457369 /   -  person itowlson    schedule 22.12.2009


Ответы (3)


См. Также ответ на этот вопрос.

Короче говоря, особенно если вы используете шаблон репозитория, вы должны создать и удалить текст данных для каждой единицы работы. Обычно я использую что-то вроде:

public someclass someMethod(int id)
{
    using (var db = new SomeDataContext())
    {
        return db.FindMyClass(id);
    }
}
person Eric King    schedule 22.12.2009
comment
Что вы имеете в виду SomeDataContext ()? Означает ли это созданный вами класс, наследующий от DataContext? Итак, вы создаете методы для получения нужных вам данных внутри, а не в репозитории? - person Ryan; 23.12.2009
comment
Нет, это фактический текст данных, а не производный класс. Извините, если поддельное название сбивает с толку. - person Eric King; 23.12.2009

Лично я сделал репозиторий одноразовым. Затем вы получите такие конструкции, как:

void DeleteCustomer(int id)
{
    using(var repos = GetRepos())
    {
        var customer = repos.GetAll<Customer>().Single(x => x.Id == id);
        repos.Delete(customer);
        repos.SaveChanges();
    }
}

Это может быть реализовано путем создания контекста в репозитории ctor и удаления его в реализации Dispose().

Вам нужно убедиться, что вы не добавляете / не изменяете / не удаляете объекты и не выбираете из одного и того же контекста. Контекст делается так, чтобы «длиться» единицу работы.

Однако вы должны быть осторожны с такими вещами:

IQueryable<Customer> GetCustomers()
{
    using(var repos = GetRepos())
    {
        return repos.GetAll<Customer>();
    }
}

void Test()
{
    // This will throw an exception, because it extends the linq query
    // while the context is disposed.
    var customers = GetCustomers().Where(x => x.Id == 123); 
}

В этом случае лучше вынести репозиторий как можно дальше наружу:

IQueryable<Customer> GetCustomers(MyRepository repos)
{
    return repos.GetAll<Customer>();
}

void Test()
{
    using(var repos = ...)
    {
        var customers = GetCustomers(repos).Where(x => x.Id == 123); 
    }
}
person Sander Rijken    schedule 22.12.2009
comment
Какие преимущества это дает? По моему опыту, если вы не можете гарантировать, что, возможно, захотите больше поработать с контекстом, то использование using может вызвать неожиданные ошибки времени выполнения. - person Rippo; 23.12.2009
comment
Да, вы могли бы вообще отказаться от использования. Я думаю, что это довольно безопасно, потому что он закрывает соединение, как только оно с ним завершается (чтобы оно не оставалось открытым), и GC очистит его, когда контекст + запрос выпадает из области видимости. - person Sander Rijken; 23.12.2009

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

Теперь я открываю контекст в каждом методе, однако из-за linq 2 sql и его отложенного выполнения я не уверен, имеет ли это большое значение.

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

person Rippo    schedule 22.12.2009
comment
Это связано с открытием и закрытием соединения. Linq-to-sql и Entity Framework закрывают соединение сразу после завершения запроса. В сочетании с пулом соединений это дает довольно быстрое и масштабируемое решение. Обратите внимание, что соединения намного короче, чем сам контекст. - person Sander Rijken; 22.12.2009
comment
Значит, контекст данных следует схеме: открывать соединение поздно и закрывать раньше? Мое последнее замечание относительно профиля sql все еще в силе. - person Rippo; 23.12.2009