Comment obtenir un résultat distinct avec nHibernate et QueryOver API?

j'ai cette méthode de dépôt

    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();
    }

vous fournissez une chaîne de recherche de texte libre et une liste d'étiquettes. Le problème est que si un message a plus d'une étiquette, il est répertorié fois dupliqué. Je veux un résultat distinct basé sur l'entité du Message. J'ai regardé

Projections.Distinct

Mais il nécessite une liste de Propriétés à la question distincte. Ce Message est ma racine d'entity il y a le plus de façon d'obtenir ce comportement sans fournir toute l'entity propriétés?

Merci d'avance, Anders

44
demandé sur Anders 2011-01-06 17:01:13

4 réponses

si vous utilisez L'API ICriteria, vous devez:

.SetResultTransformer(new DistinctEntityRootTransformer())

si vous utilisez L'API QueryOver, vous devez:

.TransformUsing(Transformers.DistinctRootEntity)

mais attention, tout cela se produit du côté du client, donc toutes les lignes dupliquées sont encore tirées.

61
répondu Sly 2015-08-06 10:49:07

Essayez quelque chose comme ceci

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);
}
25
répondu Craig 2011-02-24 00:33:32

vous pouvez utiliser SelectList et GroupBy, E. g:

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

devrait fonctionner et produire le même plan de requête que distinct.

Si vous avez besoin de plusieurs éléments dans le groupe, de faire quelque chose comme:

tags.SelectList(t => t.SelectGroup(x => x.Id)
                      .SelectGroup(x => x.Name)
               )
12
répondu Chris Haines 2011-04-06 15:49:53

j'ai récemment créé une méthode pour appliquer select distinct basée sur un type d'objet mappé. Cela s'applique à un objet IQueryOver (propriété de classe). La méthode a également accès à la configuration de nhibernate. Vous pouvez les ajouter comme paramètres de méthode. A besoin de travail pour la production, mais la méthode fonctionne bien dans dev, utilisé seulement pour une entité jusqu'à présent.

cette méthode a été créée parce que j'essaie de page Mes données au niveau du serveur et un transformateur de résultat distinct ne serait pas travail.

après avoir obtenu votre collection d'objets (requête.List ()) vous devrez peut-être recharger les objets pour en peupler un ou plusieurs. Beaucoup de mappings à un seront remplacés pour les charges paresseuses.

 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>());
    }

Code j'avais l'habitude de charger une ou plusieurs relations... T est le type d'entité.

for (int i = 0; i < resp.Data.Count; i++)
        {
            resp.Data[i] = session.Load<T>(GetInstanceIdValue(resp.Data[i]));
        }
0
répondu longday 2011-08-12 13:45:48