Члены поля против переменных метода? - PullRequest
2 голосов
/ 11 мая 2010

Недавно я думал о разнице в производительности между членами поля класса и переменными метода. Что именно я имею в виду в следующем примере:

Допустим, у нас есть DataContext объект для Linq2SQL

class DataLayer
{
    ProductDataContext context = new ProductDataContext();

    public IQueryable<Product> GetData()
    {
       return context.Where(t=>t.ProductId == 2);
    }
}

В приведенном выше примере контекст будет сохранен в куче, а переменные метода GetData будут удалены из стека после выполнения метода.

Итак, давайте рассмотрим следующий пример, чтобы провести различие:

class DataLayer
{
    public IQueryable<Product> GetData()
    {
       ProductDataContext context = new ProductDataContext();
       return context.Where(t=>t.ProductId == 2);
    }
} 

(* 1) Итак, первое, что мы знаем, если мы определим экземпляр ProductDataContext как поле, мы можем достичь его везде в классе, что означает, что нам не нужно создавать один и тот же объект. Экземпляр все время.

Но допустим, что мы говорим об Asp.NET, и когда пользователи нажимают кнопку отправки, данные сообщения отправляются на сервер, и события выполняются, а опубликованные данные сохраняются в базе данных вышеописанным способом, поэтому вероятно, что один и тот же пользователь может отправлять разные данные друг за другом. Если я правильно знаю после выполнения страницы, в игру вступают финализаторы и очищают данные из памяти (из кучи), а это означает, что мы теряем переменные нашего экземпляра как из памяти, так и после другой post, DataContext необходимо создать еще раз для нового цикла страницы.

Так что, кажется, единственное преимущество публичного объявления его всему классу - это текст номер один выше.

Или есть что-то другое?

Заранее спасибо ...

(Если я сказал что-то неправильное, пожалуйста, исправьте меня ..)

Ответы [ 4 ]

6 голосов
/ 11 мая 2010

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

Класс DataContext работает как единая единица работы. Таким образом, вы создаете DataContext, вы создаете объекты, обновляете и удаляете объекты, вы отправляете все изменения и после этого удаляете DataContext. Вы можете создать несколько классов DataContext для одного запроса, по одному на (бизнес) транзакцию. Но в ASP.NET вы никогда не должны создавать DataContext, который выдерживает веб-запрос. Все DataContexts, созданные во время запроса, должны быть удалены, когда или до того, как этот запрос закончен. Для этого есть две причины.

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

Вторая причина отсутствия кэширования DataContexts заключается в том, что они не являются поточно-ориентированными. Лучше всего рассматривать DataContext как единицу работы или как (бизнес) транзакцию. Вы создаете кучу новых объектов, добавляете их в DataContext, изменяете некоторые другие, удаляете некоторые объекты, и когда вы закончите, вы вызываете SubmitChanges. Если другой запрос вызывает SubmitChanges для этого же экземпляра во время этой операции, вы теряете идею транзакции. Когда вы позволяете коду делать это, в наиболее удачной ситуации ваши новые объекты сохраняются, а ваша транзакция разделяется на две отдельные транзакции. В худшем случае вы оставляете свой DataContext или объекты, которые он сохраняет, в недопустимом состоянии, что может означать сбой других запросов или попадание недопустимых данных в вашу базу данных. И это не маловероятный сценарий, я видел странные вещи, происходящие с проектами, когда разработчики создавали один (статический) DataContext для каждого веб-сайта.

Итак, помня об этом, давайте вернемся к вашему вопросу. Хотя определение DataContext в качестве поля экземпляра не является проблемой, важно знать, как вы используете класс DataLayer. Когда вы создаете один DataLayer для запроса или для каждого вызова метода, вы, вероятно, будете в безопасности, но в этом случае вы не должны хранить этот DataLayer в статическом поле. Если вы хотите сделать это, вы должны создать DataContext для каждого вызова метода.

Важно знать, как устроен класс DataLayer. В своем коде вы показываете только метод запроса. Нет CUD методов. Каждый метод должен быть отдельной транзакцией, или вы хотите вызвать несколько методов и впоследствии вызвать SaveChanges для DataLayer? Когда вы хотите эту последнюю опцию, вам нужно сохранить DataContext как поле экземпляра, и в этом случае вы должны реализовать IDisposable на DataLayer. Когда каждый метод является своей собственной транзакцией, вы можете создать DataContext для каждого метода, и вам следует заключить DataContext в оператор using. Однако обратите внимание, что удаление DataContext может вызвать проблемы, когда вы возвращаете объекты с отложенной загрузкой свойств из метода. Эти свойства больше не могут быть загружены при удалении DataContext. Здесь более интересная информация об этом.

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

Извините за длинный ответ: -)

4 голосов
/ 11 мая 2010

Вы никогда не хотите хранить класс DataContext на уровне класса. Если вы это сделаете, вам нужно будет реализовать IDisposable интерфейс в вашем классе и вызвать метод Dispose , когда вы узнаете, что с ним покончено.

Лучше просто создать новый DataContext в вашем методе и использовать оператор using , чтобы автоматически избавиться от него, когда вы закончите.

Хотя реализация IDisposable в DataContext ничего не делает, это деталь реализация , в то время как предоставление интерфейса IDisposable - это контракт , который вы всегда должны соблюдать.

Это особенно удобно, если вы обновляетесь до LINQ-to-Entities и используете ObjectContext класс , где вы должны вызвать Dispose для экземпляра, когда вы закончите с ним, в противном случае, ресурсы будут просачиваться до следующей сборки мусора.

1 голос
/ 12 мая 2010

См. Это, например, «Linq to SQL DataContext Lifetime Management»: http://www.west -wind.com / weblog / posts / 246222.aspx Такой подход упрощает жизнь.

1 голос
/ 11 мая 2010

Так что, похоже, единственное преимущество объявив это публично всему класс - это просто номер один текст выше.

Да, объявление переменной уровня класса позволяет всему классу обращаться к одной и той же переменной. Он не должен использоваться для того, чтобы попытаться сознательно предотвратить сборку мусора. Модификатор доступа к свойствам, методам и т. Д. Используется для определения того, какие объекты, внешние или внутренние для вашего класса, могут получить доступ / изменить / получить доступ к этому коду.

В ASP.NET после отправки запроса в браузер созданные объекты для этого запроса страницы будут получать CGed в определенный момент времени в будущем, независимо от того, является ли переменная общедоступной. Если вы хотите, чтобы информация оставалась между запросами, вам нужно либо создать одноэлементный экземпляр объекта, либо сериализовать объект в состояние сеанса или приложения.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...