Как получить отличный результат с помощью nHibernate и QueryOver API? - PullRequest
45 голосов
/ 06 января 2011

У меня есть этот метод репозитория

    public IList<Message> ListMessagesBy(string text, IList<Tag> tags, int pageIndex, out int count, out int pageSize)
    {
        pageSize = 10;
        var likeString = string.Format("%{0}%", text);
        var query = session.QueryOver<Message>()
            .Where(Restrictions.On<Message>(m => m.Text).IsLike(likeString) || 
            Restrictions.On<Message>(m => m.Fullname).IsLike(likeString));

        if (tags.Count > 0)
        {
            var tagIds = tags.Select(t => t.Id).ToList();
            query
                .JoinQueryOver<Tag>(m => m.Tags)
                .WhereRestrictionOn(t => t.Id).IsInG(tagIds);
        }            

        count = 0;
        if(pageIndex < 0)
        {
            count = query.ToRowCountQuery().FutureValue<int>().Value;
            pageIndex = 0;
        }
        return query.OrderBy(m => m.Created).Desc.Skip(pageIndex * pageSize).Take(pageSize).List();
    }

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

Projections.Distinct

Но для этого требуется список свойств, чтобы ответить на отдельный вопрос.Это сообщение является моим корнем сущности. Есть ли способ получить такое поведение без указания всех свойств сущности?

Заранее спасибо, Андерс

Ответы [ 4 ]

65 голосов
/ 06 января 2011

Если вы используете API-интерфейс ICriteria, вам необходимо:

.SetResultTransformer(new DistinctEntityRootTransformer())

Если вы используете API QueryOver, вам необходимо:

.TransformUsing(Transformers.DistinctRootEntity)

Но будьте осторожны, все это происходит на стороне клиента, поэтому все повторяющиеся строки по-прежнему извлекаются.

27 голосов
/ 24 февраля 2011

Попробуйте что-то вроде этого

public IPagedList<Client> Find(int pageIndex, int pageSize)
{
    Client clientAlias = null;

    var query = Session.QueryOver<Client>(() => clientAlias)

        .Select(
            Projections.Distinct(
                Projections.ProjectionList()
                    .Add(Projections.Property<Client>(x => x.Id).As("Id"))
                    .Add(Projections.Property<Client>(x => x.Name).As("Name"))
                    .Add(Projections.Property<Client>(x => x.Surname).As("Surname"))
                    .Add(Projections.Property<Client>(x => x.GivenName).As("GivenName"))
                    .Add(Projections.Property<Client>(x => x.EmailAddress).As("EmailAddress"))
                    .Add(Projections.Property<Client>(x => x.MobilePhone).As("MobilePhone"))
            )
        )
        .TransformUsing(Transformers.AliasToBean<Client>())

        .OrderBy(() => clientAlias.Surname).Asc
        .ThenBy(() => clientAlias.GivenName).Asc;

    var count = query
        .ToRowCountQuery()
        .FutureValue<int>();

    return query
        .Take(pageSize)
        .Skip(Pagination.FirstResult(pageIndex, pageSize))
        .List<Client>()
        .ToPagedList(pageIndex, pageSize, count.Value);
}
13 голосов
/ 06 апреля 2011

Вы можете использовать SelectList и GroupBy, например:

tags.SelectList(t => t.SelectGroup(x => x.Id))

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

Если вам нужно несколько предметов в группе, сделайте что-то вроде:

tags.SelectList(t => t.SelectGroup(x => x.Id)
                      .SelectGroup(x => x.Name)
               )
0 голосов
/ 12 августа 2011

Я недавно создал метод для применения выбора различного на основе типа сопоставленного объекта. Это применяет это к объекту IQueryOver (свойство класса). Метод также имеет доступ к конфигурации nhibernate. Вы можете добавить их в качестве параметров метода. Нужно работать для производства, но метод отлично работает в dev, пока что использовал его только для одного объекта.

Этот метод был создан, потому что я пытаюсь выстроить свои данные на уровне сервера и преобразователь результатов не будет работать.

После того как вы получите коллекцию объектов (query.List ()), вам может потребоваться перезагрузить объекты, чтобы заполнить один или несколько дочерних объектов. Отображения "многие к одному" будут передаваться для ленивых нагрузок.

 public void DistinctRootProjectionList<E>()
    {
        var classMapping = Context.Config.GetClassMapping(typeof(E));
        var propertyIterator = classMapping.UnjoinedPropertyIterator;
        List<IProjection> projections = new List<IProjection>();
        ProjectionList list = Projections.ProjectionList();

        list.Add(Projections.Property(classMapping.IdentifierProperty.Name), classMapping.IdentifierProperty.Name);

        foreach (var item in propertyIterator)
        {
            if (item.Value.IsSimpleValue || item.Value.Type.IsEntityType)
            {
                list.Add(Projections.Property(item.Name), item.Name);
            }
        }
        query.UnderlyingCriteria.SetProjection(Projections.Distinct(list));
        query.TransformUsing(Transformers.AliasToBean<E>());
    }

Код, который я использовал для загрузки отношений один ко многим ... T - тип сущности.

for (int i = 0; i < resp.Data.Count; i++)
        {
            resp.Data[i] = session.Load<T>(GetInstanceIdValue(resp.Data[i]));
        }
...