Как решить проблему неправильной инициализации коллекции nHibernate

nHibernate3; получение записей 4xxx из схемы данных EAV. Когда nHibernate или .NET впервые инициализирует эти коллекции, мы наблюдаем серьезный штраф. Последующие вызовы кажутся более эффективными. Выполнение тех же запросов в SQL Server Management Studio приведет к ожидаемому быстрому возврату.

Использование Fluent и сопоставления времени выполнения вместо .hbm.xml; Любопытно, поможет ли здесь сериализованное отображение?

nHibernate Profiler и log4net, похоже, не дали мне многого. В этом процессе гидратируется около 140 000 сущностей.

Прилагаю снимок экрана моей трассировки производительности dotTrace, который показывает штраф за инициализацию коллекции: dotTrace медленной инициализации коллекции nHibernate

Пробовали объединяться и нетерпеливые типы выборки, без видимых результатов, но я не на 100% уверен, что реализовал их правильно - нужно ли так назначить только родительский элемент или дочерние таблицы также должны быть помечены?

var products = ((HandleSession)_handleSession).Session.CreateCriteria(typeof(Product))
                    .SetFetchMode("Product", FetchMode.Eager)
                    .List<Product>()
                    .AsEnumerable();

С включенным оптимизатором отражения (я думаю) через web.config: С включенным оптимизатором отражения

Вот где тратится больше всего времени:

return new ProductList(products.Select(p => p.ToProductContract()));

Это просто метод расширения, делающий это:

public static ProductContract ToProductContract(this Product product)
        {
            return new ProductContract
                       {
                           Name = product.ProductName,
                           ProductTypeName = product.ProductType.ProductTypeName,
                           UpdateTimeStamp = product.UpdateDateTime,
                           ProductNumber = product.ProductNumber,
                           Attributes = product.ProductAttributes.ToCommonAttribute().ToList(),
                           GroupCategories = product.ProductGroups.ToGroupCategory().ToList(),
                           PublicUniqueId = product.PublicUniqueId
                       };
        }

сопоставления:

internal class ProductMapping : ClassMap<Product>
    {
        private const string _iscurrentindicator = "IsCurrentIndicator=1";

        public ProductMapping()
        {
            Table("Product");
            Id(Reveal.Member<Product>("ProductId")).GeneratedBy.Identity().Column("ProductID");
            Map(x => x.ProductNumber).Column("ProductNumber").Not.Nullable();
            Map(x => x.ProductName).Column("ProductName").Not.Nullable();
            Map(x => x.InsertDateTime).Column("InsertedDateTime").Nullable().ReadOnly();
            Map(x => x.UpdateDateTime).Column("UpdatedDateTime").Nullable();
            Map(x => x.PublicUniqueId).Column("ProductGUID").Generated.Insert();

            References(x => x.ProductType).Column("ProductTypeId").Not.Nullable();
            HasMany(x => x.ProductAttributes)
                .KeyColumn("ProductId")
                .Inverse()
                .Fetch
                .Subselect()
                .Where(_iscurrentindicator)
                .Cascade
                .SaveUpdate();

            HasMany(x => x.ProductGroups).KeyColumn("ProductId").Fetch.Subselect().Where(_iscurrentindicator);
            DynamicUpdate();
            DynamicInsert();
            BatchSize(500);
        }
    }

internal class ProductGroupMapping : ClassMap<ProductGroup>
    {
        public ProductGroupMapping()
        {
            Table("ProductGroup");
            Id(x => x.ProductGroupId).Column("ProductGroupId").GeneratedBy.Identity();
            References(x => x.Product).Column("ProductId").Not.Nullable();
            References(x => x.Group).Column("GroupId").Not.Nullable();
            //Where("IsCurrentIndicator=1");
        }
    }

