Это плохой дизайн, чтобы ссылаться на Autofac в моих проектах только для Own <T>?

Недавно я стал тяжелым пользователем функции Autofac OwnedInstances. Например, я использую его для создания factory для создания Единицы работы для моей базы данных, что означает, что мои классы, которые зависят от UnitOfWork factory, запрашивают объекты типа:

Func<Owned<IUnitOfWork>>

Это невероятно полезно - отлично подходит для сохранения IDisposable из моих интерфейсов - но он поставляется с ценой: поскольку Owned < > является частью сборки Autofac, Я должен ссылаться на Autofac в каждом из моих проектов, который знает о Owned < > и помещает "использование Autofac.Features.OwnedInstances" в каждый файл кода.

Func < > имеет большое преимущество в том, чтобы быть встроенным в платформу .NET, поэтому я не сомневаюсь, что для использования Func в качестве универсальной обертки factory следует использовать Func. Но Owned < > находится в сборке Autofac, и каждый раз, когда я его использую, я создаю жесткую ссылку на Autofac (даже если моя единственная ссылка на Autofac - это тип Owned < > в аргументе метода интерфейса).

Мой вопрос: это плохо? Это начнет укусить меня в некотором роде, что я еще не учею? Иногда у меня будет проект, на который ссылаются многие другие проекты, и поэтому, естественно, мне нужно максимально приблизить его зависимости до нуля; Я делаю зло, передавая Func < Owned < IUnitOfWork → (который фактически является поставщиком транзакций базы данных) в методы на этих интерфейсах (что в противном случае было бы автоактивным)?

Возможно, если Owned < > был встроенным типом .NET, вся эта дилемма исчезла бы? (Должен ли я даже задержать дыхание, чтобы это произошло?)

Ответ 1

Я бы сказал, что это хорошо, чтобы ссылаться на четко определенный набор основных сторонних DLL в каждом проекте решения "корпоративного приложения" (или на любое приложение, требующее гибкости). Я не вижу ничего плохого в зависимости от, по крайней мере, следующего в каждом проекте, который ему нужен:

  • Структура ведения журнала (например, log4net)
  • Некоторые контейнеры IoC (например, Autofac)

Тот факт, что они не являются частью основной платформы .NET, не должен мешать нам использовать их как либерально.

Единственные возможные негативы, которые я вижу, относительно незначительны по сравнению с возможными преимуществами:

  • Это может затруднить понимание приложения средним программистом
  • У вас могут возникнуть проблемы совместимости версий в будущем, с которыми вы не столкнулись бы, если бы вы использовали .NET framework
  • Есть очевидные, но незначительные накладные расходы с добавлением всех этих ссылок на каждое решение

Ответ 2

Я согласен с @steinar, я бы рассмотрел Autofac как еще одну стороннюю DLL, которая поддерживает ваш проект. Ваша система зависит от этого, почему вы должны ограничивать себя ссылкой? Я был бы более заинтересован, если бы ILifetimeScope или IComponentContext были посыпаны вашим кодом.

Тем не менее, я чувствую, что вы живете. В конце концов, контейнер DI должен работать за кулисами, а не "проливать" на код. Но мы могли бы легко создать оболочку и интерфейс, чтобы скрыть даже Owned<T>. Рассмотрим следующий интерфейс и реализацию:

public interface IOwned<out T> : IDisposable
{
    T Value { get; }
}

public class OwnedWrapper<T> : Disposable, IOwned<T>
{
    private readonly Owned<T> _ownedValue;

    public OwnedWrapper(Owned<T> ownedValue)
    {
        _ownedValue = ownedValue;
    }

    public T Value { get { return _ownedValue.Value; } }

    protected override void Dispose(bool disposing)
    {
        if (disposing)
            _ownedValue.Dispose();
    }
}

Регистрация может быть выполнена либо с использованием источника регистрации, либо с помощью строителя, например. например:

var cb = new ContainerBuilder();
cb.RegisterGeneric(typeof (OwnedWrapper<>)).As(typeof (IOwned<>)).ExternallyOwned();
cb.RegisterType<SomeService>();
var c = cb.Build();

Теперь вы можете решить как обычно:

using (var myOwned = c.Resolve<IOwned<SomeService>>())
{
    var service = myOwned.Value;       
}

Вы можете разместить этот интерфейс в общем пространстве имен в своей системе для удобства включения. Оба Owned<T> и OwnedWrapper<T> теперь скрыты от вашего кода, отображается только IOwned<T>. Если требования меняются, и вам необходимо заменить Autofac другим контейнером DI, там будет гораздо меньше трения с этим подходом.

Ответ 3

Возможно, если Owned < > был встроенным .NET типа, вся эта дилемма далеко? (Если я даже задержу дыхание чтобы это произошло?)

Он станет встроенным типом .NET: ExportLifeTimeContext<T>. Несмотря на название, этот класс не связан с .NET ExportFactory<T>. Конструктор просто принимает значение и Action, чтобы вызвать, когда время жизни этого значения расположено.

Пока что он доступен только в Silverlight. Для обычной платформы .NET вам придется подождать до .NET 4.x(или любой другой следующей версии после 4.0).

Ответ 4

Я не думаю, что ссылка на сборку Autofac - настоящая проблема. Я считаю, что такие вещи, как Owned, появляются в коде приложения "запах кода". Код приложения не должен заботиться о том, какая инфраструктура DI используется, а Owned в вашем коде теперь создает жесткую зависимость от Autofac. Весь код, связанный с DI, должен содержаться в наборе классов конфигурации (модули в мире Autofac).