使用linq查询映射为地图的集合(IDictionary) [英] Querying with linq a collection mapped as a map (IDictionary)
问题描述
使用NHibernate,我有一组映射为字典的实体.
例如,类A有一个映射为IDictionary<int, B>
的名为Children
的B的集合. B具有属性Name
.
Using NHibernate, I have a collection of entities mapped as a dictionary.
By example, class A has a collection of B named Children
, mapped as a IDictionary<int, B>
. B has a property Name
.
使用HQL
可以非常简单地根据与孩子的字典索引无关的B个孩子的某些条件来查询A类:
Querying class A base on some condition on B children unrelated to their dictionary indexation is quite straightforward with HQL
:
from A where A.Children.Name = 'aName'
完美运行.
但是要通过LINQ
实现相同的功能,就不那么简单了:
But for achieving the same with LINQ
, this is quite less straightforward:
IQueryable<A> query = ...;
query.Where(a => a.Children.Values.Any(b => b.Name == "aName"));
失败,显示消息could not resolve property: Values of: B
所以是的,我们可以欺骗它
So yes, we can trick it through
IQueryable<A> query = ...;
query.Where(a => ((ICollection<B>)a.Children).Any(b => b.Name == "aName"));
那行得通,并产生了预期的结果.
That does works and yields expected results.
但这在我看来有点丑陋,我宁愿不必执行无效"强制转换(至少在linq2NH上下文之外为无效").
But this looks to me a bit ugly, I would rather not have to do that 'invalid' cast (at least 'invalid' outside of linq2NH context).
是否有更好的方法来查询使用Linq和NHibernate映射为IDictionary的子级集合?
Is there any better way for querying a children collection mapped as a IDictionary with Linq and NHibernate?
推荐答案
作为练习,我决定扩展 linq-to-nhibernate 以支持Values
.这样可以解决这个问题.
As an exercise, I have decided to extend linq-to-nhibernate for supporting Values
. This give a solution to the question.
有很多扩展linq2NH的方法,请参见此列表.在这里,我需要添加一个新的生成器",就像我对另一个问题的答案一样.
There is many ways for extending linq2NH, see this list. Here, I need to add a new 'generator', as in my answer to another question.
首先,您需要一堆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;
然后,为Values
实施HQL转换.
Then, implement HQL translation for Values
.
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();
}
}
使用生成器扩展默认的linq2NH注册表:
Extend the default linq2NH registry with your generator:
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;
}
}
我很难弄清楚如何注册一个通用类属性生成器.在许多情况下都内置支持,包括通过GenericDictionaryRuntimeMethodHqlGeneratorBase
的派生类提供的通用词典方法,但显然不支持通用词典属性.因此,我最终在TryGetGenerator
方法的属性中对其进行了硬编码".
I had quite a hard time figuring out how to register a generic class property generator. There is built-in support for many cases including generic dictionaries methods through derived class of GenericDictionaryRuntimeMethodHqlGeneratorBase
, but apparently no support for generic dictionaries properties. So I have ended up 'hard coding' it in the TryGetGenerator
method for properties.
现在将NH配置为使用新注册表.使用hibernate.cfg.xml,在session-factory
节点下添加以下property
节点:
Now configure NH to use your new registry. With hibernate.cfg.xml, add following property
node under session-factory
node:
<property name="linqtohql.generatorsregistry">YourNameSpace.ExtendedLinqToHqlGeneratorsRegistry, YourAssemblyName</property>
现在这行得通:
IQueryable<A> query = ...;
query.Where(a => a.Children.Values.Any(b => b.Name == "aName"));
免责声明:仅作为练习,我什至没有在我的实际代码中提交.我目前在映射中不再使用任何map
.我添加了一些只是为了测试,然后撤消了所有操作.
Disclaimer: done only as an exercise, I have not even committed that in my actual code. I am currently no more using any map
in my mappings. I have added some just for testing, then I have undone all.
这篇关于使用linq查询映射为地图的集合(IDictionary)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!