Головоломка для вас!
Я разрабатываю модульную систему таким образом, чтобы модуль A мог нуждаться в модуле B, а модуль B мог также нуждаться в модуле A. Но если модуль B отключен, он просто не будет выполнять этот код и ничего не делать / возвращать ноль .
Немного больше в перспективе:
Допустим, InvoiceBusinessLogic
находится внутри модуля "Ядро". У нас также есть модуль "Ecommerce", который имеет 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
. Когда нет, он просто ничего не сделает. В этом примере он ничего не возвращает, поэтому он может просто ничего не делать, в других случаях, когда что-то будет возвращено, он вернет ноль.
Примечания:
- Как вы, вероятно, можете сказать, я использую Dependency Injection, это приложение ASP.NET Core, поэтому
IServiceCollection
заботится об определении реализаций.
- Если вы просто не определите реализацию для
IOrderBusinessLogic
, это приведет к логической проблеме во время выполнения.
- Из проведенных исследований я не хочу делать вызовы контейнера в моем домене / логике приложения. Не вызывайте DI-контейнер, он позвонит вам
- Такого рода взаимодействия между модулями сведены к минимуму, желательно внутри контроллера, но иногда вы не можете обойти его (а также в контроллере мне тогда понадобится способ ввести их и использовать их или нет).
Итак, есть 3 варианта, которые я выяснил до сих пор:
- Я никогда не звоню из модуля «Ядро» в модуль «Электронная коммерция», теоретически это звучит лучше, но на практике это сложнее для сложных сценариев. Не вариант
- Я мог бы создать множество фальшивых реализаций, в зависимости от конфигурации, решив, какую из них реализовать. Но это, конечно, привело бы к двойному коду, и мне постоянно приходилось бы обновлять фальшивый класс при появлении нового метода. Так что не идеально.
- Я могу создать ложную реализацию, используя отражение и
ExpandoObject
, и просто ничего не делать или возвращать ноль при вызове определенного метода.
И последний вариант - это то, чем я сейчас являюсь:
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);
Но, конечно, я хочу, чтобы это было динамически, так как мне динамически вставить тип возвращаемого значения и параметры.
Я понимаю, что интерфейс действует как контракт, и этот контракт должен соблюдаться, я также понимаю, что это антишаблон, но до достижения этой точки, для системы результатов, были проведены обширные исследования и переговоры мы хотим, мы думаем, что это лучший вариант, просто немного не хватает:).
- Я смотрел на этот вопрос , на самом деле я не планирую оставлять .dll вне, потому что, скорее всего, я не смогу использовать какую-либо форму
IOrderBusinessLogic
, пригодную для использования в InvoiceBusinessLogic
.
- Я смотрел на этот вопрос , но я не совсем понял, как TypeBuilder может быть использован в моем сценарии
- Я также рассмотрел Mocking интерфейсы, но в большинстве случаев вам нужно будет определить «реализацию mocking» для каждого метода, который вы хотите изменить, исправьте меня, если я ошибаюсь.