internal class ProductAttributeMapping : ClassMap<ProductAttribute>
    {
        public ProductAttributeMapping()
        {
            Table("ProductAttribute");
            LazyLoad();
            Id(x => x.ProductAttributeId).GeneratedBy.Identity().Column("ProductAttributeID");
            References(x => x.Product).Column("ProductID").Not.Nullable();
            References(x => x.Attribute).Column("AttributeID").Not.Nullable().Fetch.Join();
            Map(x => x.PositionNumber).Column("PositionNumber").Nullable();
            Map(x => x.ValueText).Column("ValueText").Nullable();
            Map(x => x.ValueBinary).Column("ValueBinary").Nullable();

            Component(x => x.OperationalAuditHistory, m =>
                        {
                            Table("ProductAttribute");
                            m.Map(x => x.ExpirationDateTime).Column("ExpirationDateTime").Nullable();
                            m.Map(x => x.IsCurrent).Column("IsCurrentIndicator").Not.Nullable();
                            m.Map(x => x.OperationCode).Column("OperationCode").Nullable();
                            m.Map(x => x.OperationDateTime).Column("OperationDateTime").Nullable();
                            m.Map(x => x.OperationSystemName).Column("OperationSystemName").Nullable();
                            m.Map(x => x.OperationUserName).Column("OperationUserName").Nullable();
                            m.Map(x => x.LastUserPriority).Column("LastUserPriority").Nullable();
                        });

            DynamicInsert();
            BatchSize(50);
        }
    }

К сожалению, с .Future я все еще получаю аналогичные результаты. Вот новый след; На данный момент я переключился на Release и x64 для ключевых проектов, так что времена меньше, но пропорции все еще в значительной степени те же; а также с .Eager:

var products = ((HandleSession) _handleSession).Session.CreateCriteria(typeof (Product))
                    .SetFetchMode("ProductAttribute", FetchMode.Join)
                    .SetFetchMode("ProductGroup", FetchMode.Join)
                    .SetFetchMode("ProductType", FetchMode.Join)
                    .Future<Product>()
                    .AsEnumerable();

dotTrace - режим выпуска, нацеленный на x64, с .Future ()

Сгенерированный SQL с .Eager и .Future на месте:

ВЫБОР this_.ProductID, как ProductID0

var products = ((HandleSession)_handleSession).Session.CreateCriteria(typeof(Product))
                    .SetFetchMode("Product", FetchMode.Eager)
                    .List<Product>()
                    .AsEnumerable();
, this_.ProductNumber как ProductN2_0
var products = ((HandleSession)_handleSession).Session.CreateCriteria(typeof(Product))
                    .SetFetchMode("Product", FetchMode.Eager)
                    .List<Product>()
                    .AsEnumerable();
, this_.ProductName как ProductN3_0
var products = ((HandleSession)_handleSession).Session.CreateCriteria(typeof(Product))
                    .SetFetchMode("Product", FetchMode.Eager)
                    .List<Product>()
                    .AsEnumerable();
, this_.InsertedDateTime, как Inserted4_0
var products = ((HandleSession)_handleSession).Session.CreateCriteria(typeof(Product))
                    .SetFetchMode("Product", FetchMode.Eager)
                    .List<Product>()
                    .AsEnumerable();
, this_.UpdatedDateTime, как UpdatedD5_0
var products = ((HandleSession)_handleSession).Session.CreateCriteria(typeof(Product))
                    .SetFetchMode("Product", FetchMode.Eager)
                    .List<Product>()
                    .AsEnumerable();
, this_.ProductGUID, как ProductG6_0
var products = ((HandleSession)_handleSession).Session.CreateCriteria(typeof(Product))
                    .SetFetchMode("Product", FetchMode.Eager)
                    .List<Product>()
                    .AsEnumerable();
, this_.ProductTypeId, как ProductT7_0
var products = ((HandleSession)_handleSession).Session.CreateCriteria(typeof(Product))
                    .SetFetchMode("Product", FetchMode.Eager)
                    .List<Product>()
                    .AsEnumerable();
, producttyp2_.ProductTypeID как ProductT1_6_0_, producttyp2_ .ProductTypeName как ProductT2_6_0_ FROM Product this_ внутреннее соединение ProductType producttyp2_ на this_.ProductTypeId = producttyp2_.ProductTypeID;

ВЫБОР productatt0_.ProductId, как ProductId2_, productatt0_.ProductAttributeID как ProductA1

return new ProductList(products.Select(p => p.ToProductContract()));
, productatt0_.ProductAttributeID как ProductA1_2
var products = ((HandleSession)_handleSession).Session.CreateCriteria(typeof(Product))
                    .SetFetchMode("Product", FetchMode.Eager)
                    .List<Product>()
                    .AsEnumerable();
, productatt0_.PositionNumber как Position2_2
var products = ((HandleSession)_handleSession).Session.CreateCriteria(typeof(Product))
                    .SetFetchMode("Product", FetchMode.Eager)
                    .List<Product>()
                    .AsEnumerable();
