ICriteria,添加对集合内容的限制

发布于 2024-09-12 19:34:20 字数 2434 浏览 5 评论 0原文

使用 NHibernate 我试图获取 B 的列表,其中 B 的 IList 属性包含 A 的特定实例。

以下代码有望更清楚地解释这种情况:

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

第二个查询失败并出现错误:could not解析属性:a_As of:B

如何使用对象实例执行此查询,而不像上面的第一个查询那样显式使用 ID 属性。

编辑: 为了扩展这一点,我有一个通用类,它对我的​​实体执行 NHibernate 操作。我刚刚创建了一个“IsReferenced”方法,其开头如下:

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

我使用 typeProps 构建 MultiCriteria 来查找引用指定实体的任何实体。它对于普通属性工作得很好,但集合属性却让我感到悲伤。我不知道如何将限制添加到标准中。

Using NHibernate I'm trying to get obtain a list of B's where an IList property of B contains a specific instance of A.

The following code should hopefully explain the situation more clearly:

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

The second query fails with the error: could not resolve property: a_As of: B

How can I perform this query using object instances without explicitly using the ID properties like in the first query above.

Edit:
To expand on this, I have a generic class which performs NHibernate operations with my entities. I've just created an "IsReferenced" method that begins as follows:

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

I'm using typeProps to build a MultiCriteria to find any entities anywhere that reference the one specified. It works fine for normal properties, but collection properties are giving me grief. I'm not sure how to go about adding the restriction into the criteria.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

故事与诗 2024-09-19 19:34:20

HQL 比 Criteria 更好地服务于这种类型的查询:

session.CreateQuery("from B b where :a in elements(b.As)")
       .SetParameter("a", a1)
       .List();

This type of query is better served by HQL than Criteria:

session.CreateQuery("from B b where :a in elements(b.As)")
       .SetParameter("a", a1)
       .List();
尽揽少女心 2024-09-19 19:34:20

我找到了一个似乎适用于我当前所有实体的解决方案,基于我在处理此问题时浏览过的两条信息:

  • 文档指出小写“id”是一个特殊属性,可以用于引用任何实体的 id。
  • ISession 暴露了一个公共方法 object ISession.GetIdentifier(object obj)

这两个花絮让我们可以做这样的事情:

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

结果 IsReferenced 方法看起来像这样:

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

如果我的大脑在我提出之前就已经启动了就好了我的声誉的一半作为赏金......

I've found a solution that appears to work for all of my current entities, based on two pieces of information I've skimmed over whilst working on this:

  • The documentation states that lower-case 'id' is a special property that can be used to reference the id of any entity.
  • ISession exposes a public method object ISession.GetIdentifier(object obj)

These two titbits let us do something like this:

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

And the resulting IsReferenced method looks like this:

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

If only my brain had kicked in before I put up half of my reputation as a bounty...

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文