创建表达式 (Create an Expression> using reflecti

2019-06-24 16:45发布

即时通讯使用起订量来创建一个数据集的嘲笑。

我创建了一个小帮手类,允许我在内存中的存储,而不是使单元测试轻而易举数据库有一个。 这样我可以添加和删除我的模拟数据集中的项目,这让我来测试我的插入和删除服务呼叫。

在模拟的设置我有一条线,如下所示

this.Setup(i => i.AcademicCycles).Returns(mockStore.GetList<AcademicCycle>());

我假装有很多的属性,所以我想执行使用反射此设置步骤。 我已成功地在Returns的过程中通过反射工作的一部分,但我被困在拉姆达的方法来Setup

Setup需要一个

Expression<Func<GoalsModelUnitOfWork, IQueryable<AcademicCycle>>>对应于i => i.AcademicCycles

我想动态地创建这一点。 使用反射我有以下几点:

该属性的名称:“AcademicCycles”

类型IQueryable<AcademicCycle>

该类型AcademicCycle

我也有实例i在lambda声明这是一个GoalsModelUnitOfWork

Answer 1:

该代码来动态创建的表达会是这样:

ParameterExpression parameter = Expression.Parameter(typeof (GoalsModelUnitOfWork), "i");
MemberExpression property = Expression.Property(parameter, "AcademicCycles");

var queryableType = typeof (IQueryable<>).MakeGenericType(typeof (AcademicCycle));
var delegateType = typeof (Func<,>).MakeGenericType(typeof (GoalsModelUnitOfWork), queryableType);

var yourExpression = Expression.Lambda(delegateType, property, parameter);

其结果将有需要的类型,但问题是,返回类型Expression.Lambda()LambdaExpression ,你不能进行类型转换Expression<Func<...>>把它作为参数,以此来设置功能,因为你不知道的泛型类型参数的Func 。 所以,你必须调用Setup反射法,太:

this.GetType().GetMethod("Setup", yourExpression.GetType()).Invoke(this, yourExpression);


Answer 2:

我决定采取一个裂缝它,并结束了这个神可怕的一段代码。

我没有反射专家,这只是第一次尝试得到的东西的工作。 我会很感兴趣什么其他方法人都有,或是否有任何relfection包装库可以让这个更好。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using Moq;
using Xunit;

namespace MyExample
{
    public class Tests
    {
        [Fact]
        public void Test()
        {
            Dictionary<Type, object> data = new Dictionary<Type, object>
            {
                { typeof(IQueryable<Cycle>), new List<Cycle> { new Cycle { Name = "Test" } }.AsQueryable() },
                { typeof(IQueryable<Rider>), new List<Rider> { new Rider { Name = "1"}, new Rider { Name = "2" } }.AsQueryable() }
            };

            var mock = new Mock<IDataContext>();
            var setup = mock.GetType().GetMethods().Single(d => d.Name == "Setup" && d.ContainsGenericParameters);
            var param = Expression.Parameter(typeof(IDataContext), "i");
            foreach (var property in typeof(IDataContext).GetProperties(BindingFlags.Public | BindingFlags.Instance))
            {
                // Build lambda
                var ex = Expression.Lambda(Expression.MakeMemberAccess(param, property), param);

                // Get generic version of the Setup method
                var typedSetup = setup.MakeGenericMethod(property.PropertyType);

                // Run the Setup method
                var returnedSetup = typedSetup.Invoke(mock, new[] { ex });

                // Get generic version of IReturns interface
                var iReturns = typedSetup.ReturnType.GetInterfaces().Single(d => d.Name.StartsWith("IReturns`"));

                // Get the generic Returns method
                var returns = iReturns.GetMethod("Returns", new Type[] { property.PropertyType });

                // Run the returns method passing in our data
                returns.Invoke(returnedSetup, new[] { data[property.PropertyType] });
            }

            Assert.Equal(1, mock.Object.Cycles.Count());
        }
    }

    public class Cycle
    {
        public string Name { get; set; }
    }

    public class Rider
    {
        public string Name { get; set; }
    }

    public interface IDataContext
    {
        IQueryable<Cycle> Cycles { get; set; }

        IQueryable<Rider> Riders { get; set; }
    }
}


Answer 3:

该方法应该构造lambda表达式。 既然你是调用由反射的设置方法,你并不需要一个强类型的lambda表达式; 你打算把它作为当你调用一个对象数组的一部分Invoke

    public LambdaExpression PropertyGetLambda(string parameterName, Type parameterType, string propertyName, Type propertyType)
    {
        var parameter = Expression.Parameter(parameterType, parameterName);
        var memberExpression = Expression.Property(parameter, propertyName);
        var lambdaExpression = Expression.Lambda(memberExpression, parameter);
        return lambdaExpression;
    }

我不认为你真正需要的参数名称。 如果我说得对,你可以简化一下:

    public LambdaExpression PropertyGetLambda(Type parameterType, string propertyName, Type propertyType)
    {
        var parameter = Expression.Parameter(parameterType);
        var memberExpression = Expression.Property(parameter, propertyName);
        var lambdaExpression = Expression.Lambda(memberExpression, parameter);
        return lambdaExpression;
    }


文章来源: Create an Expression> using reflection