, productatt0_.ValueText как ValueText2
var products = ((HandleSession)_handleSession).Session.CreateCriteria(typeof(Product))
                    .SetFetchMode("Product", FetchMode.Eager)
                    .List<Product>()
                    .AsEnumerable();
, productatt0_.ValueBinary как ValueBin4_2
var products = ((HandleSession)_handleSession).Session.CreateCriteria(typeof(Product))
                    .SetFetchMode("Product", FetchMode.Eager)
                    .List<Product>()
                    .AsEnumerable();
, productatt0_.ProductID как ProductID2
var products = ((HandleSession)_handleSession).Session.CreateCriteria(typeof(Product))
                    .SetFetchMode("Product", FetchMode.Eager)
                    .List<Product>()
                    .AsEnumerable();
, productatt0_.AttributeID как Attribut6_2
var products = ((HandleSession)_handleSession).Session.CreateCriteria(typeof(Product))
                    .SetFetchMode("Product", FetchMode.Eager)
                    .List<Product>()
                    .AsEnumerable();
, productatt0_ .ExpirationDateTime, как Expirati7_2
var products = ((HandleSession)_handleSession).Session.CreateCriteria(typeof(Product))
                    .SetFetchMode("Product", FetchMode.Eager)
                    .List<Product>()
                    .AsEnumerable();
, productatt0_.IsCurrentIndicator как IsCurren8_2
var products = ((HandleSession)_handleSession).Session.CreateCriteria(typeof(Product))
                    .SetFetchMode("Product", FetchMode.Eager)
                    .List<Product>()
                    .AsEnumerable();
, productatt0_.OperationCode как Operatio9_2
var products = ((HandleSession)_handleSession).Session.CreateCriteria(typeof(Product))
                    .SetFetchMode("Product", FetchMode.Eager)
                    .List<Product>()
                    .AsEnumerable();
, productatt0_.OperationDateTime как Operati10_2
var products = ((HandleSession)_handleSession).Session.CreateCriteria(typeof(Product))
                    .SetFetchMode("Product", FetchMode.Eager)
                    .List<Product>()
                    .AsEnumerable();
, productatt0_.OperationSystemName как Operati11_2
var products = ((HandleSession)_handleSession).Session.CreateCriteria(typeof(Product))
                    .SetFetchMode("Product", FetchMode.Eager)
                    .List<Product>()
                    .AsEnumerable();
, productatt0_.OperationUserName как Operati12_2
var products = ((HandleSession)_handleSession).Session.CreateCriteria(typeof(Product))
                    .SetFetchMode("Product", FetchMode.Eager)
                    .List<Product>()
                    .AsEnumerable();
, productatt0_.LastUserPriority как LastUse13_2
var products = ((HandleSession)_handleSession).Session.CreateCriteria(typeof(Product))
                    .SetFetchMode("Product", FetchMode.Eager)
                    .List<Product>()
                    .AsEnumerable();
, attribute1_.AttributeId как Attribut1
var products = ((HandleSession)_handleSession).Session.CreateCriteria(typeof(Product))
                    .SetFetchMode("Product", FetchMode.Eager)
                    .List<Product>()
                    .AsEnumerable();
0_, attribute1_.AttributeName как Attribut2
var products = ((HandleSession)_handleSession).Session.CreateCriteria(typeof(Product))
                    .SetFetchMode("Product", FetchMode.Eager)
                    .List<Product>()
                    .AsEnumerable();
0_, attribute1_.DisplayName как DisplayN3
var products = ((HandleSession)_handleSession).Session.CreateCriteria(typeof(Product))
                    .SetFetchMode("Product", FetchMode.Eager)
                    .List<Product>()
                    .AsEnumerable();
0_, attribute1_.DataTypeName как DataType4
var products = ((HandleSession)_handleSession).Session.CreateCriteria(typeof(Product))
                    .SetFetchMode("Product", FetchMode.Eager)
                    .List<Product>()
                    .AsEnumerable();
0_, attribute1_.ConstraintText как Constrai5
var products = ((HandleSession)_handleSession).Session.CreateCriteria(typeof(Product))
                    .SetFetchMode("Product", FetchMode.Eager)
                    .List<Product>()
                    .AsEnumerable();
