10

I've recently become a heavy user of Autofac's OwnedInstances feature. For example, I use it to provide a factory for creating a Unit of Work for my database, which means my classes which depend on the UnitOfWork factory are asking for objects of type :

Func<Owned<IUnitOfWork>>

This is incredibly useful--great for keeping IDisposable out of my interfaces--but it comes with a price: since Owned<> is part of the Autofac assembly, I have to reference Autofac in each of my projects that knows about Owned<>, and put "using Autofac.Features.OwnedInstances" in every code file.

Func<> has the great benefit of being built into the .NET framework, so I have no doubts that it's fine to use Func as a universal factory wrapper. But Owned<> is in the Autofac assembly, and every time I use it I'm creating a hard reference to Autofac (even when my only reference to Autofac is an Owned<> type in an interface method argument).

My question is: is this a bad thing? Will this start to bite me back in some way that I'm not yet taking into account? Sometimes I'll have a project which is referenced by many other projects, and so naturally I need to keep its dependencies as close as possible to zero; am I doing evil by passing a Func<Owned<IUnitOfWork>> (which is effectively a database transaction provider) into methods in these interfaces (which would otherwise be autofac-agnostic)?

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?)

Community
  • 1
  • 1
Jay Sullivan
  • 17,332
  • 11
  • 62
  • 86

4 Answers4

11

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.

Community
  • 1
  • 1
Peter Lillevold
  • 33,668
  • 7
  • 97
  • 131
  • I'd thought about something like this, but then I'd need to reference *that* interface everywhere. It ends up turning into the effectively same problem. – Jay Sullivan Mar 28 '11 at 13:10
  • 1
    Well, you will have to reference *something*. This way, at least you have ownership to the type. – Peter Lillevold Mar 28 '11 at 15:45
  • I opted for this solution some time ago when Autofac first introduced the feature. – Damian Mar 16 '12 at 20:13
  • That works great! Any tip how to handle Meta in similar way? – Michał Zych May 04 '17 at 18:48
  • @MichałZych you could follow the exact same pattern as I've done for `Owned`, create you own `IMeta`, implement a `MetaWrapper` with a ctor `MetaWrapper(Meta meta){...}` The wrapper itself would be registered with `RegisterGeneric` and consumed in the same way. You might want to implement variations for `IEnumerable` support and `Lazy` support – Peter Lillevold May 05 '17 at 11:20
10

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
steinar
  • 9,383
  • 1
  • 23
  • 37
3

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).

Kai G
  • 3,371
  • 3
  • 26
  • 30
3

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).

Wim Coenen
  • 66,094
  • 13
  • 157
  • 251
  • 2
    Wow, that's great to hear. This answers part of my question, I'd accept it as well if I could. Although, honestly, Func> doesn't look as elegant as Func> if it's going to be scattered around as interface arguments. – Jay Sullivan Mar 28 '11 at 13:12
  • 2
    @notfed: agreed. It's also annoying that you will need to reference `system.componentmodel.composition` assembly and namespace. Types which represent fundamental component relationships really belong in `System`, like `System.Lazy`. – Wim Coenen Apr 04 '11 at 10:39