Domanda Come ottenere un risultato distinto con l'API nHibernate e QueryOver?


Ho questo metodo di repository

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

Fornisci una stringa di ricerca di testo libero e un elenco di tag. Il problema è che se un messaggio ha più di un tag è elencato tempi duplicati. Voglio un risultato distinto basato sull'entità Messaggio. Ho guardato

Projections.Distinct

Ma richiede una lista di proprietà alla domanda distinta. Questo messaggio è la mia radice di entità in cui c'è un modo per ottenere questo comportamento senza fornire tutte le proprietà dell'entità?

Grazie in anticipo, Anders


44
2018-01-06 14:01


origine


risposte:


Se si utilizza l'API ICriteria, è necessario:

.SetResultTransformer(new DistinctEntityRootTransformer())

Se si utilizza l'API QueryOver, è necessario:

.TransformUsing(Transformers.DistinctRootEntity)

Ma attenzione, tutto ciò avviene sul lato client, quindi tutte le righe duplicate vengono ancora tirate.


61
2018-01-06 14:58



Prova qualcosa di simile

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
2018-02-24 00:33



Puoi utilizzare SelectList e GroupBy, ad esempio:

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

Dovrebbe funzionare e produrre lo stesso piano di query come distinto.

Se hai bisogno di più elementi nel gruppo, fai qualcosa come:

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

11
2018-04-06 15:49



Ho recentemente creato un metodo per applicare select distinto basato su un tipo di oggetto mappato. Lo applica a un oggetto IQueryOver (proprietà della classe). Il metodo ha anche accesso alla configurazione di nhibernate. Potresti aggiungerli come parametri del metodo. Ha bisogno di lavoro per la produzione, ma il metodo sta funzionando benissimo in dev, l'ha usato solo per un'entità finora.

Questo metodo è stato creato perché sto provando a visualizzare i miei dati a livello di server e un trasformatore di risultati distinto non funzionerebbe.

Dopo aver ottenuto la raccolta oggetti (query.List ()) potrebbe essere necessario ricaricare gli oggetti per popolare uno a molti oggetti figlio. Molte mappature verranno proxy per carichi pigri.

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

Il codice che ho usato per caricare da una a molte relazioni ... T è il tipo di entità.

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

0
2017-08-12 13:45