Registering Collections in Autofac 2.1.10 RC - autofac

I am upgrading code from Autofac 1.4 to 2.1.10 Release Candidate.
My module previously performed registration like this:
builder.RegisterCollection<IExceptionHandler>()
.As<IEnumerable<IExceptionHandler>>()
.FactoryScoped();
builder.Register<AspNetExceptionHandler>()
.As<IExceptionHandler>()
.MemberOf<IEnumerable<IExceptionHandler>>()
.FactoryScoped();
Now, RegisterCollection has no parameterless overload. I don't care about assigning it a name. Assuming it's OK to just pass in null, my code looks like this in 2.1:
builder.RegisterCollection<IExceptionHandler>(null)
.As<IEnumerable<IExceptionHandler>>()
.InstancePerDependency();
builder.RegisterType<AspNetExceptionHandler>()
.As<IExceptionHandler>()
.MemberOf<IEnumerable<IExceptionHandler>>(null)
.InstancePerDependency();
However, when I compile, I get the following error regarding .MemberOf:
Using the generic method
'Autofac.RegistrationExtensions.MemberOf(Autofac.Builder.RegistrationBuilder,
string)' requires '3' type arguments
I tried putting in a collection name instead of null, just in case, and that had no effect.
What's the proper way to register collections in 2.1?

As I understand it, you just register a bunch of IExceptionHandler types, and then when you request an IEnumerable<IExceptionHandler> Autofac 2 will just take care of everything for you.
From the NewInV2 page:
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());

Related

Autofac: How do I register generic collections?

