动态Linq到Xml的示例 [英] Dynamic Linq to Xml example

查看:84
本文介绍了动态Linq到Xml的示例的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我需要一个有关如何将System.Linq.Dynamic与Xml一起使用的基本示例.这是我要转换为动态Linq的有效语句:

I need a basic example on how to use System.Linq.Dynamic with Xml. Here’s a functioning statement I want to convert to dynamic Linq:

XElement e = XElement.Load(new XmlNodeReader(XmlDoc));
var results =
    from r in e.Elements("TABLES").Descendants("AGREEMENT")
    where (string)r.Element("AGRMNT_TYPE_CODE") == "ISDA"
    select r.Element("DATE_SIGNED");

foreach (var x in results)
{
    result = x.Value;
    break;
}

这是我使用的方法:

string whereClause = "(\"AGRMNT_TYPE_CODE\") == \"ISDA\"";
string selectClause = "(\"DATE_SIGNED\")";
var results = e.Elements("TABLES").Descendants<XElement>("AGREEMENT").
                AsQueryable<XElement>().
                Where<XElement>(whereClause).
                Select(selectClause); 

foreach (var x in results)
{
    result = (string)x;
    break;
}

它执行没有错误,但是没有产生结果.

It executes without error but produces no results.

我正在尝试编写类似于在

I am trying to code this similar to the canonical example found at http://weblogs.asp.net/scottgu/archive/2008/01/07/dynamic-linq-part-1-using-the-linq-dynamic-query-library.aspx where a constructed string is applied against a database:

Dim Northwind as New NorthwindDataContext
Dim query = Northwind.Products _
                     .Where("CategoryID=2 and UnitPrice>3") _
                     .OrderBy("SupplierId")
GridView1.Datasource = query
GridView1.Databind()

我想念什么?

我终于开始工作了.我放弃了原来的方法,因为到目前为止,我还不确定它是否打算与Xml一起使用.我几乎没有在任何地方张贴任何文章来反对该声明.相反,我使用了乔恩·斯基特(Jon Skeet)对

I finally got it working. I abandoned my original approach because as of now I'm not convinced it was even intended for use with Xml. I've seen little posted anywhere to argue against that statement. Instead, I used Jon Skeet's response to this question as the basis for my answer:

XElement e = XElement.Load(new XmlNodeReader(XmlDoc));

List<Func<XElement, bool>> exps = new List<Func<XElement, bool>> { };
exps.Add(GetXmlQueryExprEqual("AGRMNT_TYPE_CODE", "ISDA"));
exps.Add(GetXmlQueryExprNotEqual("WHO_SENDS_CONTRACT_IND", "X"));

List<ConditionalOperatorType> condOps = new List<ConditionalOperatorType> { };
condOps.Add(ConditionalOperatorType.And);
condOps.Add(ConditionalOperatorType.And);

//Hard-coded test value of the select field Id will be resolved programatically in the
//final version, as will the preceding literal constants.
var results = GetValueFromXml(171, e, exps, condOps);

foreach (var x in results)
{
    result = x.Value;
break;
}

return result;
...
public static Func<XElement, bool> GetXmlQueryExprEqual(string element, string compare)
{
    try
    {
        Expression<Func<XElement, bool>> expressExp = a => (string)a.Element(element) == compare;
        Func<XElement, bool> express = expressExp.Compile();
        return express;
    }   
    catch (Exception e)     
    {
        return null;
    }
}

public static Func<XElement, bool> GetXmlQueryExprNotEqual(string element, string compare)
{
    try
    {
        Expression<Func<XElement, bool>> expressExp = a => (string)a.Element(element) != compare;
        Func<XElement, bool> express = expressExp.Compile();
        return express;
    }
    catch (Exception e)
    {
        return null;
    }
}

