48 votes

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 en texte libre et une liste de balises. Le problème est que si un message a plus d'une étiquette, il est listé en double. Je veux un résultat distinct basé sur l'entité Message. J'ai consulté

Projections.Distinct

Mais il faut une liste de propriétés pour répondre à la question distincte. Ce message est mon entité. Il y a peut-être un moyen d'obtenir ce comportement sans fournir toutes les propriétés de l'entité ?

Merci d'avance, Anders

67voto

Sly Points 8146

Si vous utilisez l'API ICriteria, vous devez :

.SetResultTransformer(new DistinctEntityRootTransformer())

Si vous utilisez l'API QueryOver, vous avez besoin :

.TransformUsing(Transformers.DistinctRootEntity)

Mais attention, tout cela se passe du côté du client, de sorte que toutes les lignes dupliquées sont toujours extraites.

27voto

Craig Points 15049

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

13voto

Hainesy Points 1958

Vous pouvez utiliser SelectList et GroupBy, par exemple :

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

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

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

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

1voto

longday Points 789

J'ai récemment créé une méthode pour appliquer une sélection distincte basée sur un type d'objet mappé. Elle l'applique à un objet IQueryOver (propriété de la classe). La méthode a également accès à la configuration nhibernate. Vous pourriez les ajouter en tant que paramètres de la méthode. Il y a du travail à faire pour la production, mais la méthode fonctionne très bien en développement, elle n'a été utilisée que pour une seule entité jusqu'à présent.

Cette méthode a été créée parce que j'essaie de paginer mes données au niveau du serveur et qu'un transformateur de résultat distinct ne fonctionnerait pas.

Après avoir obtenu votre collection d'objets (query.List()), il se peut que vous deviez recharger les objets pour remplir les objets enfants. Les mappages de plusieurs à un seront mandatés pour les chargements paresseux.

 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 que j'ai utilisé pour charger des relations one to many... 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]));
        }

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X