0_, attribute1_.ConstraintMin как Constrai6
var products = ((HandleSession)_handleSession).Session.CreateCriteria(typeof(Product))
                    .SetFetchMode("Product", FetchMode.Eager)
                    .List<Product>()
                    .AsEnumerable();
0_, attribute1_.ConstraintMax как Constrai7
var products = ((HandleSession)_handleSession).Session.CreateCriteria(typeof(Product))
                    .SetFetchMode("Product", FetchMode.Eager)
                    .List<Product>()
                    .AsEnumerable();
0_, attribute1_.ValuesMin как ValuesMin1_0_, attribute1_.ValuesMax как ValuesMax1_0.Соединение_, attribute1_. IsCurrentIndicator = 1) и productatt0_.ProductId в (выберите this_.ProductID FROM Product this_ внутреннее соединение ProductType producttyp2_ на this_.ProductTypeId = producttyp2_.ProductTypeID)

ВЫБЕРИТЕ productgro0_.ProductId как ProductId1_, productgro0_.ProductGroupId как ProductG1

var products = ((HandleSession)_handleSession).Session.CreateCriteria(typeof(Product))
                    .SetFetchMode("Product", FetchMode.Eager)
                    .List<Product>()
                    .AsEnumerable();
, productgro0_.ProductGroupId как ProductG1
public static ProductContract ToProductContract(this Product product)
        {
            return new ProductContract
                       {
                           Name = product.ProductName,
                           ProductTypeName = product.ProductType.ProductTypeName,
                           UpdateTimeStamp = product.UpdateDateTime,
                           ProductNumber = product.ProductNumber,
                           Attributes = product.ProductAttributes.ToCommonAttribute().ToList(),
                           GroupCategories = product.ProductGroups.ToGroupCategory().ToList(),
                           PublicUniqueId = product.PublicUniqueId
                       };
        }
0_, productgro0_.ProductId как ProductId3_0_, productgro0_.GroupId как Product_d3_groupId_продукта (Product_GroupId_Group_Droduct_d3_0_) ProductID FROM Product this_ внутреннее соединение ProductType producttyp2_ на this_.ProductTypeId = producttyp2_.ProductTypeID)


person andrewbadera    schedule 15.04.2011    source источник
comment
попробуйте включить оптимизатор отражения   -  person driushkin    schedule 16.04.2011
comment
был незнаком с этим, посмотрю, спасибо - у вас есть код для этого, в отличие от конфигурации xml, которую я, кажется, нахожу через Google?   -  person andrewbadera    schedule 16.04.2011
comment
Добавление скриншота с включенной оптимизацией отражения через web.config (думаю).   -  person andrewbadera    schedule 16.04.2011
comment
Оптимизация отражения включена по умолчанию, если вы специально не отключили ее. Пожалуйста, разместите свои сопоставления для ваших коллекций.   -  person jishi    schedule 18.04.2011
comment
Я не думаю, что вы можете оптимизировать его на данном этапе. Я думаю, вам нужно будет детализировать, какие данные вам действительно нужны, и сузить их, используя HQL в вашем случае. 140 000 сущностей - это много для обработки NHibernate, и мне еще предстоит найти оптимизацию, которая справится с этим. Я сам страдаю от такого же снижения производительности и попытаюсь решить его, используя более простые объекты представления, которые я сам заполняю базовыми полями, полученными с помощью HQL. Все сводится к сценарию использования.   -  person jishi    schedule 20.04.2011
comment
Спасибо, Джиси, я тоже пришел к такому выводу. И / или полностью исключить nHibernate для этой операции.   -  person andrewbadera    schedule 20.04.2011


Ответы (3)


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

2) Установить FetchMode нужно применить к дочерним элементам, например:

var products = ((HandleSession)_handleSession).Session.CreateCriteria(typeof(Product))
                .SetFetchMode("ProductChildren", FetchMode.Eager)
                .List<Product>()
                .AsEnumerable();

3) Это похоже на проблему N + 1, если я правильно интерпретирую методы на скриншотах. Вы преобразуете Products в результате запроса в список ProductDTO? Если это так, кажется, что дочерние коллекции лениво загружаются из БД в цикле.

Изменить:

Чтобы противостоять N + 1 Select, нам нужно будет указать NHibernate, чтобы он загружал все заранее, предпочтительно с Futures. Вот потенциальное решение, которое в основном извлекает все ваши данные из базы данных с помощью нескольких операторов Select. Я не включил никаких условий Где. Те, которые вам придется добавить соответственно.

