Domanda

Utilizzando NHibernate Sto cercando di ottenere ottenere un elenco di B dove una proprietà IList di B contiene una specifica istanza di A.

Il seguente codice dovrebbe auspicabilmente spiegare la situazione in modo più chiaro:

public void test()
{
    A a1 = new A();
    A a2 = new A();
    B b1 = new B();
    b1.As = new List<A> { a1 };
    // ...database save cut...

    using (ISession session = SessionFactory.OpenSession())
    {
        var result1 = session.CreateCriteria<B>()
            .CreateAlias("As", "a_As")
            .Add(Restrictions.Eq("a_As.ID", a1.ID))
            .List();

        var result2 = session.CreateCriteria<B>()
            .CreateAlias("As", "a_As")
            .Add(Restrictions.Eq("a_As", a1))
            .List();
    }
}

class A
{
    public virtual int ID { get; set; }
}

class B
{
    public virtual IList<A> As { get;set;}
}

La seconda query fallisce con l'errore: could not resolve property: a_As of: B

Come posso eseguire questa query utilizzando istanze di oggetti senza usare esplicitamente le proprietà ID, come nella prima query sopra.

Modifica: Per espandere su questo, ho una classe generica che esegue operazioni NHibernate con i miei soggetti. Ho appena creato un metodo di "IsReferenced" che inizia come segue:

public bool IsReferenced(T entity)
{
    // Get the types (and their properties) that reference the type in question
    var typeProps = from type in typeof(T).Assembly.GetTypes()
                    let props = type.GetProperties().Where(p => p.PropertyType == typeof(T)).Select(p => p.Name)
                    let collections = type.GetProperties().Where(p => typeof(IEnumerable<T>).IsAssignableFrom(p.PropertyType)).Select(p => p.Name)
                    where type.IsClass && !type.IsAbstract && ((props != null && props.Count() > 0) || (collections != null && collections.Count() > 0))
                    select new { EntityType = type, Properties = props, Collections = collections };

    var multiCriteria = NHibernateSession.CreateMultiCriteria();
    foreach (var typeProp in typeProps)
    {
        var criteria = NHibernateSession.CreateCriteria(typeProp.EntityType);
        var disjunction = new Disjunction();
        foreach (var propName in typeProp.Properties)
        {
            disjunction.Add(Restrictions.Eq(propName, entity));
        }
        foreach (var collectionName in typeProp.Collections)
        {
            throw new NotImplementedException();
        }
        criteria.Add(disjunction);
        multiCriteria.Add(criteria);
    }
.....
}

sto usando typeProps per costruire un multicriterio per trovare qualsiasi entità da nessuna parte che il riferimento quello specificato. Funziona bene per le proprietà normali, ma le proprietà di raccolta stanno dando me dolore. Non sono sicuro di come fare per aggiungere la restrizione nei criteri.

È stato utile?

Soluzione 2

ho trovato una soluzione che sembra funzionare per tutti i miei soggetti in corso, sulla base di due pezzi di informazioni che ho scremato sopra mentre si lavora su questo:

  • Gli stati di documentazione che minuscole 'id' è una proprietà speciale che può essere utilizzato per fare riferimento l'id di qualsiasi entità.
  • ISession espone un metodo pubblico object ISession.GetIdentifier(object obj)

Queste due leccornie ci permettono di fare qualcosa di simile:

var result2 = session.CreateCriteria<B>()
        .CreateAlias("As", "a_As")
        .Add(Restrictions.Eq("a_As.id", session.GetIdentifier(a1)))
        .List();

e il conseguente metodo sembra IsReferenced come questo:

    public bool IsReferenced(T entity)
    {
        // Get the types (and their properties) that reference the type in question
        var typeProps = from type in typeof(T).Assembly.GetTypes()
                        let props = type.GetProperties().Where(p => p.PropertyType == typeof(T)).Select(p => p.Name)
                        let collections = type.GetProperties().Where(p => typeof(IEnumerable<T>).IsAssignableFrom(p.PropertyType)).Select(p => p.Name)
                        where type.IsClass && !type.IsAbstract && ((props != null && props.Count() > 0) || (collections != null && collections.Count() > 0))
                        select new { EntityType = type, Properties = props, Collections = collections };

        var multiCriteria = NHibernateSession.CreateMultiCriteria();

        // Create a big or query to test whether any of the properties are, or contain, the entity parameter
        foreach (var typeProp in typeProps)
        {
            var criteria = NHibernateSession.CreateCriteria(typeProp.EntityType);
            var disjunction = new Disjunction();
            foreach (var propName in typeProp.Properties)
            {
                disjunction.Add(Restrictions.Eq(propName, entity));
            }
            foreach (var collectionName in typeProp.Collections)
            {
                string alias = string.Format("a_{0}", collectionName);
                criteria.CreateAlias(collectionName, alias, NHibernate.SqlCommand.JoinType.LeftOuterJoin);

                disjunction.Add(Restrictions.Eq(string.Format("{0}.id", alias), NHibernateSession.GetIdentifier(entity)));
            }
            criteria.Add(disjunction);
            multiCriteria.Add(criteria);
        }

        var results = multiCriteria.List();

        bool hasReferences = false;
        foreach (var resultSet in results)
        {
            if ((resultSet as System.Collections.IList).Count != 0)
            {
                hasReferences = true;
                break;
            }
        }
        return hasReferences;
    }

Se solo il mio cervello aveva preso a calci in prima di mettere la metà della mia reputazione di una taglia ...

Altri suggerimenti

Questo tipo di query è meglio servita da HQL di criteri:

session.CreateQuery("from B b where :a in elements(b.As)")
       .SetParameter("a", a1)
       .List();
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top