NHibernate / LINQ - Совокупный запрос по вложенной коллекции - PullRequest
4 голосов
/ 24 июля 2010

Запрос дочерних коллекций был постоянной проблемой в наших приложениях, где мы используем NHibernate (через LINQ). Я хочу выяснить, как сделать это правильно. Я просто пытался навсегда заставить этот запрос работать эффективно, используя LINQ, и сдался. Может кто-нибудь помочь мне понять лучший способ сделать что-то подобное?

Model: ServiceProvider
            HasMany->ServicesProvided

Гвоздь здесь в том, что HasMany отображается как компонент, поэтому я не могу напрямую запросить ServicesProvided. Для потомков, вот карта:

    public ServiceProviderMap()
    {
        DiscriminatorValue(ProfileType.SERVICE_PROVIDER.ID);

        HasMany(p => p.ServicesProvided)
            .Table("ServiceProvider_ServicesProvided")
            .KeyColumn("ProfileID")
            .Component(spMapping =>
            {
                spMapping.Map(service => service.ID)
                    .Not.Nullable();
            })
            .AsBag();
    }

Запрос, который я пытаюсь создать, вернул бы набор счетчиков для каждой предоставляемой службы. IE: Service1 -> 200, Service2 -> 465 и т. Д.

Мне удалось заставить запрос работать с использованием HQL, так что вот оно. Обратите внимание, что он просто возвращает идентификатор предоставляемой услуги:

    select service.ID, count(service) 
    from ServiceProvider as profile 
    inner join profile.ServicesProvided as service 
    group by service.ID

Мне удалось заставить запрос "работать" с помощью LINQ, но он выполнялся ужасно. Вот код, который я использовал (предупреждение - это ужасно).

    Func<ServiceProvider, IEnumerable<ServicesProvided>> childSelector = sp => sp.ServicesProvided;
    var counts = this._sessionManager.GetCurrentSession().Linq<ServiceProvider>()
        .Expand("ServicesProvided")
        .SelectMany(childSelector, (t, c) => new { t = t, c = c })
        .Select(child => child.c)
        .GroupBy(sp => sp.ID)
        .Select(el => new { serviceID = el.Key, count = el.Count() });

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

1 Ответ

0 голосов
/ 23 апреля 2011

Если не считать HQL, самым элегантным решением, которое я могу придумать, было бы использование объекта Criteria. Следующее даст вам то, что вам нужно и с очень низкими накладными расходами:

ICriteria criteria = this._sessionManager.GetCurrentSession().CreateCriteria(typeof(ServiceProvider), "sp");

//set projections for the field and aggregate, making sure to group by the appropriate value
criteria.CreateAlias("sp.ServicesProvided", "s", JoinType.LeftOuterJoin)
   .SetProjection(Projections.ProjectionList()
      .Add(Projections.Property("s.ID"), "serviceID")
      .Add(Projections.Count("sp.ID"), "count")
      .Add(Projections.GroupProperty("s.ID")));

IList<object[]> results = criteria.List();

foreach (object[] entry in results)
{
   int id = (int)entry[0], qty = (int)entry[1];

   //Do stuff with the values
}
...