2011-09-28 54 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' 「」。這似乎是一般的方法應該工作。 (請參閱我上面原文的補充)我確定這只是一個正確的語法問題。在發佈此消息之前,我一再嘗試反覆嘗試和失敗。 –