使用ExpandoObject创建接口的“假”实现 - 动态添加方法

问题描述 投票:1回答:2

为你做脑筋急转弯!

我正在开发一个模块化系统,模块A可能需要模块B而模块B也需要模块A.但是如果模块B被禁用,它将不执行该代码而不执行任何操作/返回null。

透视一点:

让我们说InvoiceBusinessLogic在模块“核心”内。我们还有一个“电子商务”模块,它有一个OrderBusinessLogic。然后InvoiceBusinessLogic看起来像这样:

public class InvoiceBusinessLogic : IInvoiceBusinessLogic
{
    private readonly IOrderBusinessLogic _orderBusinessLogic;

    public InvoiceBusinessLogic(IOrderBusinessLogic orderBusinessLogic)
    {
        _orderBusinessLogic = orderBusinessLogic;
    }

    public void UpdateInvoicePaymentStatus(InvoiceModel invoice)
    {
        _orderBusinessLogic.UpdateOrderStatus(invoice.OrderId);
    }
}

所以我想要的是:当启用模块“电子商务”时,它实际上会在OrderBusinessLogic上做一些事情。如果没有,它根本就不会做任何事情。在这个例子中,它什么都不返回,所以它可以简单地什么也不做,在其他一些会返回某些东西的例子中,它会返回null。

笔记:

  • 您可以告诉我,我正在使用依赖注入,它是一个ASP.NET核心应用程序,因此IServiceCollection负责定义实现。
  • 从逻辑上讲,简单地不定义IOrderBusinessLogic的实现将导致运行时问题。
  • 从大量的研究中,我不想在应用程序的域/逻辑中调用容器。 Don't call the DI Container, it'll call you
  • 模块之间的这种交互保持最小,最好在控制器内完成,但有时你无法绕过它(在控制器中我还需要一种方法来注入和使用它们)。

所以我到目前为止有3个选项:

  1. 我从不打电话给模块“核心”到模块“电子商务”,理论上这听起来是最好的方式,但在实践中它对于高级场景来说更复杂。不是一种选择
  2. 我可以创建很多虚假的实现,具体取决于配置决定实现哪一个。但这当然会导致双重代码,并且我会不得不在引入新方法时更新假类。所以不完美。
  3. 我可以使用反射和ExpandoObject构建一个虚假的实现,并且在调用特定方法时不做任何操作或返回null。

最后一个选项就是我现在所追求的:

private static void SetupEcommerceLogic(IServiceCollection services, bool enabled)
{
    if (enabled)
    {
        services.AddTransient<IOrderBusinessLogic, OrderBusinessLogic>();
        return;
    }
    dynamic expendo = new ExpandoObject();
    IOrderBusinessLogic fakeBusinessLogic = Impromptu.ActLike(expendo);
    services.AddTransient<IOrderBusinessLogic>(x => fakeBusinessLogic);
}

通过使用Impromptu Interface,我能够成功创建一个虚假的实现。但我现在需要解决的是动态对象还包含所有方法(主要是不需要的属性),但这些方法很容易添加。所以目前我能够运行代码并起床直到它将调用OrderBusinessLogic,然后逻辑上会抛出该方法不存在的异常。

通过使用反射,我可以遍历界面中的所有方法,但是如何将它们添加到动态对象?

dynamic expendo = new ExpandoObject();
var dictionary = (IDictionary<string, object>)expendo;
var methods = typeof(IOrderBusinessLogic).GetMethods(BindingFlags.Public);
foreach (MethodInfo method in methods)
{
    var parameters = method.GetParameters();
    //insert magic here
}

注意:现在直接调用typeof(IOrderBusinessLogic),但稍后我会迭代某个程序集中的所有接口。

Impromptu有一个例子如下:expando.Meth1 = Return<bool>.Arguments<int>(it => it > 5);

但是我当然希望这是动态的,所以如何动态插入返回类型和参数。

我确实理解一个界面就像合同一样,并且应该遵循该合同,我也理解这是一种反模式,但在达到这一点之前已经进行了广泛的研究和谈判,对于我们想要的结果系统,我们认为这是最好的选择,只是有点缺失:)。

  • 我看过this question,我并不打算离开.dll,因为很可能我不能在IOrderBusinessLogic中使用任何形式的InvoiceBusinessLogic
  • 我看过this question,但我真的不明白TypeBuilder在我的场景中是如何使用的
  • 我也研究了Mocking接口,但是大多数情况下你需要为你想要改变的每个方法定义'mocking implementation',如果我错了就纠正我。
c# dependency-injection modularity impromptu-interface
2个回答
© www.soinside.com 2019 - 2024. All rights reserved.