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 开发者_如何学JAVAa 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?)
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.
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
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).
精彩评论