|
NewInV2
Upgrade help for users of Autofac 1.x
Autofac2 IntroductionAutofac 2.x introduces some significant changes with the aim of simplifying and streamlining Autofac usage. This page highlights the most important changes for Autofac 1.x users. For an overview of the new features for new users, see http://nblumhardt.com/2010/04/introducing-autofac-2-1-rtw/ and http://nblumhardt.com/2010/05/autofac-2-2-released/. Namespace ChangesMost namespaces have been updated to fit a scheme that should keep things more organised and easier to find. The biggest change is that the Autofac.Builder namespace has been moved into Autofac, and an additional namespace Autofac.Core created to hold types that are less-frequently used. Most applications can now be built using the Autofac namespace only. New users will find this namespace easier to explore, as only the types relating to the most common scenarios are there. Builder Syntax ChangesSeparation of Registration OverloadsWhereas previously the type/delegate/instance registration methods were all called Register(), these have now been given distinct names. Only delegates can be registered with the Register() method, other registration types are more explicit: builder.Register(c => new Plane()); builder.Register<Car>(); builder.Register(new Boat()); Becomes: builder.Register(c => new Plane()); // Unchanged builder.RegisterType<Car>(); builder.RegisterInstance(new Boat()); This change makes the available functionality easier to find in IntelliSense(TM), as well as bringing consistency with other methods like RegisterGeneric(), RegisterModule() etc. Non-Shared ('Factory') Components by DefaultThe default lifetime in Autofac 1 is 'singleton'. In Autofac 2, components default to 'factory'. New Lifetime/Sharing TerminologyThe names for lifetime/sharing configuration have been changed to reflect changes in the underlying architecture, and to remove common sources of confusion.
Strongly-typed Activation EventsThe OnActivating configuration method now has a strongly-typed parameter: builder.RegisterType<Connection>().OnActivating(c => c.Open()); Adding to an Existing ContainerIn Autofac 1.x registraitons can be added to an existing container with ContainerBuilder.Build(IContainer). In Autofac 2, registrations are added when creating a new lifetime scope: var container = ...
using (var lifetime = container.BeginLifetimeScope(builder =>
{
builder.RegisterType<X>();
builder.RegisterType<Y>();
}))
{
lifetime.Resolve<X>() //....
}For changes to an existing container, use ContainerBuilder.Update(): var container = // something already built var updater = new ContainerBuilder(); updater.RegisterType<A>(); updater.Register(c => new B()).As<IB>(); // Add the registrations to the container updater.Update(container); Configuration this way is often much harder to follow than a simple "register then build" style, so again, use this feature only where necessary. Other Syntax Changes
Lifetime and ScopeAutofac 2 is more explicit about the role of nesting in lifetime. Instead of creating nested containers with CreateInnerContainer(), as in: using (var requestContainer = container.CreateInnerContainer())
{
}An ILifetimeScope object is now created using BeginLifetimeScope(): using (var requestScope = container.BeginLifetimeScope())
{
}Modules Included by DefaultCollection Support ("Resolve All")All instances supporting a particular service can be retrieved by resolving IEnumerable<T> as in: builder.RegisterType<A1>().As<IA>(); builder.RegisterType<A2>().As<IA>(); var container = builder.Build(); // Contains an instance of both A1 and A2 Assert.AreEqual(2, container.Resolve<IEnumerable<IA>>().Count()); Types can use IEnumerable<T> to declare a dependency, and Autofac 2 will automatically provide all instances as above: class UsesA
{
public UsesA(IEnumerable<IA> allAs) { }
}Auto-Generated FactoriesWhen one component needs to dynamically create instances of another, it can take a dependency on a delegate that returns the type of component it requires: class UsesServer
{
Func<IServerConnection> _connectionFactory;
public UsesServer(Func<IServerConnection> connectionFactory)
{
_connectionFactory = connectionFactory;
}
public void OnNewWork()
{
IServerConnection connection = _connectionFactory();
// ...
}
}No explicit configuration is required to use this feature: builder.RegisterType<FtpConnection>().As<IServerConnection>(); builder.RegisterType<UsesServer>(); By registering IServerConnection, Autofac 2 will automatically inject delegates that return type IServerConnection. Owned InstancesAutofac 2 introduces the Owned<T> type, and automatically resolves dependencies of this type for any available services. A component that needs to dynamically create instances of another component can accept a Func<Owned<T>> to gain this functionality. A component that gains references to Owned<T> is responsible for calling Owned<T>.Dispose when the instance is no longer required. It is a bug to not clean up owned instances. Under the hood, an Owned<T> is allocated its own nested lifetime scope, so all of its dependencies will be cleaned up when it is. Container Instance Ownership ChangesIn Autofac 1, weak references are held by the container. This makes sense if the objects being referenced use disposal to release GC/finalizer resources, but if the dispose method contains application logic then GC timing could introduce unexpected behaviour. Autofac 2 holds normal references. To opt out of this behaviour and mange disposal manually, use the ExternallyOwned registration modifier. Convention-Based (Data-Driven) RegistrationThe RegisterAssemblyTypes method provides syntax for declaratively specifying how a set of types is registered: Assembly controllers = // ... builder.RegisterAssemblyTypes(controllers) .Where(t => t.IsAssignableTo(typeof(IController)) .Named(t => "controller-" + t.Name.ToLower()); Other syntax elements for lifetime, ownership etc. are available through the same methods as for regular types. An overloaded version of As allows types to be mapped to the services they provide, and the AsImplementedInterfaces method supports the common convention of having components provide all interfaces they implement. Changes to Named ServicesNamed services now require type information as well, and this must match in both the registration and resolution calls: builder.RegisterType<Foo>().Named<IFoo>("foo");matches: container.Resolve<IFoo>("foo");but not: container.Resolve<IBar>("foo");This means that names can now be used with open generic registrations, among other things. XML Configuration ChangesSeparate Assembly for XML ConfigurationConfigurationSettingsReader and related XML configuration support has been moved to Autofac.Configuration.dll This will allow the XML configuration support to be significantly extended without bloating the core assembly. It is also a goal of Autofac 2 for the same core assembly (Autofac.dll) to run under both .NET and Silverlight. Moving the System.Configuration-dependent classes, which are not supported on Silverlight, out of the core assembly enables this. Syntax ChangesElement Names Changed
Instance Scope Value Changes
Instance Ownership Value Changes
Property Injection Value Changes Rather than 'none', 'all' and 'unset' the values are now 'yes' or 'no'. ASP.NET MVC Integration ChangesAutofac 1.4 and earlier 2.1 versions used AutofacControllerModule as a way of finding and registering ASP.NET MVC controllers with the container. In the Autofac 2.1 MVC integration, there is a new ContainerBuilder extension method called RegisterControllers. var builder = new ContainerBuilder();
// Was:
// builder.RegisterModule(new AutofacControllerModule(...))
builder.RegisterControllers(Assembly.GetExecutingAssembly());
_containerProvider = new ContainerProvider(builder.Build());
ControllerBuilder.Current.SetControllerFactory(
new AutofacControllerFactory(_containerProvider));The reason for the switch is that RegisterControllers is a thin wrapper around the new assembly scanner, and supports the same syntax as the rest of Autofac’s registration methods: builder.RegisterControllers(assembly)
.InjectProperties()
.OnActivated(e => Log.Information("Controller created: " + e.Instance));
| ||||||||||||||||||||||||||||||||||||||
The auto-gen'd factories are a great idea. I'm looking forward to implementing
Looks promising! Keep up the good work!
I've found that with the new API it is impossible for a Module to get a hold of the container it is acting on. I've spent about 8 hrs on this and still can't see how its possible. Maybe this was a desired feature but in the case of my custom module, I must be able to at some point provide one of my objects the container on which it resides I used to use the Configure method for this but this is no longer supported in V2.
@DamReev? thanks for the feedback! Components can get access to the container by depending on it, i.e. your object that needs the container can just include IComponentContext (or ILifetimeScope) in its constructor arguments.
Most of the time in Autofac there is a better alternative than working directly with the container - can you give more details of what your object needs to do with it?
This looks like a lot of good stuff. I have a question about Collection Support being included by default. What is the best practice for implementing a Composite pattern then? If I have the following classes:
FooImpl1? : IFoo {}
FooImpl2? : IFoo {}
CompositeFoo? : IFoo {
}How can I set things up so that FooImpl1? and FooImpl2? are injected into the CompositeFoo? constructor, and CompositeFoo? is registered as the IFoo implementation? Today I do that by using the MemberOf?<IEnumerable<IFoo>>. Will that still work and will that be the preferred method of wiring up a composite?
@andyalm The new collection support works as a fallback - if you use the member-of style of collection configuration it will override the default behavior, i.e., your example will still work. I'll make sure there is a test case to cover it, thanks very much for raising the point.
As of 2.1.6, "ContainerBuilder?.Build(IComponentRegistry componentRegistry)" has been marked as "internal" thus prevents us from making registrations inside of a resolved object (since there's no way to assign the "IContainer" generated by a call of ContainerBuilder?.Build(), which the builder is instantiated inside the resolved object, back to main container).
With the approach above, it's possible to do registration + initialization of modules (custom modules, not autofac modules) in one phase, otherwise breaking up the registration and initialization in 2 phases for all module is the only possible solution I can think of, which make the code less elegant.
Is there any other workaround to this problem? Or if it's not a good design, Is there any other better alternative with autofac?
@ywl633 thanks for raising the point - I've updated the page with "Adding to an Existing Container".
The method you're looking for is an extension IContainer.Configure().
Cheers, Nick
In Autofac 1.x: "Factory scoped components that are owned by the container are held using weak references". Why these refereces are strong in 2.x?
@yuri, the Autofac 1 behaviour is find 99% of the time, but it is a possible source of Heisenbugs - program behaviour could become incorrect because of GC timing. In Autofac 2, if you don't want references held, use .ExternallyOwned? when you register a component.
Judging by your other sample, you're in Windows Forms app, where I can see this being tricky. Try resolving forms with the Owned<T> type at the very root of the object graph, thus disposing Owned<T> will dispose the form and anything else it references.
If you have any ideas for making this more friendly please email autofac@googlegroups.com.
Thanks!
Nick, have no problem with disposing it manually. Just was caught by surprise that splash screen, which I know was disposed and application has no references to it after startup, was disposed again from container dispose at application end.
I believe it is important to clearly state such change in behavior on this page.
Thanks!
Done, thanks Yuri.
How can we use DynamicProxy? in V2? OnActivating? handler.Instance has only get property, so it is difficult to replace resolved instance with proxy instance.
Thanks!
@rikard Hi - there's an outstanding issue in the issue tracker (I believe called "update contrib") - getting DP integration back online is the #1 piece of work left. You should expect an update before too long that resolves this problem.
Thanks,
Nick
Looking good Nick!
One question, was there a reason the 'TryResolve?<T>( out T instance )' method was removed? I've simply added my own using an extension method without any difficulties, but was wondering if it was removed for some technical reason?
Cheers. Tyson.
Tyson, I've raised the missing overload as a bug. You'll be glad to hear that your Configure() issue has already been addressed in the trunk:
var scope = container.BeginLifetimeScope?(builder => {
});Needs a little more polish but will be available in the 2.1 RC.
Thanks for the detailed feedback!
Haha I already downloaded the latest source and noticed that Configure overload, thanks! I'll be sure to feedback any bugs, but seems to be working fine so far.
Much nicer way of doing it IMO by the way! Cleaner API and more visible. And also fits with my virtual method on my base classes that takes a ContainerBuilder? parameter for configuration. I dont event need to adapt it with a lambda, just pass the function delegate straight in! :D
Cheers. Tyson.
In 2.1.10.754 there doesn't seem to be an IContainer.Configure method. Am I missing something? It's certainly not part of RegistrationExtensions?. Should I be doing this instead?
module.Configure(container.ComponentRegistry?)
@joel, in 2.1.10 and onwards a constructed container is configured via BeginLifetimeScope?(builder => xxx).
The best way to configure any container is via the ContainerBuilder? that creates it, if that is possible in your scenario.
Cheers
@nicholas - So you're saying that in v2 there's no way to add registrations to an existing container? Instead, you can only create a new scope that has extra registrations in it? That seems like a step backwards to me, but maybe it will make more sense to me after I've finished rewriting a large amount of code to accommodate this change. In some future version of my software, when I have time to upgrade to Autofac 2.
I'm with joel. I need to be able to add registrations to an existing container without defining a child scope. Is there something I'm missing?
In v1, I like to use a registration variation which does not use a local variable for registrations. The syntax is:
public class FooModule? : BuilderModule? {
}
The class which enables this is:
public class BuilderModule? : ContainerBuilder?, IModule {
}
In v2, Build(IComponentRegistry) is internal, so this simple indirection won't work. At first glace, it looks like I could do:
public void Configure(IComponentRegistry componentRegistry) {
}
But I don't feel fully confident in that approach, as I don't know if the Registrations property includes registration source information.
How would I enable this in v2?
Hello,
I do not see the IComponentDescriptor. How do I get to BestKnownImplementationType? ?
@bogdanutz, this is now IComponentRegistration.Activator.LimitType?
@bwatts - override ContainerBuilder?.RegisterCallback? and save a collection of callbacks in BuilderModule?. Then, on IModule.Configure, execute each of these callbacks against the provided IComponentRegistry... Voila! :)
@nicholas.blumhardt - Very nice. Thanks for the tip!
I'm with @rob.eisenberg and @joel on the fact it "should be" easier to add new registration afterwards on the same container.
Thanks
ContainerBuilder?.Build(IContainer) is again public in Autofac 2, as ContainerBuilder?.Update().