private IEnumerable<XElement> GetValueFromXml(int selectFieldId, XElement elem, 
    List<Func<XElement, bool>> predList, List<ConditionalOperatorType> condOpsList)
{
    try
    {
        string fieldName = DocMast.GetFieldName(selectFieldId);
        string xmlPathRoot = DocMast.Fields[true, selectFieldId].XmlPathRoot;
        string xmlPathParent = DocMast.Fields[true, selectFieldId].XmlPathParent;
        IEnumerable<XElement> results = null;
        ConditionalOperatorType condOp = ConditionalOperatorType.None; 

    switch (predList.Count)
    {
        case (1):
          results =
            from r in elem.Elements(xmlPathRoot).Descendants(xmlPathParent)
            where (predList[0](r))
            select r.Element(fieldName);
          break;
        case (2):
            CondOp = (ConditionalOperatorType)condOpsList[0];
            switch (condOp)
            {  
                case (ConditionalOperatorType.And):
                    results =
                    from r in elem.Elements(xmlPathRoot).Descendants(xmlPathParent)
                    where (predList[0](r) && predList[1](r))
                    select r.Element(fieldName);
                    break;
                case (ConditionalOperatorType.Or):
                    results =
                    from r in elem.Elements(xmlPathRoot).Descendants(xmlPathParent)
                    where (predList[0](r) || predList[1](r))
                    select r.Element(fieldName);
                    break;
                default:
                    break;
            }
            break;
        default:
            break;
    }
    return results;
}
    catch (Exception e)
    {
        return null;
    }
}

但是,这种方法显然远非完美.

This approach, however, is obviously far from perfect.

  1. 我有单独的函数来解析和编译表达式-只是为了合并不同的条件运算符.更糟糕的是,我将添加更多内容以支持其他逻辑运算符和数值;
  2. GetValueFromXml例程笨拙,当我添加更多参数时,将不得不增加其他情况.

任何想法或建议都将不胜感激.

Any ideas or suggestions would be greatly appreciated.

推荐答案

这里确实有两个问题,您的where子句:

There are two issues here really, your where clause:

("AGMNT_TYPE_CODE") == "ISDA"

...当然将计算为false,因为它们都是字符串.

... will of course evaluate to false, because they are both strings.

第二个问题是ExpressionParser的范围受到限制,它只能对一组预定义类型进行比较.您需要重新编译动态库,并允许一些其他类型(可以通过修改ExpressionParser类型的predefinedTypes静态字段来做到这一点),或者删除对预定义类型的检查(这是我之前所做的) :

The second issue is that the ExpressionParser is limited in scope, it can only do comparisons on a set of predefined types. You need to recompile the Dynamic library and either allow some additional types (you can do this by modifying the predefinedTypes static field of the ExpressionParser type) or, remove the check for predefined types (which is what I have done previously):

Expression ParseMemberAccess(Type type, Expression instance)
{
  // ...
        switch (FindMethod(type, id, instance == null, args, out mb))
        {
            case 0:
                throw ParseError(errorPos, Res.NoApplicableMethod,
                    id, GetTypeName(type));
            case 1:
                MethodInfo method = (MethodInfo)mb;
                //if (!IsPredefinedType(method.DeclaringType)) // Comment out this line, and the next.
                    //throw ParseError(errorPos, Res.MethodsAreInaccessible, GetTypeName(method.DeclaringType));
                if (method.ReturnType == typeof(void))
                    throw ParseError(errorPos, Res.MethodIsVoid,
                        id, GetTypeName(method.DeclaringType));
                return Expression.Call(instance, (MethodInfo)method, args);
            default:
                throw ParseError(errorPos, Res.AmbiguousMethodInvocation,
                    id, GetTypeName(type));
        }
  // ...
}

我注释掉的那些行是检查预定义类型的地方.

Those lines I have commented out are where the check for predefined types are done.

进行了此更改后,您需要更新查询(请记住,ExpressionParser会生成已编译的表达式,因此仅使用"(\"AGRMNT_TYPE_CODE\") == \"ISDA\""不会起作用.您将需要以下内容:

Once you have made that change, you need to update your query (remember, ExpressionParser builds expressions that are compiled, so simply using "(\"AGRMNT_TYPE_CODE\") == \"ISDA\"" wont work. You would need something like:

string where = "Element(\"AGMNT_TYPE_CODE\").Value == \"ISDA\"";

这篇关于动态Linq到Xml的示例的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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