给你的脑筋急转弯!
我正在开发一个模块化系统,这样模块 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 Core 应用程序,因此
IServiceCollection
负责定义实现。 - 从逻辑上讲,简单地不定义实现
IOrderBusinessLogic
将导致运行时问题。 - 从完成的大量研究中,我不想在我的应用程序的域/逻辑中调用容器。不要调用 DI 容器,它会调用你
- 模块之间的这种交互保持在最低限度,最好在控制器内完成,但有时你无法绕过它(而且在控制器中,我需要一种方法来注入它们并使用它们)。
所以到目前为止我想出了3个选项:
- 我从不从模块“Core”调用模块“Ecommerce”,理论上这听起来是最好的方法,但实际上对于高级场景来说它更复杂。不是一个选项
- 我可以创建很多虚假的实现,具体取决于配置决定要实现哪一个。但这当然会导致双重代码,当引入新方法时,我必须不断更新假类。所以并不完美。
- 我可以通过使用反射和构建一个假实现
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)
,但稍后我会遍历某个程序集中的所有接口。
即兴有一个例子如下:
expando.Meth1 = Return<bool>.Arguments<int>(it => it > 5);
但是我当然希望这是动态的,所以我如何动态插入返回类型和参数。
我确实理解接口的行为类似于合同,并且应该遵循合同,我也理解这是一种反模式,但是在达到这一点之前已经进行了广泛的研究和谈判,对于我们想要的结果系统,我们认为这是最好的选择,只是缺少一点 :)。