如何使用反射调用IDBSet <t> .FirstOrDefault(谓词)?</t>

时间:2014-02-01 18:50:23

标签: c# entity-framework reflection

给定一个IDbSet,其中Person包含“Id”属性,如何执行以下命令:

var p = PersonDbSet.FirstOrDefault(i=>i.Id = 3);

我可以构建谓词,并获得对FirstOrDefault扩展方法的引用,但我似乎无法将它们全部放在一起:

首先是谓词

ParameterExpression parameter = Expression.Parameter(entityType, "Id");
MemberExpression property = Expression.Property(parameter, 3);
ConstantExpression rightSide = Expression.Constant(refId);
BinaryExpression operation = Expression.Equal(property, rightSide);
Type delegateType = typeof (Func<,>).MakeGenericType(entityType, typeof (bool));
LambdaExpression predicate  = Expression.Lambda(delegateType, operation, parameter);

现在引用扩展方法:

    var method = typeof (System.Linq.Queryable).GetMethods(BindingFlags.Static | BindingFlags.Public)
                .FirstOrDefault(m => m.Name == "FirstOrDefault" && m.GetParameters().Count() == 2);

   MethodInfo genericMethod = method.MakeGenericMethod(new[] { entityType });            

最后尝试执行方法:

object retVal = genericMethod.Invoke(null, new object[] {dbSet, predicate});

使用以下消息抛出ArgumentException:

“System.Reflection.RuntimePropertyInfo”类型的对象无法转换为类型'System.Linq.IQuerable`1 [Person]'。“

有什么想法吗?

3 个答案:

答案 0 :(得分:2)

您必须制作该方法的通用版本:

MethodInfo genericMethod = method.MakeGenericMethod(entityType);

object retVal = genericMethod.Invoke(dbSet, new object[] {expr});

顺便说一句。你不应该尝试从System.Linq.Queryable获取方法吗? System.Linq.Enumerable完全是关于对象的linq,看起来你正在试图调用你的数据库。

答案 1 :(得分:0)

我想出了问题..我正在反映我的数据上下文以获取dbset,并返回属性信息而不是属性的值。

以上代码现在效果很好..感谢所有人的帮助!

答案 2 :(得分:0)

我有同样的问题。这段代码也对我有用。希望这对您有帮助...

public async Task<Unit> Handle(UpdateCustomCommand<TType> request, CancellationToken cancellationToken)
    {
        //TODO: set access based on user credentials
        var contextCollectionProp = _context.GetType()
            .GetProperties(BindingFlags.Instance | BindingFlags.Public)
            .FirstOrDefault(e => e.PropertyType == typeof(DbSet<TType>));

        if (contextCollectionProp == null)
        {
            throw new UnSupportedCustomEntityTypeException(typeof(TType), "DB Contexts doesn't contains collection for this type.");
        }

        var firstOrDefaultMethod = typeof(System.Linq.Queryable).GetMethods(BindingFlags.Static | BindingFlags.Public)
            .FirstOrDefault(m => m.Name == "FirstOrDefault" && m.GetParameters().Count() == 2);

        if (firstOrDefaultMethod == null)
        {
            throw new UnSupportedCustomEntityTypeException(typeof(TType), "Cannot find \"Syste.Linq.FirstOrDefault\" method.");
        }


        Expression<Func<TType, bool>> expr = e => request.Id.Equals(e.ID);

        firstOrDefaultMethod = firstOrDefaultMethod.MakeGenericMethod(typeof(TType));

        TType item = firstOrDefaultMethod.Invoke(null, new[] { contextCollectionProp.GetValue(_context), expr }) as TType;

        if (item == null)
        {
            throw new NotFoundException(nameof(TType), request.Id);
        }

        //TODO: use any mapper instead of following code
        item.Code = request.Code;
        item.Description = request.Description;

        await _context.SaveChangesAsync(cancellationToken);

        return Unit.Value;
    }