Question

En utilisant NHibernate J'essaie d'obtenir obtenir une liste de B où une propriété IList de B contient une instance spécifique de A.

Le code suivant devrait, espérons expliquer plus clairement la situation:

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 seconde requête échoue avec l'erreur: could not resolve property: a_As of: B

Comment puis-je effectuer cette requête à l'aide des instances d'objet sans utiliser explicitement les propriétés d'ID comme dans la première requête ci-dessus.

Edit: Pour développer, j'ai une classe générique qui effectue des opérations NHibernate avec mes entités. Je viens de créer une méthode « IsReferenced » qui commence comme suit:

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

J'utilise typeProps pour construire un multicritère pour trouver toutes les entités où cette référence celle spécifiée. Il fonctionne très bien pour les propriétés normales, mais les propriétés de collecte me donne la douleur. Je ne sais pas comment s'y ajouter la restriction dans les critères.

Était-ce utile?

La solution 2

Je l'ai trouvé une solution qui semble fonctionner pour toutes mes entités actuelles, sur la base de deux éléments d'information que j'ai survolaient tout en travaillant sur ce point:

  • Les états de documentation qui en minuscules « id » est une propriété spéciale qui peut être utilisé pour faire référence à l'identifiant d'une entité.
  • ISession expose une méthode publique object ISession.GetIdentifier(object obj)

Ces deux gourmandises nous ont laissé faire quelque chose comme ceci:

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

Et les regards de méthode IsReferenced résultant comme ceci:

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

Si seulement mon cerveau avait donné le coup avant que je mets la moitié de ma réputation d'une prime ...

Autres conseils

Ce type de requête est mieux servie par HQL que les critères:

session.CreateQuery("from B b where :a in elements(b.As)")
       .SetParameter("a", a1)
       .List();
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top