当编译时T未知时,如何使用Where <t>扩展方法</t>

时间:2012-08-01 09:44:54

标签: c# linq entity-framework lambda generic-collections

我正在使用JSON编写C#MVC应用程序,并尝试使用EF将JSON检索回数据库。

不幸的是,我无法使这些代码正常工作:

IEnumerable<object> NewValueSubCollection = (IEnumerable<object>)NewValue.GetType().GetProperty(col.Name).GetValue(NewValue, null);
//Where col is a property of an object of type (IEnumerable<???>)

foreach (var line in ((IEnumerable<object>)col.GetValue(originalEntity, null)).ToList<object>())
{
    Type lineType = ObjectContext.GetObjectType(line.GetType());
    var lineEntitySet = context.GetEntitySet(lineType);
    EntityKey lineKey = context.CreateEntityKey(lineEntitySet.Name, line);
    if (NewValueSubCollection.Where(lineKey).Count() == 0) //See bellow
    context.DeleteObject(line);
}

我已经实现了这个:

public static class LinqExtension
{
    //Enable searching in a collection is an entity Key exist... and retrieve info.
    public static IEnumerable<T> Where<T>(this IEnumerable<T> source,EntityKey key)
    {
        return Enumerable.Where(source, CreateFilterExpressionBasedOnKey<T>(key).Compile());
    }

    private static Expression<Func<TInput, bool>> CreateFilterExpressionBasedOnKey<TInput>(Type sourceType, EntityKey key)
    {
        var param = Expression.Parameter(sourceType, "");

        Expression myFilter = Expression.Constant(true);
        if (key != null)
        {
            foreach (var item in key.EntityKeyValues)
            {
                Expression Left = Expression.PropertyOrField(param, item.Key.ToString());
                Expression Right = Expression.Constant(item.Value);

                myFilter = Expression.AndAlso(myFilter, Expression.Equal(Left, Right));
            }
        }
        return Expression.Lambda<Func<TInput, bool>>(myFilter, param);
    }
}

问题是NewValueSubCollection的类型为:IEnumerable<object>当我希望我可以通过我的Where扩展名传递IEnumerable<MyListObjectType> .... 然后我在运行时遇到异常: System.ArgumentException:'TOId'不是'System.Object'

类型的成员

如果我使用:

Type generic = typeof(Collection<>);
Type typeArgs = (NewValue.GetType().GetProperty(col.Name).GetValue(NewValue, null)).GetType().GetGenericArguments()[0];
Type constructed = generic.MakeGenericType(typeArgs);
object o = Activator.CreateInstance(constructed);
o = NewValue.GetType().GetProperty(col.Name).GetValue(NewValue, null);

我收到以下代码的编译错误:

if (o.Where(lineKey).Count() == 0) //error!!!
// 'object' does not contain a definition for 'Where' acception a first argument of type 'object' could be found (are you missing directive or an assembly reference?)

2 个答案:

答案 0 :(得分:1)

您需要动态访问它,但您可以使用'dynamic'关键字避免一些令人头疼的反思。

所以这个:

NewValueSubCollection.Where(lineKey).Count() == 0

成为这个:

NewValueSubCollection.Cast<dynamic>().Where(d => lineKey(d)).Count() == 0

或者(更简单,更有效,如果枚举没有副作用):

!NewValueSubCollection.Cast<dynamic>().Any(d => lineKey(d))

一个简单的概念验证程序:

class Program
{
    static void Main(string[] args)
    {
        var list = new List<string>();
        IEnumerable<object> objects = list;
        Func<string, bool> func = s => s.Contains("x");

        list.Add("Test");
        list.Add("x");
        list.Add("mux");
        foreach (var item in objects.Cast<dynamic>().Where(d => func(d)))
            Console.WriteLine(item);
        Console.ReadKey();
    }
}

答案 1 :(得分:0)

在您的第一个示例中,NewValueSubCollection静态类型为IEnumerable<object>,因此您的参数化类型Tobject,当您编译表达式时,它会尝试查找对象类的属性,因此是异常。

同样地,o.Where(...)无法正常工作,因为在Where上定义了IEnumerable<>扩展方法,并且这些方法在编译时是静态解析的。

我担心你必须通过反思来调用Where方法,你可以在这里找到一些帮助:How do I invoke an extension method using reflection?