Ядро проблемы заключается в существовании вашего базового класса. Вы должны избавиться от базового класса. Удаление базового класса полностью устранит вашу проблему.
Базовый класс является проблематичным, поскольку:
- Это вызывает каждый производный тип, чтобы иметь зависимость от этого класса.
- Это усложняет тестирование, так как все сквозные проблемы всегда выполняются.
- Базовый класс станет большим бесконечно растущим классом; одно нарушение ответственности.
Хотя вы можете использовать инъекции собственности, чтобы избавиться от Service Locator, это не снимает указанные проблемы и даже вводит новую проблему, которая: Temporal Coupling.
Вместо этого вы должны просто избавиться от базового класса и переместить эти сквозные проблемы в декораторы. В вашем случае я предлагаю создать два декоратора.
TransactionBusinessDecorator
:
public class TransactionBusinessDecorator<TRequest, TResult>
: IBusiness<TRequest, TResult>
where TResult : BaseResponse
{
private readonly IConnectionProvider connectionProvider;
private readonly IBusiness<TRequest, TResult> decoratee;
public TransactionBusinessDecorator(
IConnectionProvider connectionProvider,
IBusiness<TRequest, TResult> decoratee)
{
this.connectionProvider = connectionProvider;
this.decoratee = decoratee;
}
public TResult Send(TRequest request)
{
TResult response;
using (DatabaseScope scope = DatabaseScopeManager.Instance.GetScope(
this.connectionProvider,
DatabaseScopeType.UseExistingOrNewTX,
IsolationLevel.ReadCommitted))
{
response = this.decoratee.Handle(request);
scope.Complete();
}
return response;
}
}
И ExceptionWrapperBusinessDecorator
:
public class ExceptionWrapperBusinessDecorator<TRequest, TResult>
: IBusiness<TRequest, TResult>
where TResult : BaseResponse
{
private readonly IBusiness<TRequest, TResult> decoratee;
public ExceptionWrapperBusinessDecorator(
IBusiness<TRequest, TResult> decoratee)
{
this.decoratee = decoratee;
}
public TResult Send(TRequest request)
{
TResult response = Activator.CreateInstance<TResult>();
try
{
var response = this.decoratee.Handle(request);
response.Success = true;
}
catch (Exception ex)
{
response.Success = false;
response.Message = exc.Message;
}
return response
}
}
Это позволяет AccountCreateBusiness
быть записана следующим образом:
public sealed class AccountCreateBusiness
: IBusiness<AccountCreateRequest, AccountCreateResponse>
{
private readonly IMessageProvider messageProvider;
public AccountCreateBusiness(IMessageProvider messageProvider)
{
this.messageProvider = messageProvider;
}
public AccountCreateResponse Handle(AccountCreateRequest request)
{
//handle request
}
}
Обертывание каждый обработчик с декораторами тривиальна с большинством контейнеров и n делать это вручную (a.k.a. Pure DI).
Был последним советом по дизайну. Занимаясь каждый исключение и возвращать информацию как часть сообщения, как правило, плохая идея, потому что:
- Эти коды ошибок просачивается в базе системы (ваш бизнес слой), который не имеет ничего общего с ним. Вместо того, чтобы эта информация была доступна в базовом ответе, используйте конверт, содержащий эту информацию.
- Вы предоставите клиенту техническую информацию. Обычно клиент не интересуется этим уровнем детализации. Вы хотите только вернуть функциональные сообщения.
- Информация, относящаяся к безопасности, возвращается; хакеры любят такую информацию.
- Если вы не регистрируете эту информацию, вы теряете множество важных сведений об ошибке.
- Чтобы в принципе вернуть коды ошибок, вместо работы с исключениями. Коды ошибок очень подвержены ошибкам; клиент должен всегда проверять результат, чтобы увидеть, удалось ли ответить.
Вложение свойств, однако, приводит к [Временному соединению] (http://blog.ploeh.dk/2011/05/24/DesignSmellTemporalCoupling/). – Steven
@steven BTW, когда вы используете контейнер, есть небольшая вероятность, что в результате будут установлены зависимости, не заданные по свойствам .. это может произойти, если вы не настроили какой-то компонент, верно? –
Правильно. Конструктор-инжектор дает нам * гарантию *, что объект задан правильно после его построения. Мы теряем эту способность с введением свойств. – Steven