// any where-condition will have to be applied here and in the subsequent queries
var products = session.QueryOver<Product>()
    .Future();

var products2 = session.QueryOver<Product>()
    .Fetch(p => p.ProductType).Eager
    .Future();

var products3 = session.QueryOver<Product>()
    .Fetch(p => p.ProductAttributes).Eager
    .Future();

var products4 = session.QueryOver<Product>()
    .Fetch(p => p.ProductGroups).Eager
    .Future();

// Here we execute all of the above queries in one roundtrip.
// Since we already have all the data we could possibly want, there is no need
// for a N+1 Select.
return new ProductList(products.Select(p => p.ToProductContract()));
person Florian Lim    schedule 16.04.2011
comment
Пока что это был первый доступ к БД в моем тестировании; Я только что вошел в этот проект, поэтому у меня не было возможности подробно описать сценарии тестирования, я не уверен, является ли это проблемой первого доступа или нет, я выясню. - person andrewbadera; 16.04.2011
comment
Да, я считаю, что именно это и делается в ToProductList. Проверю в понедельник. - person andrewbadera; 16.04.2011
comment
Применение .Eager к двум дочерним элементам таблицы Product не привело к повышению производительности. Кроме того, это не проблема первого доступа, поэтому сериализованное сопоставление, вероятно, мне ничего не даст. На самом деле проблема заключается в преобразовании сущностей в DTO. Выложу код выше; есть ли более эффективные средства для заполнения этих коллекций, или это неизбежный эффект схемы EAV? - person andrewbadera; 18.04.2011
comment
@andrewbadera Вы пытались применить FetchMode.Eager к .ProductType, .ProductAttributes и .ProductGroups. Все это определенно вызовет выбор N + 1 из-за цикла. Да, и .ToCommonAttribute() и .ToGroupCategory() могут вызвать аналогичную проблему. Чтобы обойти это, вам, вероятно, придется сначала получить все данные, прежде чем преобразовывать объект в DTO. Похоже, что загрузка 4000+ сущностей из базы данных с дочерними коллекциями и внуками приводит к 140000 операторам выбора. Вы можете убедиться в этом в выводе log4net NHibernate.SQL. - person Florian Lim; 18.04.2011
comment
@andrewbadera Если вы закомментируете три строки для ProductTypeName, Attributes и GroupCategories, а затем раскомментируете каждую из них, вы сможете сузить проблему. Также посмотрите, лениво ли .ToCommonAttribute() и .ToGroupCategory() загружают что-нибудь из базы данных. - person Florian Lim; 18.04.2011
comment
Да, как уже говорилось, сделано 140 000 звонков, сущности здесь гидратированы. Изначально использовалось соединение выбора по умолчанию, я пробовал .Join и .Eager без явно заметных изменений в поведении или производительности. В профилировщике SQL Server или профилировщике nHibernate отображаются только два запроса ... - person andrewbadera; 18.04.2011
comment
Это должно быть как минимум 3 запроса, один для продуктов и два последующих для каждой коллекции, чтобы предотвратить декартово произведение - person jishi; 18.04.2011
comment
Комментирование этих строк, к сожалению, приводит к тому, что результаты не возвращаются, точка, без дальнейшего редактирования в каком-то еще неопределенном месте. - person andrewbadera; 18.04.2011
comment
@andrewbadera Я добавил решение с QueryOver. Если вам нужна версия ICriteria (MultiCriteria) и вы не знаете, как ее перевести, дайте мне знать. - person Florian Lim; 19.04.2011
comment
К сожалению, .Future, похоже, ничем не помогает. Код и dotTrace выше. - person andrewbadera; 19.04.2011
comment
@andrewbadera Не могли бы вы опубликовать созданный SQL-запрос? Мне кажется, что мое предположение относительно N + 1 Select было неверным. (.Future() помогает только при наличии более одного запроса, поэтому я добавил его в свое предложение.) - person Florian Lim; 19.04.2011
comment
@andrewbadera Думаю, я подошел к этому с неправильной точки зрения. Как заявил Джиши, NHibernate может быть слишком медленным для такого количества данных, когда ему нужно преобразовать более 140 тысяч строк в ResultSet в фактические объекты. Поскольку вы все равно конвертируете объекты в DTO, возможно, имеет смысл создавать DTO непосредственно из запроса (проекций), тем самым освобождая NHibernate от ответственности за гидратирование сложных объектов. Если вы открыты для этого решения, я посмотрю, смогу ли я что-нибудь собрать. (Я не уверен, сработает ли это вообще, но я готов попробовать.) - person Florian Lim; 20.04.2011
comment
спасибо Флориану; это то, что мы обсуждали с другим разработчиком, но ни один из них не был хорошо знаком с тем, как это выглядело, и у него еще не было возможности изучить это. - person andrewbadera; 20.04.2011
comment
@andrewbadera Просто чтобы вы знали, я протестировал несколько возможных решений, но даже используя StatelessSession и прогнозы (я не решил проблему с дочерними коллекциями, но неважно) вы сможете сэкономить только около 60-70% время, которое, я полагаю, все равно займет слишком много времени. Итак, я согласен с дзиси и предлагаю то, что вы, по-видимому, придумали сейчас. Не используйте NH в этом конкретном случае, а прибегните к простому SQL и самостоятельно объедините объекты. - person Florian Lim; 23.04.2011
comment
Спасибо, Флориан; Я не мог заставить работать сеансы без сохранения состояния из-за всех коллекций атрибутов. Есть ли шанс, что вы могли бы поделиться своим кодом по этому поводу или по прогнозам, которые я тоже начал, но изменились ли мои приоритеты для меня из-за внешних зависимостей? Нам удалось несколько значительно улучшить его, сглаживая или удаляя ассоциацию компонентов; не на 100% уверен, что здесь было сделано, я проверю код на следующей неделе, другой разработчик занимался этим. Тем временем я также реализовал простое кэширование материализованных объектов AppFabric вместе с действием запуска службы для этого большого набора / карты. - person andrewbadera; 23.04.2011

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

