自定义linq提供程序,用于在XML字段中搜索具有特定值的xml属性

时间:2016-06-30 09:01:09

标签: sql xml nhibernate hql custom-linq-providers

我通过NHibernate与我交互的一些数据库表包含一个具有以下结构的XML字段:

<L xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
   <I>
     <C>
       <N>Attribute1</N>
       <V>a_value</V>
     </C>
     <C>
       <N>Attribute2</N>
       <V>123</V>
     </C>
  </I>
</L>

基本上,每个“C”标签都包含一个属性,其名称包含在标签“N”中,而它的值在标签“V”中。

我想要实现的是能够在我的查询中编写这种LINQ语法:

..
.Where(m=>m.XMLField(attribute_name, attribute_value))
..

这样我就可以获取特定表的实体,其XML字段包含名为“ attribute_name ”的属性,其字符串值由“ attribute_value ”指定

就这么简单,XML结构总是这样,我只需要查询具有特定值的单个属性。

进行搜索我发现有一种特定的技术可以实现自定义LINQ提供程序:

  1. http://www.primordialcode.com/blog/post/nhibernate-3-extending-linq-provider-fix-notsupportedexception
  2. http://fabiomaulo.blogspot.it/2010/07/nhibernate-linq-provider-extension.html
  3. How would I alter the SQL that Linq-to-Nhibernate generates for specific columns?
  4. 不幸的是,我无法找到关于如何使用treebuilder的结构化文档,因此,目前这就是我所拥有的:

    我已经找到了正确的HQL来执行这样的任务:

    where [some other statements] and XML_COLUMN_NAME.exist('/L/I/C[N=\"{0}\" and V=\"{1}\"]') = 1","attribute_name", "attribute_value");
    

    我要在LINQ查询中调用的方法:

    public static bool AttributeExists(this string xmlColumnName, string attributeName, string attributeValue)
    {
       throw new NotSupportedException();
    }
    

    与HQL的集成部分:

    public class XMLAttributeGenerator : BaseHqlGeneratorForMethod
    {
       public XMLAttributeGenerator()
       {
           SupportedMethods = new[] { ReflectionHelper.GetMethodDefinition(() => TestClass.AttributeExists(null, null, null)) };
       }
    
        public override HqlTreeNode BuildHql(MethodInfo method, Expression targetObject,
            ReadOnlyCollection<Expression> arguments, HqlTreeBuilder treeBuilder, IHqlExpressionVisitor visitor)
        {
            return treeBuilder.Exists(???);
        }
    }
    

    正如您所看到的,我仍然没有弄清楚如何正确使用treebuilder和visitor对象来复制上面表达的HQL语法。可能有人帮我解决这个问题,或者至少指出一些关于treebuilder使用的基本文档?感谢

1 个答案:

答案 0 :(得分:0)

这就是我实现预期结果的方式:

MOCK METHOD

public static class MockLINQMethods
    {
        public static bool XMLContains(this MyCustomNHType input, string element, string value)
        {
            throw new NotImplementedException();
        }
}

CUSTOM GENERATOR

public class CustomLinqToHqlGeneratorsRegistry : DefaultLinqToHqlGeneratorsRegistry
    {
        public CustomLinqToHqlGeneratorsRegistry()
            : base()
        {
            RegisterGenerator(ReflectionHelper.GetMethod(() => MockLINQMethods.XMLContains((MyCustomNHType) null, null, null)),
                              new LINQtoHQLGenerators.MyCustomNHTypeXMLContainsGenerator());
        }
}

public class MyCustomNHTypeXMLContainsGenerator : BaseHqlGeneratorForMethod
        {
            public MyCustomNHTypeXMLContainsGenerator()
            {
                SupportedMethods = new[] { ReflectionHelper.GetMethod(() => MockLINQMethods.XMLContains((MyCustomNHType) null, null, null)) };
            }

            public override HqlTreeNode BuildHql(MethodInfo method, Expression targetObject,
                ReadOnlyCollection<Expression> arguments, HqlTreeBuilder treeBuilder, IHqlExpressionVisitor visitor)
            {
                var column_name = visitor.Visit(arguments[0]).AsExpression();
                var element_name = visitor.Visit(arguments[1]).AsExpression();
                var value = visitor.Visit(arguments[2]).AsExpression();

                return treeBuilder.BooleanMethodCall("_ExistInMyCustomNHType", new [] { column_name, element_name, value});
            }
        }

CUSTOM FUNCTION

public class CustomLinqToHqlMsSql2008Dialect : MsSql2008Dialect
    {
        public CustomLinqToHqlMsSql2008Dialect()
        {
            RegisterFunction("_ExistInMyCustomNHType", 
                new SQLFunctionTemplate(NHibernateUtil.Boolean, 
                    "?1.exist('/L/I/C[N=sql:variable(\"?2\") and V=sql:variable(\"?3\")]') = 1"));
        }
    }

最后,将定制发生器与会议中的定制功能联系起来

    factory = Fluently.Configure()
.Database(MsSqlConfiguration.MsSql2008
    .ConnectionString(connectionString)
        .Dialect<CustomLinqToHqlMsSql2008Dialect>())
        ..
        .ExposeConfiguration(c =>
            {    
            ..         
            c.SetProperty("linqtohql.generatorsregistry", "APP.MyNAMESPACE.CustomLinqToHqlGeneratorsRegistry, APP.MyNAMESPACE");
            ..                        
            })                    
        .BuildSessionFactory();