使用linq查询映射为映射的集合(IDictionary)

时间:2015-12-15 11:15:36

标签: nhibernate linq-to-nhibernate

使用NHibernate,我有一组映射为字典的实体。 例如,A类有一个名为Children的B集合,映射为IDictionary<int, B>。 B有一个属性Name

使用HQL非常简单地根据与字典索引无关的B个孩子的某些条件查询A类:

from A where A.Children.Name = 'aName'

完美无瑕地跑。

但是为了与LINQ实现相同,这是不那么直截了当的:

IQueryable<A> query = ...;
query.Where(a => a.Children.Values.Any(b => b.Name == "aName"));

信息could not resolve property: Values of: B

失败

是的,我们可以通过

来欺骗它
IQueryable<A> query = ...;
query.Where(a => ((ICollection<B>)a.Children).Any(b => b.Name == "aName"));

这确实有效,并产生了预期的结果。

但这对我来说有点难看,我宁愿不要做'无效'演员(至少在linq2NH背景之外'无效')。

有没有更好的方法来查询使用Linq和NHibernate映射为IDictionary的子集合?

1 个答案:

答案 0 :(得分:0)

作为练习,我决定延长以支持non-const version struct MyObject * const version struct MyObject const * 。这给出了问题的解决方案。

有很多方法可以扩展linq2NH,请参阅此list。在这里,我需要添加一个新的生成器&#39;,就像我的answer to another question一样。

首先,你需要一堆Values

using

然后,为using System.Reflection; using System.Linq.Expressions; using System.Collections; using System.Collections.Generic; using NHibernate.Hql.Ast; using NHibernate.Linq.Visitors; using NHibernate.Linq.Functions; 实现HQL翻译。

Values

使用您的生成器扩展默认的linq2NH注册表:

public class DictionaryValuesGenerator : BaseHqlGeneratorForProperty
{
    public override HqlTreeNode BuildHql(
        MemberInfo member, Expression expression,
        HqlTreeBuilder treeBuilder, IHqlExpressionVisitor visitor)
    {
        // Just have to skip Values, HQL does not need it.
        return visitor.Visit(expression).AsExpression();
    }
}

我很难搞清楚如何注册泛型类属性生成器。对于许多情况都有内置支持,包括通过派生类public class ExtendedLinqToHqlGeneratorsRegistry : DefaultLinqToHqlGeneratorsRegistry { public override bool TryGetGenerator(MemberInfo property, out IHqlGeneratorForProperty generator) { if (base.TryGetGenerator(property, out generator)) return true; return TryGetDictionaryValuesGenerator(property, out generator); } private DictionaryValuesGenerator _dictionaryValuesGenerator = new DictionaryValuesGenerator(); protected bool TryGetDictionaryValuesGenerator(MemberInfo property, out IHqlGeneratorForProperty generator) { generator = null; if (property == null || property.Name != "Values") return false; var declaringType = property.DeclaringType; if (declaringType.IsGenericType) { var genericType = declaringType.GetGenericTypeDefinition(); if (genericType != typeof(IDictionary<,>)) return false; generator = _dictionaryValuesGenerator; return true; } if (declaringType != typeof(IDictionary)) return false; generator = _dictionaryValuesGenerator; return true; } } 的通用字典方法,但显然不支持通用字典属性。所以我最终还是硬编码了#39;它在GenericDictionaryRuntimeMethodHqlGeneratorBase属性方法中。

现在配置NH以使用新的注册表。使用hibernate.cfg.xml,在TryGetGenerator node:

下添加以下property节点
session-factory

现在这确实有效:

<property name="linqtohql.generatorsregistry">YourNameSpace.ExtendedLinqToHqlGeneratorsRegistry, YourAssemblyName</property>

免责声明:仅作为练习完成,我甚至没有在我的实际代码中提交。我目前在映射中不再使用任何IQueryable<A> query = ...; query.Where(a => a.Children.Values.Any(b => b.Name == "aName")); 。我添加了一些仅用于测试,然后我撤消了所有。