Динамический пример Linq to Xml - PullRequest
1 голос
/ 28 сентября 2011

Мне нужен базовый пример того, как использовать 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.Я видел мало опубликованных где-либо, чтобы утверждать против этого заявления.Вместо этого я использовал ответ Джона Скита на этот вопрос в качестве основы для моего ответа:

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 Ответ

1 голос
/ 28 сентября 2011

Здесь на самом деле есть две проблемы, условие where:

("AGMNT_TYPE_CODE") == "ISDA"

... конечно, оценивается как false, потому что они обе строки.

Вторая проблемаявляется то, что ExpressionParser ограничен в области, он может делать сравнения только с набором предопределенных типов.Вам нужно перекомпилировать динамическую библиотеку и либо разрешить некоторые дополнительные типы (вы можете сделать это, изменив статическое поле predefinedTypes типа ExpressionParser), либо удалить проверку для предопределенных типов (что я и делал ранее):

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\"";
...