|
| 1 | +# FAQ - Migration from Autofac |
| 2 | + |
| 3 | + |
| 4 | +- [FAQ - Migration from Autofac](#faq---migration-from-autofac) |
| 5 | + - [Autofac version](#autofac-version) |
| 6 | + - [Separate build stage](#separate-build-stage) |
| 7 | + - [Registration order](#registration-order) |
| 8 | + - [Auto-activated components](#auto-activated-components) |
| 9 | + - [IStartable](#istartable) |
| 10 | + - [Register AsImplementedInterfaces](#register-asimplementedinterfaces) |
| 11 | + - [Owned instances](#owned-instances) |
| 12 | + - [Using constructor with most resolvable parameters](#using-constructor-with-most-resolvable-parameters) |
| 13 | + - [Modules](#modules) |
| 14 | + |
| 15 | + |
| 16 | +## Autofac version |
| 17 | + |
| 18 | +Relevant to __Autofac v3.5.2__ |
| 19 | + |
| 20 | + |
| 21 | +## Separate build stage |
| 22 | + |
| 23 | +By default DryIoc does not have separate build stage - you may resolve and then add new registrations at any time. |
| 24 | + |
| 25 | +Autofac on the other hand has `ContainerBuilder` which accumulate registrations and then produce `Container` to resolve from. |
| 26 | + |
| 27 | +The __pros__ of DryIoc approach is to provide more flexibility and less API obstacles in using the container, especially when you need register later after resolve. |
| 28 | + |
| 29 | +The __cons__ may be openness for later/external registrations that may override your initial setup. So you need to be careful when sharing container with plugins or other third-parties. |
| 30 | + |
| 31 | +Other __cons__ may be to be aware when container is built, so to hook some additional logic to this event: e.g. [Auto-activated components](FaqAutofacMigration#markdown-header-auto-activated-). |
| 32 | + |
| 33 | +__The question is:__ Does the Autofac provides the first __cons__ by guarding container from later changes? It seems no, because you may create new `ContainerBuilder`, put new registrations into it, and then update/mutate initial container. |
| 34 | + |
| 35 | +On the other hand DryIoc provides the way to produce resolution-only container to share with third-parties: |
| 36 | +```cs |
| 37 | + var resolutionOnlyContainer = container.WithNoMoreRegistrationAllowed(); |
| 38 | + resolutionOnlyContainer.Register<A>(); // will throw ContainerException |
| 39 | +``` |
| 40 | + |
| 41 | +or you may ignore later registrations: |
| 42 | +```cs |
| 43 | + var resolutionOnlyContainer = container.WithNoMoreRegistrationAllowed(ignoreInsteadOfThrow: true); |
| 44 | + resolutionOnlyContainer.Register<A>(); // ignores registration - does nothing |
| 45 | +``` |
| 46 | + |
| 47 | + |
| 48 | +## Registration order |
| 49 | + |
| 50 | +Sometimes you need to get container registrations in determined order. |
| 51 | +DryIoc has the way to get service registrations without actually resolving them: |
| 52 | +```cs |
| 53 | + IEnumerable<ServiceRegistrationInfo> registrations = container.GetServiceRegistrations(); |
| 54 | + foreach (var r in registrations) { /*...*/ } |
| 55 | +``` |
| 56 | + |
| 57 | +`ServiceRegistrationInfo` contains: |
| 58 | + |
| 59 | +- `ServiceType` |
| 60 | +- `OptionalServiceKey` is `null` for single default registration, arbitrary object for keyed registration, and `DefaultKey` object for multiple defaults. |
| 61 | +- `Factory` holds implementation details (__may be the same when single implementation registered with multiple services__). Includes: |
| 62 | + - `ImplementationType`, may be null |
| 63 | + - `Reuse`, e.g. `SingletonReuse` |
| 64 | + - `Setup`, e.g. `Setup.Metadata` |
| 65 | +- `FactoryRegistrationOrder` is relative number identifying the order of Factory registration (__may be the same for multiple services__) |
| 66 | + |
| 67 | +By default the return order is undetermined (internally ordered by Type hash code + Service Key hash code). The solution is to use LINQ `OrderBy` method: |
| 68 | +```cs |
| 69 | + var registrations = container.GetServiceRegistrations() |
| 70 | + .OrderBy(r => r.FactoryRegistrationOrder); |
| 71 | + |
| 72 | + foreach (var r in registrations) { /*...*/ } |
| 73 | +``` |
| 74 | + |
| 75 | + |
| 76 | +## Auto-activated components |
| 77 | + |
| 78 | +[This Autofac feature](http://docs.autofac.org/en/latest/lifetime/startup.html#auto-activated-components) |
| 79 | +allows to automatically resolve and create specific service when Container is built. DryIoc does not have separate build stage. |
| 80 | +That means at any time you can get service registrations, filter specific services and resolve them to activate. |
| 81 | +For instance, let's mark activate-able services with Metadata and then create them as following: |
| 82 | +```cs |
| 83 | + // Defining Metadata in extensible way: |
| 84 | + public abstract class Metadata |
| 85 | + { |
| 86 | + public class AutoActivated : Metadata |
| 87 | + { |
| 88 | + public static readonly AutoActivated It = new AutoActivated(); |
| 89 | + } |
| 90 | + } |
| 91 | + |
| 92 | + // Configure registrations: |
| 93 | + container.Register<ISpecific, Foo>(setup: Setup.With(Metadata.AutoActivated.It)); |
| 94 | + container.Register<INormal, Bar>(); |
| 95 | + |
| 96 | + // Resolve to activate: |
| 97 | + var ignored = container.GetServiceRegistrations() |
| 98 | + .Where(r => r.Factory.Setup.Metadata is Metadata.AutoActivated) |
| 99 | + .OrderBy(r => r.FactoryRegistrationOrder) |
| 100 | + .GroupBy(r => r.FactoryRegistrationOrder, (f, r) => r.First()) |
| 101 | + .Select(r => container.Resolve(r.ServiceType, r.OptionalServiceKey)); |
| 102 | +``` |
| 103 | + |
| 104 | +__Note:__ `GroupBy` required to activate single implementation (Factory) only once, because single implementation may be registered with multiple services. |
| 105 | + |
| 106 | +Metadata provides generic way to filter services. Alternatively you may go with convention, for instance auto-activate Singletons: `.Where(r => r.Factory.Reuse is SingletonReuse)`. |
| 107 | + |
| 108 | + |
| 109 | +## IStartable |
| 110 | + |
| 111 | +[Autofac Startable components](http://docs.autofac.org/en/latest/lifetime/startup.html#startable-components) are the same __auto-activated__ services with additional `Start` action executed on activation (and never on normal resolve). It is pretty easy to modify above example to filter `IStartable` interface instead of (or in addition to) Metadata: |
| 112 | +```cs |
| 113 | + var ignored = container.GetServiceRegistrations() |
| 114 | + .Where(r => (r.Factory.ImplementationType ?? r.ServiceType).IsAssignableTo(typeof(IStartable))) |
| 115 | + .OrderBy(r => r.FactoryRegistrationOrder) |
| 116 | + .GroupBy(r => r.FactoryRegistrationOrder, (f, r) => r.First()) |
| 117 | + .Select(r => ((IStartable)container.Resolve(r.ServiceType, r.OptionalServiceKey)).Start()); |
| 118 | +``` |
| 119 | + |
| 120 | + |
| 121 | +## Register AsImplementedInterfaces |
| 122 | + |
| 123 | +Autofac has conventional method to register type implemented interfaces as service types. |
| 124 | + |
| 125 | +DryIoc provides a set of `RegisterMany` methods to register multiple service types for possibly multiple implementation types or assemblies. To achieve Autofac behavior you need to say: |
| 126 | +```cs |
| 127 | + container.RegisterMany<FooBar>(serviceTypeCondition: type => type.IsInterface); |
| 128 | +``` |
| 129 | + |
| 130 | +Condition will keep interface service types and skip _public_ base types and source type itself. |
| 131 | + |
| 132 | +__Note:__ General purpose interfaces like `IDisposable` will not be registered in any case. |
| 133 | + |
| 134 | +To register _non-public_ service types modify previous example: |
| 135 | +```cs |
| 136 | + container.RegisterMany<FooBar>( |
| 137 | + nonPublicServiceTypes: true, |
| 138 | + serviceTypeCondition: type => type.IsInterface); |
| 139 | +``` |
| 140 | + |
| 141 | + |
| 142 | +## Owned instances |
| 143 | + |
| 144 | +[The feature](http://docs.autofac.org/en/latest/advanced/owned-instances.html) implicitly opens scope for `Owned<TService>`. |
| 145 | +Disposing owned service will dispose the scope and also dispose its disposable dependencies. |
| 146 | +Using `Owned` is closely related to the fact that Autofac tracks `IDisposable` services even if they are Transient (`InstancePerDependency`). |
| 147 | + |
| 148 | +Meanwhile DryIoc [does not track disposable transients by default](ReuseAndScopes#markdown-header-disposabletransient). |
| 149 | +Instead DryIoc will throw exception on registering disposable transient. |
| 150 | + |
| 151 | +To allow such registrations you need to explicitly use `allowDisposableTransient` setup option |
| 152 | +or use the container rule `WithoutThrowOnRegisteringDisposableTransient()`. |
| 153 | +This option is similar to Autofac registration as `ExternallyOwned()`. |
| 154 | +That way you do not need to use `Owned` to control disposal. |
| 155 | + |
| 156 | +To match default Autofac disposable tracking you need to use DryIoc `trackDisposableTransinet` registration setup option |
| 157 | +or specify the container rule `WithTrackingDisposableTransient()`. |
| 158 | +Then instead of `Owned` in DryIoc, just wrap dependency in `Func`. This way the tracking will be prevented. |
| 159 | +Using `Func` has a slight advantage over `Owned` because `Func` is not container specific type and does not |
| 160 | +require DryIoc reference. |
| 161 | + |
| 162 | +If you want the Autofac [InstancePerOwned](http://docs.autofac.org/en/stable/lifetime/instance-scope.html#instance-per-owned) |
| 163 | +to [reuse service in object sub-graph](ReuseAndScopes#markdown-header-reuseinresolutionscopeof) use: |
| 164 | +```cs |
| 165 | + container.Register<SomeService>(setup: Setup.With(openResolutionScope: true)); |
| 166 | +``` |
| 167 | + |
| 168 | +To dispose dependencies instead of `Owned` just define `IDisposable` parameter and it will be automatically injected with current Resolution Scope. |
| 169 | + |
| 170 | +Similar setups in Autofac and DryIoc: |
| 171 | + |
| 172 | +In Autofac: |
| 173 | +```cs |
| 174 | + class SomeService |
| 175 | + { |
| 176 | + public SomeService(Dependency d) {} |
| 177 | + } |
| 178 | + |
| 179 | + class SomeClient : IDisposable |
| 180 | + { |
| 181 | + // Owned will open scope and provide access for its disposal |
| 182 | + public SomeClient(Owned<SomeService> owned) { _owned = owned; } |
| 183 | + |
| 184 | + // Will dispose scoped dependency and nested dependencies |
| 185 | + public void Dispose() { _owned.Dispose(); } |
| 186 | + } |
| 187 | + |
| 188 | + // configure: |
| 189 | + builder.RegisterType<NestedDependency>().InstancePerOwned<SomeService>(); |
| 190 | + builder.RegisterType<Dependency>().InstancePerOwned<SomeService>(); |
| 191 | + builder.RegisterType<SomeService>(); |
| 192 | + builder.RegisterType<SomeClient>(); |
| 193 | +``` |
| 194 | + |
| 195 | +In DryIoc: |
| 196 | + |
| 197 | +```cs |
| 198 | + class SomeService |
| 199 | + { |
| 200 | + public SomeService(Dependency d) {} |
| 201 | + } |
| 202 | + |
| 203 | + class SomeClient : IDisposable |
| 204 | + { |
| 205 | + // No need in Owned wrapper - Scope will be injected automatically |
| 206 | + public SomeClient(SomeService s, IDisposable scope) { _scope = scope; } |
| 207 | + |
| 208 | + // Will dispose scoped dependency and nested dependencies |
| 209 | + public void Dispose() { _scope.Dispose(); } |
| 210 | + } |
| 211 | + |
| 212 | + // configure: |
| 213 | + container.Register<NestedDependency>(Reuse.InResolutionScopeOf<SomeService>()); |
| 214 | + container.Register<Dependency>(Reuse.InResolutionScopeOf<SomeService>()); |
| 215 | + container.Register<SomeService>(setup: Setup.With(openResolutionScope: true)); |
| 216 | + container.Register<SomeClient>(); |
| 217 | +``` |
| 218 | + |
| 219 | + |
| 220 | +## Using constructor with most resolvable parameters |
| 221 | + |
| 222 | +Autofac by default will use constructor with most resolvable parameters. So the `A` will be successfully resolved in example below: |
| 223 | +```cs |
| 224 | + public class B {} |
| 225 | + public class C {} |
| 226 | + |
| 227 | + public class A |
| 228 | + { |
| 229 | + public A(B b) {} |
| 230 | + public A(C c) {} |
| 231 | + } |
| 232 | + |
| 233 | + // configure: |
| 234 | + var builder = new ContainerBuilder(); |
| 235 | + builder.RegisterType<A>(); |
| 236 | + builder.RegisterType<B>(); |
| 237 | + // C is not registered. |
| 238 | + var container = builder.Build(); |
| 239 | + |
| 240 | + container.Resolve<A>(); |
| 241 | +``` |
| 242 | + |
| 243 | +DryIoc on the other hand will expect type to have a single constructor. |
| 244 | +In case of multiple constructors available DryIoc will throw `ContainerException` with corresponding message. |
| 245 | +This behavior was selected as default, because it is more deterministic - you always know the way of instantiating your service. |
| 246 | + |
| 247 | +But you may enable the Autofac behavior in DryIoc: |
| 248 | + |
| 249 | +- per whole Container: |
| 250 | +```cs |
| 251 | + var container = new Container(rules => |
| 252 | + rules.With(FactoryMethod.ConstructorWithResolvableArguments)); |
| 253 | +``` |
| 254 | + |
| 255 | +- per individual registration: |
| 256 | + |
| 257 | +```cs |
| 258 | + var container = new Container(); |
| 259 | + container.Register<A>(made: FactoryMethod.ConstructorWithResolvableArguments); |
| 260 | +``` |
| 261 | + |
| 262 | +If you enable the feature per Container but provide some parameter specs on registration level, they will completely override the Container level setting: |
| 263 | +```cs |
| 264 | + public class B {} |
| 265 | + public class C {} |
| 266 | + |
| 267 | + public class A |
| 268 | + { |
| 269 | + public bool IsCreatedWithB { get; private set; } |
| 270 | + public bool IsCreatedWithC { get; private set; } |
| 271 | + |
| 272 | + public A(B b) { IsCreatedWithB = true; } |
| 273 | + public A(C c) { IsCreatedWithC = true; } |
| 274 | + } |
| 275 | + |
| 276 | + // configure: |
| 277 | + var container = new Container(rules => |
| 278 | + rules.With(FactoryMethod.ConstructorWithResolvableArguments)); |
| 279 | + |
| 280 | + container.Register(Made.Of(() => new A(Arg.Of<C>(IfUnresolved.ReturnDefault)))); |
| 281 | + container.Register<B>(); |
| 282 | + // No C registered |
| 283 | + var a = container.Resolve<A>(); |
| 284 | + Assert.IsTrue(a.IsCreatedWithC); // Because registration level setting override container's |
| 285 | +``` |
| 286 | + |
| 287 | + |
| 288 | +## Modules |
| 289 | + |
| 290 | +Here the docs describing [Autofac Modules feature](http://docs.autofac.org/en/latest/configuration/modules.html). |
| 291 | + |
| 292 | +They major responsibility of Module to be the unit of configuration and registration. Actually the Module plays the role of [Facade](https://en.wikipedia.org/wiki/Facade_pattern) to hide some related registrations behind. |
| 293 | + |
| 294 | +Here is the how defining and using the module looks like in Autofac: |
| 295 | +```cs |
| 296 | + // Here is the AModule |
| 297 | + public class AutofacModule : Module |
| 298 | + { |
| 299 | + protected override void Load(ContainerBuilder builder) |
| 300 | + { |
| 301 | + builder.RegisterType<A>().SingleInstance(); |
| 302 | + } |
| 303 | + } |
| 304 | + |
| 305 | + // And using it |
| 306 | + var builder = new ContainerBuilder(); |
| 307 | + |
| 308 | + builder.RegisterModule<AModule>(); // registers module with some registrations inside |
| 309 | + builder.RegisterType<B>(); |
| 310 | + |
| 311 | + var container = builder.Build(); |
| 312 | + |
| 313 | + var a = container.Resolve<A>(); |
| 314 | + Assert.IsInstanceOf<B>(a.B); |
| 315 | +``` |
| 316 | + |
| 317 | +And here the equivalent in DryIoc without use of any additional abstractions except for `IModule` interface for conformity: |
| 318 | +```cs |
| 319 | + public interface IModule |
| 320 | + { |
| 321 | + // Here we are using registration role of DryIoc Container for the builder |
| 322 | + void Load(IRegistrator builder); |
| 323 | + } |
| 324 | + |
| 325 | + public class DryIocModule : IModule |
| 326 | + { |
| 327 | + public void Load(IRegistrator builder) |
| 328 | + { |
| 329 | + builder.Register<BB>(Reuse.Singleton); |
| 330 | + } |
| 331 | + } |
| 332 | + |
| 333 | + // And the use is straightforward |
| 334 | + var container = new Container(); |
| 335 | + |
| 336 | + container.RegisterMany<AModule>(); |
| 337 | + container.Register<B>(); |
| 338 | + |
| 339 | + // Resolve all registered modules and call Load on them |
| 340 | + foreach (var module in container.ResolveMany<IModule>()) |
| 341 | + module.Load(container); |
| 342 | + |
| 343 | + var a = container.Resolve<A>(); |
| 344 | + Assert.IsInstanceOf<B>(a.B); |
| 345 | +``` |
0 commit comments