如何从 Expression<Action<T>> 中获取值?

How to get values out of an Expression<Action<T>>?

我正在编写一个非常奇怪的测试,用于测试是否发送了电子邮件。我们使用 hangfire,所以我有一个包装器 IHangfireWrapper,这样我就可以模拟 Enqueue 方法。这个Enqueue方法接受一个参数(Expression<Action<T>>).

所以如果我想使用这个方法,它看起来像这样。

_ServiceProvider.HangfireWrapper
    .Enqueue<HangfireTasks>(x => x.SendEmail(subject, body, to));

我在其中传递主题、正文和收件人的字符串。

所以这是棘手的部分。我想模拟这个调用,这样我就可以查看主题、正文和字符串。

我有这个有效的代码,我可以取回我想要的字符串,但它非常难看。

var hangfireMock = new Mock<IHangfireWrapper>();

string body = null;
string subject = null;
string to = null;

hangfireMock.Setup(x => x.Enqueue(It.IsAny<Expression<Action<HangfireTasks>>>()))
    .Callback<Expression<Action<HangfireTasks>>>(expr =>
    {
        object parameters = ((ConstantExpression)((MemberExpression)((MethodCallExpression)expr.Body).Arguments[0]).Expression).Value;
        body = (string)parameters.GetType().GetField("body").GetValue(parameters);
        subject = (string)parameters.GetType().GetField("subject").GetValue(parameters);
        to = (string)parameters.GetType().GetField("to").GetValue(parameters);
    });

有没有 better/shorter 方法来做到这一点? (这段代码甚至有意义吗?)

您正在做的事情可能适用于您的编译器版本,只要您始终完全按照那样形成 lambda。

以下是一些它无法使用的 lambda 表达式示例:

x => x.SendEmail("subject", "body", "to")
x => x.SendEmail(subject, subject, to)

你应该做的是获取 SendEmail 调用的 Arguments 并通过为它创建一个 lambda 表达式然后编译它来评估它们中的每一个:

var arguments = ((MethodCallExpression)expr.Body).Arguments;
subject = Expression.Lambda<Func<string>>(arguments[0]).Compile()();
body = Expression.Lambda<Func<string>>(arguments[1]).Compile()();
to = Expression.Lambda<Func<string>>(arguments[2]).Compile()();

编译表达式的成本相对较高,因此解释表达式是有意义的。 You have that choice on .Net Core 并且您可能还会在某些未来版本的 .Net Framework 中使用。