2011-09-28 51 views
1

我需要一个关于如何在Xml中使用System.Linq.Dynamic的基本示例。这里是一个有效的声明我要转换为动态的LINQ:动态Linq到Xml示例

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。我没有看到任何地方发表反对声明的观点。相反,我用乔恩斯基特的回应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例程非常笨重,因为我添加了更多参数,所以必须增加额外的情况。

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

回答

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\""; 
+0

我以前试过: string where =“Element(\”AGMNT_TYPE_CODE \“)。Value == \”ISDA \“”;并且它会产生一个System.Linq.Dynamic.ParseException: “XElement类型中不存在可应用的方法'Element' “”。这似乎是一般的方法应该工作。 (请参阅我上面原文的补充)我确定这只是一个正确的语法问题。在发布此消息之前,我一再尝试反复尝试和失败。 –