Frage

I have this element that I'm working with. I've tried to reduce everything down as much as possible because I feel like it's still going to be a lot of code.

public class HtmlElement
{
    public IDictionary<string, IEnumerable<string>> Attributes { get; set; }
    public IEnumerable<HtmlElement> Descendants { get; set; }
}

My goal is to be able to create this. I want to make it generic so I can pass in other values for "key" and "value" and perform other string transformations/comparisons as well.

Expression<Func<HtmlElement, IEnumerable<HtmlElement>>> test = 
   (HtmlElement ele) => 
        ele.Descendants
           .Where(d => d.Attributes.Any(a => 
                                           a.Key.Equals("key") && 
                                           a.Value.Any(v => v.Equals("value"))));

This displays as this in the DebugView in VS2012 as:

.Lambda
#Lambda1<System.Func`2[HtmlElement,System.Collections.Generic.IEnumerable`1[HtmlElement]]>(HtmlElement $ele) {
    .Call System.Linq.Enumerable.Where(
        $ele.Descendants,
        .Lambda #Lambda2<System.Func`2[HtmlElement,System.Boolean]>) }

.Lambda
#Lambda2<System.Func`2[HtmlElement,System.Boolean]>(HtmlElement $d) {
    .Call System.Linq.Enumerable.Any(
        $d.Attributes,
        .Lambda #Lambda3<System.Func`2[System.Collections.Generic.KeyValuePair`2[System.String,System.Collections.Generic.IEnumerable`1[System.String]],System.Boolean]>) }

.Lambda
#Lambda3<System.Func`2[System.Collections.Generic.KeyValuePair`2[System.String,System.Collections.Generic.IEnumerable`1[System.String]],System.Boolean]>(System.Collections.Generic.KeyValuePair`2[System.String,System.Collections.Generic.IEnumerable`1[System.String]] $a) {
    .Call ($a.Key).Equals("key") && .Call System.Linq.Enumerable.Any(
        $a.Value,
        .Lambda #Lambda4<System.Func`2[System.String,System.Boolean]>) }

.Lambda
#Lambda4<System.Func`2[System.String,System.Boolean]>(System.String $v) {
    .Call $v.Equals("value") }

This is the call that is currently failing...Specifically the assignment to anyExpr with the error:

An exception of type 'System.InvalidOperationException' occurred in System.Core.dll but was not handled in user code Additional information: No generic method 'Any' on type 'System.Linq.Enumerable' is compatible with the supplied type arguments and arguments. No type arguments should be provided if the method is non-generic.

var valueBlock = CreateKeyValueBoolExpression("key", "value", "ToLowerInvariant", "Equals","ToLowerInvariant","Equals");

var valueLamda = Expression.Lambda<Func<KeyValuePair<string, IEnumerable<string>>, bool>>(valueBlock, new[] { Expression.Parameter(typeof(KeyValuePair<string, IEnumerable<string>>)) });

var valueType = typeof(HtmlElement);
var valueParam = Expression.Parameter(valueType, "elem");
var valueProp = Expression.Property(valueParam, "Attributes");
var anyExpre = Expression.Call(typeof(Enumerable), EnumerableMethodName.Any.ToString(), new[] { valueType }, valueProp, valueLamda);

Here are the methods that are being called above:

public static BinaryExpression CreateKeyValueBoolExpression<TKey, TValue>(TKey keyCompareValue, TValue valueCompareValue, string valueTransform, string valueComparison, string keyTransform, string keyComparison)
{
    var keyValueParm = Expression.Parameter(typeof(KeyValuePair<TKey, IEnumerable<TValue>>));

    var keyProp = Expression.Property(keyValueParm, "Key");
    var keyConst = Expression.Constant(keyCompareValue, typeof(TKey));
    var keyBlock = CreateStringCompare(keyTransform, keyComparison, keyProp, keyConst);

    var valueType = typeof(TValue);
    var valueParam = Expression.Parameter(valueType, "val");
    var valueConst = Expression.Constant(valueCompareValue, typeof(TValue));
    var valueBlock = CreateStringCompare(valueTransform, valueComparison, valueParam, valueConst);
    var valueLamda = Expression.Lambda<Func<TValue, bool>>(valueBlock, new[] { Expression.Parameter(typeof(TValue)) });

    var valueProp = Expression.Property(keyValueParm, "Value");
    var anyExpre = Expression.Call(typeof(Enumerable), EnumerableMethodName.Any.ToString(), new[] { valueType }, valueProp, valueLamda);

    return Expression.AndAlso(keyBlock, anyExpre);
}

public static MethodCallExpression CreateStringCompare(string stringTransform, string stringComparison, Expression memberExpression, ConstantExpression constantExpression)
{
    var stringType = typeof(string);
    var stringParameter = Expression.Parameter(stringType);

    return memberExpression == null
        ? Expression.Call(
            Expression.Call(stringParameter, stringType.GetMethod(stringTransform, Type.EmptyTypes)),
            typeof(string).GetMethod(stringComparison, new[] { stringType }),
            Expression.Call(constantExpression, stringType.GetMethod(stringTransform, Type.EmptyTypes)))
        : Expression.Call(
            Expression.Call(memberExpression, stringType.GetMethod(stringTransform, Type.EmptyTypes)),
            typeof(string).GetMethod(stringComparison, new[] { stringType }),
            Expression.Call(constantExpression, stringType.GetMethod(stringTransform, Type.EmptyTypes)));
}

With all of this Lambda#4 and Lambda#3 from the debugview seem to be generated properly, but I keep getting stuck on Lambda#2.

Any help would be greatly appreciated. I've put a lot of time into this project and made a lot of progress, but am quite stuck at this point.

War es hilfreich?

Lösung

Your sequence and lambda both treat T in your call to Any as a KeyValuePair<string, IEnumerable<string>>. That's what you have a sequence of, and that's what your predicate accepts as a parameter, but what you pass as the generic type argument is HtmlElement. This isn't compatible, hence the error.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top