Is it bad design to reference Autofac in my projects just for Owned<T>?

安稳与你 提交于 2019-11-30 18:33:34

I would say that it's fine to reference a well defined set of core 3rd party DLLs in every project of an "enterprise application" solution (or any application that needs flexibility). I see nothing wrong with having a dependency on at least the following in every project that needs it:

  • A logging framework (e.g. log4net)
  • Some IoC container (e.g. Autofac)

The fact that these aren't part of the core .NET framework shouldn't stop us from using them as liberally.

The only possible negatives I can see are relatively minor compared to the possible benefits:

  • This may make the application harder to understand for the average programmer
  • You could have version compatibility problems in the future which you wouldn't encounter if you were just using the .NET framework
  • There is an obvious but minor overhead with adding all of these references to every solution
Peter Lillevold

I agree with @steinar, I would consider Autofac as yet another 3rd party dll that supports your project. Your system depends on it, why should you restrict yourself from referencing it? I would be more conserned if ILifetimeScope or IComponentContext were sprinkled around your code.

That said, I feel your consern. After all, a DI container should work behind the scenes and not "spill" into the code. But we could easily create a wrapper and an interface to hide even the Owned<T>. Consider the following interface and implementation:

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();
    }
}

The registration could be done, either using a registration source or a builder, e.g. like this:

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

You can now resolve as usual:

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

You could place this interface in a common namespace in your system for easy inclusion. Both the Owned<T> and OwnedWrapper<T> are now hidden from your code, only IOwned<T> is exposed. Should requirements change and you need to replace Autofac with another DI container there's a lot less friction with this approach.

Perhaps if Owned<> was a built-in .NET type, this whole dilemma would go away? (Should I even hold my breath for that to happen?)

It will become a built-in .NET type: ExportLifeTimeContext<T>. Despite the name, this class isn't really bound to the .NET ExportFactory<T>. The constructor simply takes a value, and an Action to invoke when the lifetime of that value is disposed.

For now, it is only available in Silverlight though. For the regular .NET framework you'll have to wait until .NET 4.x (or whatever the next version after 4.0 will be).

I don't think referencing the Autofac assembly is the real problem - I consider things like Owned appearing in application code a 'code smell'. Application code shouldn't care about what DI framework is being used and having Owned in your code now creates a hard dependency on Autofac. All DI related code should be cleanly contained in a set of configuration classes (Modules in the Autofac world).

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!