I have a couple of classes that take IList<IHero> in the constructor. I would like to register the generic collection List<IHero> in Autofac so that whenever Autofac needs to resolve a service that takes IList<IHero, it returns a new instance of List<IHero>. The code below compiles but I get a ton of error messages at run-time.
builder.RegisterType<List<IHero>>().As<IList<IHero>>();
My current workaround is as follows:
var printer = scope.Reseolve<IPrinter>(new TypedParameter(typeof(IList<IHero>), new List<IHero>();
var newEngine = scope.Resolve<IEngine>(new TypedParameter(typeof(IPrinter), printer));
Don't register collections yourself. Autofac handles collections for you.

Registering a type with both EnableClassInterceptors and WithParameter

I'm having an issue with Autofac where it seems like EnableClassInterceptors is interfering with my ability to use .WithParameter(...). When the constructor is being called on Service using the code below, someString is not being populated. Notes:
I've tried using ResolvedParameter instead, it does not help (note: my Resolved parameter still includes the name of the parameter when I tried that)
If I remove EnableClassInterceptors and InterceptedBy, the parameter does get populated properly. This, however, isn't a valid solution as I need the interceptors.
Re-ordering WithParameter, EnableClassInterceptors, and InterceptedBy does not help.
Looking at Type Interceptors, specifically the "Class Interceptors and UsingConstructor" section, on docs.autofac.org, it mentions that using EnableClassInterceptors will cause ConstructUsing to fail. I think something similar might be happening with my scenario below.
Snippet of my registration code looks like this:
var builder = new ContainerBuilder();
builder.RegisterType<Dependency>.As<IDependency>.InstancePerLifetimeScope();
builder.RegisterType<Service>()
.As<IService>()
.WithParameter(new NamedParameter("someString", "TEST"))
.EnableClassInterceptors()
.InterceptedBy(typeof(LogExceptionsInterceptor));
Service's constructor looks something like this:
public class Service : IService
{
public Service(IDependency dependency, string someString)
{
if(dependency == null)
throw ArgumentNullException(nameof(dependency));
if(someString == null)
//**throws here**
throw ArgumentNullException(nameof(someString));
}
}
[Guess] What I'm thinking is happening is that when EnableClassInterceptors is called, a proxy class is generated with a constructor that works on top of the existing one, but the parameter names do not copy over into the proxy class/constructor.
Is this a problem? Is there a way to form the registration that allows both WithParameter and EnableClassInterceptors to be used together? Is it a bug in Autofac?
Your guess is correct: the generated proxy class does not keep the constructor parameter names.
Currently there is no way to influence this in DynamicProxy so this is not a bug of Autofac (although this edge case currently not documented on the Autofac documentation website).
This is how your original Service class's parameters look like:
typeof(Service).GetConstructors()[0].GetParameters()
{System.Reflection.ParameterInfo[2]}
[0]: {ConsoleApplication10.IDependency dependency}
[1]: {System.String someString}
But the generated proxy does not keep the names:
GetType().GetConstructors()[0].GetParameters()
{System.Reflection.ParameterInfo[3]}
[0]: {Castle.DynamicProxy.IInterceptor[] }
[1]: {ConsoleApplication10.IDependency }
[2]: {System.String }
So you have two not very robust options to workaround this limitation with WithParameter:
use the TypedParamter with string as the type:
.WithParameter(new TypedParameter(typeof(string), "TEST"))
However if you have multiple paramters with the same type this won't work
use the PositionalParameter in this case you need to add 1 if the type is proxied
.WithParameter(new PositionalParameter(2, "TEST"))
Another options would be to don't use a primitive string type but create a wrapper e.g. MyServiceParameter or create another service which can provide these string configuration values to your other services.

StructureMap Specifying Explicit Constructor Arguments

I'm working on legacy code.
I have different methods of the same class that pass different arguments to the constructor of a dependency. I'm trying to get some basic IoC usage introduced. Right now I have StructureMap passing my arguments like this:
var thing = ObjectFactory.GetInstance<IThingInterface>(new ExplicitArguments(
new Dictionary<string, object> {
{ "constructorArgA", notShown },
{ "constructorArgB", redacted.Property } }));
Where the actual properties passed for constructorArgA and B change depending on where I am.
Instead of "constructorArgA" is there a way to configure this via actual types, like you can do when configuring the objectFactory, like:
x.For<IHidden>().Use<RealType>()
.Ctor<IConfig>().Is(new Func<IContext, IConfig>(
(context) => someMethodToGetIConfig()));
If I were writing this from scratch I'd probably structure the dependencies a bit different to avoid this, but that's not an option for me right now.
This is something of a classic/common question with DI Containers.
My first choice would be to create a "manual" abstract factory to create IThingInterface, and then use Structuremap to inject IThingInterfaceFactory where it is needed. By manual factory, I mean a class the calls new ThingInterface() and returns it. If you do it this way, your implementation will no longer be container-managed, and if it has dependencies, they would no longer be provided by the container (may or may not be a problem for you).
Second choice would be to create an abstract factory that actually uses/wraps the container. So basically your first code snippet but wrapped in a factory class where the Create() method takes your parameters. This has the advantage of everything (including your implementation and its dependencies) being container-managed, but the disadvantage of referencing your container directly (which is not a best practice--see Article on Composition Roots).
You could also do setter injection, but I would personally consider it a last resort.
Castle Windsor has a good solution to this problem built in (Typed Factory Facility). Not sure if switching containers in an option, but you might consider it.

Is there any way to create a fake from a System.Type object in FakeItEasy?

Is there any way to create a fake from a System.Type object in FakeItEasy? Similar to:
var instance = A.Fake(type);
I try to write a fake container for AutoFac that automatically return fakes for all resolved types. I have looked in the code for FakeItEasy and all methods that support this is behind internal classes but I have found the interface IFakeObjectContainer that looks pretty interesting, but the implementations still need registration of objects that is the thing that I want to come around.
As of FakeItEasy 2.1.0 (but do consider upgrading to the latest release for more features and better bugfixes), you can create a fake from a Type like so:
using FakeItEasy.Sdk;
…
object fake = Create.Fake(type);
If you must use an earlier release, you could use some reflection based approach to create a method info for the A.Fake() method. (since it's about auto mocking this shouldn't be a problem really).
This is best done using a registration handler. You should look into how AutofacContrib.Moq implements its MoqRegistrationHandler. You'll see that it is actually using the generic method MockRepository.Create to make fake instances. Creating a similar handler for FakeItEasy should be quite simple.

How to carry out custom initialisation with autofac

I'm adding autofac to an existing project and some of the service implementations require their Initialize method to be called and passed configuration information. Currently I'm using the code:
builder.Register(context =>
{
var service =
new SqlTaxRateProvider(context.Resolve<IUserProvider>());
service.Initialize(config);
return service;
}
).As<ITaxService>()
.SingleInstance();
which works but I'm still creating the object myself which is what I'm trying to get away from this and allow autofac to handle it for me. Is it possible to configure a post create operation that would carry out the custom initialisation?
To give you an idea of what I'm after ideally this would be the code:
builder.RegisterType<SqlTaxRateProvider>()
.As<ITaxService>()
.OnCreated(service=> service.Initialize(config))
.SingleInstance();
Update:
I am using Autofac-2.1.10.754-NET35
.OnActivating(e => e.Instance.Initialize(...))
should do the trick.
You might also investigate the Startable module (see the Startable entry in the Autofac wiki).
Mark's suggestion to do initialisation in the constructor is also a good one. In that case use
.WithParameter(new NamedParameter("config", config))
to merge the config parameter in with the other constructor dependencies.