使用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的子集合?
答案 0 :(得分:0)
作为练习,我决定延长linq-to-nhibernate以支持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"));
。我添加了一些仅用于测试,然后我撤消了所有。