使用linq查询映射为地图的集合(IDictionary) [英] Querying with linq a collection mapped as a map (IDictionary)

查看:251
本文介绍了使用linq查询映射为地图的集合(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?

推荐答案

作为练习,我决定扩展以支持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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