2010-10-26 3 views
3

Как оптимизировать вызовы ActiveRecord в ваших веб-приложениях ASP.NET MVC 2?Как оптимизировать вызовы Castle ActiveRecord

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

Планета имеет много стран. В стране много штатов/провинций. В штате есть много городов. В городе много кварталов.

Ниже приведен пример замок ActiveRecord/NHibernate персистирующей бизнес-объекта

[ActiveRecord] 
public class Country { 

    [Property] 
    public String CountryName { get; set; } 

    [HasMany(typeof(States))] 
    public IList<State> States { get; set; } 
} 

Теперь предположим, что вы хотите сделать невинный запрос, как получить список всех стран на планете

По умолчанию ActiveRecord/Nhibernate загрузит все дерево до самой последней зависимости.

Это может быть много вызовов SQL.

Хорошо, мы можем решить, что с отложенной загрузки [ActiveRecord(lazy=true)], но тогда, когда вы хотите сделать что-то аккуратно, как показано ниже

String text = "This country of " + country.CountryName + " has the following states:"; 

foreach(State s in country.States) 
{ 
    text += s.StateName + " "; 
} 

С ленивым ActiveRecord нагрузки приписывать каждый раз, когда он извлекает для s.StateName это еще один вызов SQL ,

Это слишком много вызовов sql.

Некоторые мои друзья предложили использовать методы ActiveRecordBase, такие как FindAll (критерии).

Но это заканчивается тем, что действительно уродливо и трудно читать со всеми этими Expression.Eq, а что нет.

А потом другие люди также предложили использовать что-то вроде HQL. HQL выглядит как LOT, как SQL.

Итак, самая чистая и оптимизированная версия - это простой и простой SQL-запрос. Это идет прямо к делу.

SELECT * FROM Countries //No loading of an entire tree of dependencies 

SELECT * FROM States WHERE CountryId = @selectedId //1 call and you have all your states 

В любом случае, сейчас я использую запросы SQL и хранимые процедуры для извлечения данных и ActiveRecord для сохранения/удаления объектов.

Пожалуйста, исправьте меня, если я ошибаюсь ...?

Спасибо, Оценка.

ответ

4

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

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

Если вы используете ленивый, но не получаете с нетерпением, вы сталкиваетесь с проблемой SELECT N+1, которую вы заметили в своем примере foreach.

Все, что я упоминал до сих пор, не зависит от используемого вами API запросов. Вы можете сделать все это, используя либо HQL, Criteria, Linq, либо QueryOver. Некоторые запросы легче выразить в HQL, а в некоторых других случаях удобнее использовать критерии, Linq или QueryOver. Но опять же, это ортогональный вопрос.

Кроме того, это не так сильно отличается от запуска необработанных SQL-запросов с помощью ADO.NET. В вашем примере, если вы хотите страну с ее состояниями, вы должны были бы INNER JOINed получить все штаты заранее, а не выдавать отдельные SELECts для каждого состояния.

+0

Глаз быка! Спасибо! – TchiYuan

0

Это звучит очень странно, к сожалению!

Одно из преимуществ ORM заключается в том, что вы можете аннотация в базе данных за 24 часа ...! Итак, да, HQL может чувствовать, выглядеть и звучать как SQL, но он имеет одну большую разницу: он независим от СУБД. Тот же HQL будет работать для SQL Server, или Oracle, или MySQL ...

Вам также понадобится работа для совместного использования соединения с базой данных между NHibernate и SQL Server. Возможно, но немного уродливо.

Я бы пошел с HQL, даже не подумав об этом.Иногда вам действительно нужно перейти на более низкий уровень и написать некоторые SQL-запросы, но сценарий, который вы описываете, далек от того, чтобы быть в этом случае.

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

Просто подумайте: если пользователь запрашивает результирующий набор из 1000 000 записей, но он видит только первые 20 записей на веб-странице, мне не нужно извлекать ВСЕ 1 000 000 записей из базы данных. И это именно то, что ленивая загрузка предназначена для ...

Редактировать: почти забыл кеширование первого и второго уровня NHibernate. Используя это приложение может избежать запросов к уровню СУБД. Это само по себе может привести к усилению повышения производительности ...

+0

Хм, я понимаю вашу точку зрения. Однако в моем случае это всегда будет SQL SERVER. Также я уже отрисовываю все запросы внутри объектов доступа к данным. Если по какой-то причине мне нужно изменить базу данных, единственное, что мне нужно будет переписать, - это объекты доступа к данным. Производительность. Я по-прежнему рекомендую полностью писать ваши запросы в SQL. Особенно в том случае, если ваша собственная база данных не изменится в течение следующих 5 лет. – TchiYuan

+0

Хех, будучи там, сделал это. Могу только сказать, что это решение, о котором вы, вероятно, пожалеете. Существуют способы повышения производительности от HHibernate (например, NHibernate Profiler), и вы будете проигрывать ленивую загрузку, используя простые SQL-запросы (что очень неразумно для решения по производительности). Помните, что мир Java уже доказал, что вы используете GAIN, используя ORM в реальных сценариях. Но будь моим гостем ... :) – rsenna

+0

В моем случае производительность имеет первостепенное значение ... Сценарий, который я представил в своем оригинальном посте, - это упрощенная версия сложности моих структур данных. – TchiYuan

2

Это хорошо известная проблема с ORM. Один из способов решения этой проблемы - добавить настройку nhibernate BatchSize, чтобы каждая поездка в db возвращала n записей (в данном случае состояний) вместо одного.

[HasMany(typeof(States), BatchSize = 20)] 
public IList<State> States { get; set; } 
+0

Проницательный спасибо! – TchiYuan