【问题标题】:Dynamic Linq to Xml example动态 Linq to XML 示例
【发布时间】:2011-11-27 00:23:56
【问题描述】:

我需要一个关于如何将 System.Linq.Dynamic 与 Xml 一起使用的基本示例。这是我想转换为动态 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;
}

它执行没有错误,但没有产生任何结果。

我正在尝试编写类似于 http://weblogs.asp.net/scottgu/archive/2008/01/07/dynamic-linq-part-1-using-the-linq-dynamic-query-library.aspx 的规范示例的代码,其中将构造的字符串应用于数据库:

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 对this question 的回复作为我回答的基础:

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;
    }
}

然而,这种方法显然远非完美。

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

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

【问题讨论】:

    标签: c# linq-to-xml dynamic-linq


    【解决方案1】:

    这里真的有两个问题,你的 where 子句:

    ("AGMNT_TYPE_CODE") == "ISDA"
    

    ...当然会评估为false,因为它们都是字符串。

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

    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));
            }
      // ...
    }
    

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

    一旦您进行了更改,您需要更新您的查询(请记住,ExpressionParser 构建已编译的表达式,因此仅使用 "(\"AGRMNT_TYPE_CODE\") == \"ISDA\"" 将不起作用。您需要类似以下内容:

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

    【讨论】:

    • 我之前试过:string where = "Element(\"AGMNT_TYPE_CODE\").Value == \"ISDA\"";它会产生一个 System.Linq.Dynamic.ParseException:“类型 'XElement' 中不存在适用的方法 'Element'”。似乎这种通用方法应该有效。 (请参阅我对上面原始帖子的补充)我确信这只是语法正确的问题。在发布此帖子之前,我一直在反复试验和错误,但没有成功。
    猜你喜欢
    • 2020-01-09
    • 1970-01-01
    • 1970-01-01
    • 2012-04-18
    • 1970-01-01
    • 2011-01-02
    • 1970-01-01
    • 1970-01-01
    • 2011-04-10
    相关资源
    最近更新 更多