Это не имеет значения, если вы выбираете 1 объект с одной коллекцией, но может иметь огромное значение, если вы выберете 1000 объектов, все из которых имеют одну коллекцию. Использование размера пакета 1000 приведет к 2 запросам вместо 1001.

Пытался найти документацию, но нашел только этот пример:

nhibernate заменяет размер пакета

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

Еще одна вещь, которая может улучшить производительность, - это установка:

Session.FlushMode = FlushMode.Never;

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

person jishi    schedule 18.04.2011
comment
Размер пакета уже включен; было 50, я попробовал и 500, в поведении не изменилось. - person andrewbadera; 18.04.2011
comment
Кроме того, здесь всего два запроса - это инициализация коллекции и заполнение, которые занимают все время. Ну, и механизм MVC отображает результаты. - person andrewbadera; 18.04.2011
comment
Мой опыт показывает, что NHibernate довольно медленно преобразует данные из набора результатов в сущности, когда вы достигаете приличного количества объектов. Согласно снимку экрана, вы имеете дело с результатами размером более 100 000 строк, это правильно? - person jishi; 18.04.2011
comment
Сколько на самом деле строк у вас в БД? для продукта, CommonAttribute и GroupCategory для этого конкретного запроса? - person jishi; 18.04.2011
comment
~ 4000 продуктов, 2 типа продукта, 60 атрибутов, ~ 140 000 строк атрибута ProductAttribute. - person andrewbadera; 18.04.2011
comment
Я считаю, что набор результатов шириной всего в несколько столбцов, но длиной 140 000 не должен быть таким медленным, как указано. Но если он является частью нескольких объединений, то результат, который ему нужно получить, будет намного больше. Я думаю, нам нужно больше узнать о вашей структуре данных, чтобы дать вам полезные советы. - person jishi; 19.04.2011
comment
Я начал подозревать, что проблема в значительной степени может быть результатом предыдущего решения кого-то использовать схему EAV. Я собираюсь попробовать описанную выше установку Флориана; если это не сработает, я отправлю схему для дальнейшего изучения или комментариев, спасибо! - person andrewbadera; 19.04.2011

Если вы используете этот сеанс только для отчетов, вам необходимо использовать сеансы без сохранения состояния: http://nhforge.org/blogs/nhibernate/archive/2008/10/30/bulk-data-operations-with-nhibernate-s-stateless-sessions.aspx

person VahidN    schedule 20.04.2011
comment
Спасибо, вчера вечером это тоже было упомянуто в списке nhusers, я посмотрю! - person andrewbadera; 20.04.2011