How do you provide specific registrations in Autofac as parameters to other registrations? - autofac

Can you plase provide some guidance on how to pass some references to other registrations ?
//registration of 1st http client
builder.RegisterType<HttpClient>()
//.Keyed<HttpMessageInvoker>("authHttpClient")
.WithProperties(new[] { new NamedPropertyParameter("PooledConnectionLifetime", 2 })
.WithProperties(new[] { new NamedPropertyParameter("BaseAddress", "whatever"))})
.SingleInstance();
//registration of 2nd httpclient
builder.RegisterType<HttpClient>()
.Keyed<HttpMessageInvoker>("dynamicsHttpClient")
.WithProperties(new[] { new NamedPropertyParameter("PooledConnectionLifetime", 20) })
.WithProperties(new[] { new NamedPropertyParameter("BaseAddress", "other something" })
.SingleInstance();
// **I need to do the registration of type and pass the 1st httpClient registration**
builder.RegisterType<DynamicsAuthApiGateway>()
.As<IDynamicsAuthApiGateway>()
// **I need to do the registration of type pass 2nd instance of httpClient registration**
builder.RegisterType<DynamicsApiGateway>()
.As<IDynamicsApiGateway>()
.SingleInstance();
//Method ctor's
//DynamicsAuthApiGateway(**HttpClient client**, DynamicsAuthApiGatewaySettings apiGatewaySettings)
//DynamicsApiGateway(**HttpClient client**, Func<HttpResponseMessage, Task> errorHandler = null)
Can you help on how to achieve that ?
Any help would be appreciated ?
Thanks,
ME

You're on the right track with using keyed services. Docs here for details and examples.
The easiest way will be to use the Autofac.Features.AttributeFilters.KeyFilterAttribute to just mark up the constructor on the consuming class.
First, update the consumers. Here's an example.
public class DynamicsAuthApiGateway : IDynamicsAuthApiGateway
{
// KeyFilterAttribute on the parameter
public DynamicsAuthApiGateway([KeyFilter("authHttpClient")] HttpClient client)
{
// ...
}
}
Then register the keyed clients and enable the attribute filtering on the consumers.
// Don't forget
// using Autofac.Features.AttributeFilters;
// at the top... then:
// Register the HttpClient instances with keys
// and be sure the key is on the same type/interface
// as you see in the constructor of the consumer.
builder.RegisterType<HttpClient>()
.Keyed<HttpClient>("authHttpClient")
.SingleInstance();
builder.RegisterType<HttpClient>()
.Keyed<HttpClient>("dynamicsHttpClient")
.SingleInstance();
// Register the consumers and enable attribute filters
builder.RegisterType<DynamicsAuthApiGateway>()
.As<IDynamicsAuthApiGateway>()
.WithAttributeFiltering();
builder.RegisterType<DynamicsApiGateway>()
.As<IDynamicsApiGateway>()
.SingleInstance()
.WithAttributeFiltering();
You have to opt into attribute filtering because it's extra perf overhead if you don't need it. If you don't put the WithAttributeFiltering() part, the filtering won't happen.
If you don't want that attribute in your class (e.g., you want to avoid tying Autofac into things) then it's a little harder - you'll need to use ResolvedParameter (docs here).
That's going to look more like this (I'm kinda writing it off the top of my head, so if there's a typo or something, you've been warned, but it should be basically)...
// Register the HttpClient instances with keys
// and be sure the key is on the same type/interface
// as you see in the constructor of the consumer.
builder.RegisterType<HttpClient>()
.Keyed<HttpClient>("authHttpClient")
.SingleInstance();
// Register the consumers using resolved parameters.
builder.RegisterType<DynamicsAuthApiGateway>()
.As<IDynamicsAuthApiGateway>()
.WithParameter(
new ResolvedParameter(
(pi, ctx) => pi.ParameterType == typeof(HttpClient),
(pi, ctx) => ctx.ResolveKeyed<HttpClient>("authHttpClient")));
It's not quite as clean but that keeps the Autofac references out of the consumers (if that matters). If you use that a lot, you could write your own extension method to make it easier. I'll leave that as an exercise for the reader.

Related

How does the logging module for Autofac and NLog work?

I am still fairly new to Autofac and Nlog and I need some help in understanding what is taking place in my Autofac LoggingModule for Nlog. It works as expected thanks to following the injecting-nlog-with-autofacs-registergeneric. But rather than just copy paste, I would like to make sure I understand what is occurring in each method (Load & AttachToComponentRegistration). If you could review my thoughts and further clarify anything I have incorrect (quite a bit I am sure), I would greatly appreciate it. Thank you in advance!
Database Target using Nlog
Dependency Injection using Autofac
ASP.NET MVC web app for learning
Dvd Libary app (DvdAdd, DvdEdit, DvdDelete, DvdList)
LoggingModule
public class LoggingModule : Module
{
protected override void Load(ContainerBuilder builder)
{
builder
.Register((c, p) => new LogService(p.TypedAs<Type>()))
.AsImplementedInterfaces();
}
protected override void AttachToComponentRegistration(IComponentRegistry componentRegistry, IComponentRegistration registration)
{
registration.Preparing +=
(sender, args) =>
{
var forType = args.Component.Activator.LimitType;
var logParameter = new ResolvedParameter(
(p, c) => p.ParameterType == typeof(ILog),
(p, c) => c.Resolve<ILog>(TypedParameter.From(forType)));
args.Parameters = args.Parameters.Union(new[] { logParameter });
};
}
}
My understanding of the code within Load()
c - The parameter c, provided to the expression, is the component context(an IComponentContext object) in which the component is being created. The context in which a service can be accessed or a component's dependencies resolved.
p - An IEnumerable with the incoming parameter set
AsImplementedInterfaces - Autofac allows its users to register the types explicitly or implicitly. While "As" is used for explicit registrations, "AsImplementedInterfaces" and "AsSelf" are used for implicit ones. In other words, the container automatically registers the implementation against all the interfaces it implements.
Thoughts: The Load method code registers a new LogService class (which represents "c") with the type of logger (which represents "p") as the constructor parameter for the LogService class
Questions:
Are my thoughts above correct?
Should it be SingleInstance or should it / will it only live as long as the calling classes scope? (I am thinking about my Unit Of Work)
My understanding of the code within AttachToComponentRegistration()
AttachToComponentRegistration method - Override to attach module-specific functionality to a component registration.
AttachToComponentRegistration Parameters:
IComponentRegistry componentRegistry - Provides component registrations according to the services they provide.
IComponentRegistration registration - Describes a logical component within the container.
registration.Preparing - Fired when a new instance is required. The instance can be provided in order to skip the regular activator, by setting the Instance property in the provided event arguments.
var forType = args.Component.Activator.LimitType;
args = Autofac.Core.PreparingEventArgs - Fired before the activation process to allow parameters to be changed or an alternative instance to be provided.
Component = PreparingEventArgs.Component Property - Gets the component providing the instance being activated
Activator = IComponentRegistration.Activator Property - Gets the activator used to create instances.
LimitType = IInstanceActivator.LimitType Property - Gets the most specific type that the component instances are known to be castable to.
Thoughts on forType - As I understand it, this variable holds the Name and FullName of the calling class from where the logging service is being called?
forType Debugger Image
Questions:
Are my thoughts forType correct?
var logParameter = new ResolvedParameter(
(p, c) => p.ParameterType == typeof(ILog),
(p, c) => c.Resolve<ILog>(TypedParameter.From(forType)));
ResolvedParameter - can be used as a way to supply values dynamically retrieved from the container,
e.g. by resolving a service by name.
Thoughts on logParameter - This is where I start to get lost. So does, it check that the Parameter is of Type ILog and if so it will then resolve it with the constructor parameter and pass in forType variable?
Questions:
Are my thoughts on logParameter above correct?
args.Parameters = args.Parameters.Union(new[] { logParameter });
args.Parameters = PreparingEventArgs.Parameters Property - Gets or sets the parameters supplied to the activator.
args.Parameters.Union = Produces the set union of two sequences by using the default equality comparer. Returns an System.Collections.Generic.IEnumerable`1 that contains the elements from both input sequences, excluding duplicates.
Thoughts on args.Parameters - I really do not know at this point other than to guess that it returns a collection of Parameters and removes duplicates?
Questions:
Could you help talk me through what is going on in args.Parameters?
logParameter Debugger Image
Nlog Database Table Image
LogService class
public class LogService : ILog
{
private readonly ILogger _log;
public LogService(Type type)
{
_log = LogManager.GetLogger(type.FullName);
}
public void Debug(string message, params object[] args)
{
Log(LogLevel.Debug, message, args);
}
public void Info(string message, params object[] args)
{
Log(LogLevel.Info, message, args);
}
public void Warn(string message, params object[] args)
{
Log(LogLevel.Warn, message, args);
}
public void Error(string message, params object[] args)
{
Log(LogLevel.Error, message, args);
}
public void Error(Exception ex)
{
Log(LogLevel.Error, null, null, ex);
}
public void Error(Exception ex, string message, params object[] args)
{
Log(LogLevel.Error, message, args, ex);
}
public void Fatal(Exception ex, string message, params object[] args)
{
Log(LogLevel.Fatal, message, args, ex);
}
private void Log(LogLevel level, string message, object[] args)
{
_log.Log(typeof(LogService), new LogEventInfo(level, _log.Name, null, message, args));
}
private void Log(LogLevel level, string message, object[] args, Exception ex)
{
_log.Log(typeof(LogService), new LogEventInfo(level, _log.Name, null, message, args, ex));
}
}
ILog interface
public interface ILog
{
void Debug(string message, params object[] args);
void Info(string message, params object[] args);
void Warn(string message, params object[] args);
void Error(string message, params object[] args);
void Error(Exception ex);
void Error(Exception ex, string message, params object[] args);
void Fatal(Exception ex, string message, params object[] args);
}
There's a lot to unpack here. You're not really asking for an answer to a specific question so much as a code walkthrough and explanation of an existing solution that works, so I might suggest posting to StackExchange Code Review if you need much more than what I'm going to give you here. Not trying to be unhelpful, but, like, if your question is, "Is my thinking right?" and the answer is "sort of," there's a lot of discussion on each individual point to explain why "sort of" is the answer (or "no," or "yes," as the case may be). It can turn into a lengthy answer, followed up by additional questions for clarification, which require yet additional answers... and StackOverflow isn't really a discussion forum capable of those sorts of things.
[i.e., I'll take probably an hour and write up an answer here... but I can't promise I'll actually be back to follow up on anything because there are other questions to answer and other things I need to allocate time to. StackOverflow is really more about "How do I...?" or other things that have a single, reasonably concrete answer.]
First, I recommend diving in yourself with a debugger on some breakpoints to actually see what's going on. For example, you asked what's in LimitType in one area - you could pretty easily answer that one by just sticking a breakpoint on that line and looking at the value. This will be a good way to follow up for additional clarification yourself - breakpoints for the win.
Second, I recommend spending some time with the Autofac docs. There's a lot of documentation out there that can answer questions.
The NLog module here appears to be based on the log4net module in the documentation which has a bit more explanation of what's going on.
There's an explanation of parameters (like TypedParameter) and how they're used.
Given the docs can round out some things that may not be clear, rather than try to address each "are my thoughts correct" item, let me just heavily annotate the module and hope that clarifies things.
// General module documentation is here:
// https://autofac.readthedocs.io/en/latest/configuration/modules.html
public class LoggingModule : Module
{
// Load basically registers types with the container just like
// if you were doing it yourself on the ContainerBuilder. It's
// just a nice way of packaging up a set of registrations so
// they're not all in your program's "Main" method or whatever.
protected override void Load(ContainerBuilder builder)
{
// This is a lambda registration. Docs here:
// https://autofac.readthedocs.io/en/latest/register/registration.html#lambda-expression-components
// This one uses both the component context (c) and the incoming
// set of parameters (p). In this lambda, the parameters are NOT the set of constructor
// parameters that Autofac has resolved - they're ONLY things that
// were MANUALLY specified. In this case, it's assuming a TypedParameter
// with a System.Type value is being provided manually. It's not going
// to try resolving that value from the container. This is going hand-in-hand
// with the logParameter you see in AttachToComponentRegistration.
// Parameter docs are here:
// https://autofac.readthedocs.io/en/latest/resolve/parameters.html
// In general if you resolve something that has both manually specified parameters
// and things that can be resolved by Autofac, the manually specified parameters
// will take precedence. However, in this lambda it's very specifically looking
// for a manually specified parameter.
// You'll want to keep this as a default InstancePerDependency because you probably
// want this to live as long as the thing using it and no longer. Likely
// NLog already has object pooling and caching built in so this isn't as
// expensive as you think, but I'm no NLog expert. log4net does handle
// that for you.
builder
.Register((c, p) => new LogService(p.TypedAs<Type>()))
.AsImplementedInterfaces();
}
// This method attaches a behavior (in this case, an event handler) to every
// component registered in the container. Think of it as a way to run a sort
// of "global foreach" over everything registered.
protected override void AttachToComponentRegistration(
IComponentRegistry componentRegistry,
IComponentRegistration registration)
{
// The Preparing event is called any time a new instance is needed. There
// are docs for the lifetime events but Preparing isn't on there. Here are the
// docs and the issue I filed on your behalf to get Preparing documented.
// https://autofac.readthedocs.io/en/latest/lifetime/events.html
// https://github.com/autofac/Documentation/issues/69
// You can see the Preparing event here:
// https://github.com/autofac/Autofac/blob/6dde84e5b0a3f82136a0567a84da498b04e1fa2d/src/Autofac/Core/IComponentRegistration.cs#L83
// and the event args here:
// https://github.com/autofac/Autofac/blob/6dde84e5b0/src/Autofac/Core/PreparingEventArgs.cs
registration.Preparing +=
(sender, args) =>
{
// The Component is the thing being resolved - the thing that
// needs a LogService injected. The Component.Activator is the
// thing that is actually going to execute to "new up" an instance
// of the Component. The Component.Activator.LimitType is the actual
// System.Type of the thing being resolved.
var forType = args.Component.Activator.LimitType;
// The docs above explain ResolvedParameter - basically a manually
// passed in parameter that can execute some logic to determine if
// it satisfies a constructor or property dependency. The point of
// this particular parameter is to provide an ILog to anything being
// resolved that happens to have an ILog constructor parameter.
var logParameter = new ResolvedParameter(
// p is the System.Reflection.ParameterInfo that describes the
// constructor parameter that needs injecting. c is the IComponentContext
// in which the resolution is being done (not used here). If this
// method evaluates to true then this parameter will be used; if not,
// it will refuse to provide a value. In this case, if the parameter
// being injected is an ILog, this ResolvedParameter will tell Autofac
// it can provide a value.
(p, c) => p.ParameterType == typeof(ILog),
// p and c are the same here, but this time they're used to actually
// generate the value of the parameter - the ILog instance that should
// be injected. Again, this will only run if the above predicate evaluates
// to true. This creates an ILog by manually resolving from the same
// component context (the same lifetime scope) as the thing that
// needs the ILog. Remember earlier that call to p.AsTyped<Type>()
// to get a parameter? The TypedParameter thing here is how that
// value gets poked in up there. This Resolve call will effectively
// end up calling the lambda registration.
(p, c) => c.Resolve<ILog>(TypedParameter.From(forType)));
// The thing being resolved (the component that consumes ILog) now
// needs to be told to make use of the log parameter, so add it into
// the list of parameters that can be used when resolving that thing.
// If there's an ILog, Autofac will use this specified parameter to
// fulfill the requirement.
args.Parameters = args.Parameters.Union(new[] { logParameter });
};
}
}
Something missing from this that's present in the log4net module example is the ability to do property injection for the logger. However, I'm not going to solve that here; you can look at the example right in the documentation and take that as an exercise to work on if you need that functionality.
I hope that helps. I'll probably not be coming back to follow up on additional questions, so if this isn't enough, I very, very much recommend setting some breakpoints, maybe setting up some tiny minimal-repro unit tests, that sort of thing, and do some deeper exploration to get clarity. Honestly, it's one thing to have someone else explain it, but it's another to actually see it in action and dive into the source of various projects. You'll come out with a fuller understanding with the latter approach, even if it's potentially not as fast.

Autofac intentional circular dependency

With Autofac, what is the proper way to register types or declare dependencies for this type of circular graph?
public interface IComponent
{
void DoSomething();
}
public class AComponent: IComponent
{
...
}
public class BComponent: IComponent
{
...
}
public class CompositeComponent: IComponent
{
public CompositeComponent(IEnumerable<IComponent> components)
{
this.components = components;
}
public void DoSomething()
{
foreach(var component in components)
component.DoSomething();
}
}
The end goal would be that CompositeComponent be the default registration of IComponent and simply pass down calls to all other implementations.
I am gathering that the intent of the question is that you have some implementations of IComponent and you have some sort of CompositeComponent that also implements IComponent. CompositeComponent needs all of the registered IComponent instances except itself otherwise it creates a circular dependency.
This whole thing overlaps pretty heavily with one of our FAQs: "How do I pick a service implementation by context?"
You have some options. In order of my personal recommendation:
Option 1: Redesign the Interfaces
There are actually two concepts going on here - the notion of an individual handler and the notion of a thing that aggregates a set of individual handlers.
Using less generic terms, you might have an IMessageHandler interface and then something that passes a message through the set of all IMessageHandler implementations, but that thing that aggregates the handlers and deals with errors and ensuring the message is handled only by the right handler and all that... that isn't, itself, also a message handler. It's a message processor. So you'd actually have two different interfaces, even if the methods on the interface look the same - IMessageHandler and IMessageProcessor.
Back in your generic component terms, that'd mean you have IComponent like you do now, but you'd also add an IComponentManager interface. CompositeComponent would change to implement that.
public interface IComponentManager
{
void DoSomething();
}
public class ComponentManager : IComponentManager
{
public ComponentManager(IEnumerable<IComponent> components)
{
this.components = components;
}
public void DoSomething()
{
foreach(var component in components)
component.DoSomething();
}
}
Option 2: Use Keyed Services
If you won't (or can't) redesign, you can "flag" which registrations should contribute to the composite by using service keys. When you register the composite, don't use a key... but do specify that the parameter you want for the constructor should resolve from the keyed contributors.
builder.RegisterType<AComponent>()
.Keyed<IComponent>("contributor");
builder.RegisterType<BComponent>()
.Keyed<IComponent>("contributor");
builder.RegisterType<CompositeComponent>()
.As<IComponent>()
.WithParameter(
new ResolvedParameter(
(pi, ctx) => pi.Name == "components",
(pi, ctx) => ctx.ResolveKeyed<IEnumerable<IComponent>>("contributor")));
When you resolve IComponent without providing a key, you'll get the CompositeComponent since it's the only one that was registered that way.
Option 3: Use Lambdas
If you know up front the set of components that should go into the composite, you could just build that up in a lambda and not over-DI the whole thing.
builder.Register(ctx =>
{
var components = new IComponent[]
{
new AComponent(),
new BComponent()
};
return new CompositeComponent(components);
}).As<IComponent>();
It's more manual, but it's also very clear. You could resolve individual constructor parameters for AComponent and BComponent using the ctx lambda parameter if needed.

Reuse autofac's find ctor logic

Autofac finds "best" ctor when registering a component like this
builder.RegisterType<MyType>()...;
or
builder.RegisterType(typeof(MyType))...;
I'm looking for a way to reuse Autofac's reflection code to find ctor parameters. Is this logic available in some public API?
The reason I want above...
I have some components with unknown types at compile time (=dynamic proxies). Currently I register them like this
builder.Register(c =>
{
var dep1 = c.Resolve<IFoo>();
var dep2 = c.Resolve<IBar>();
var dep3 = c.Resolve<IFooBar>();
return someProxyFactory.CreateProxyFrom<MyType>(dep1, dep2, dep3);
}...;
--- Edit ---
The same question is valid if no proxy is involved but when RegisterType cannot be used. Eg...
builder.Register(c =>
{
[...]
if(something)
return new SomeType(dep1, dep2, dep3);
else
return new SomeOtherType(dep1, dep4, dep2, dep5);
}
Here I also would like to reuse Autofac's "find ctor logic" if possible.
--- End edit ---
It works fine but, if possible, I would like to use autofac's logic to find the ctor dependencies for me. I want to write something like
builder.Register(c =>
{
object[] ctorDependencies = letAutofacDoTheSimilarWorkAsInRegisterType(typeof(MyType));
return someProxyFactory.Create<MyType>(ctorDependencies);
}
Is this possible or do I have to write my own logic for this? Or is some completely different approach available for this scenario?
Not quite the answer you're after I guess, but could you use the existing DynamicProxy2? This enables you to attach interceptors to registered interfaces, much like what you are doing with your proxy factory.
Update: you could possibly use the IConstructorFinder and IConstructorSelector interfaces in the Autofac.Core.Activators.Reflection namespace, implemented by the PublicConstructorFinder and MostParametersConstructorSelector respectively.

autofac registration issue in release v2.4.5.724

I have the following registration
builder.Register<Func<Type, IRequestHandler>>(
c => request => (IRequestHandler)c.Resolve(request));
Basically I am trying to register a factory method that resolves an instance of IRequestHandler from a given type.
This works fine until the version 2.4.3.700. But now I am getting a the following error..
Cannot access a disposed object.
Object name: 'This resolve operation has already ended. When
registering components using lambdas,
the IComponentContext 'c' parameter to
the lambda cannot be stored. Instead,
either resolve IComponentContext again
from 'c', or resolve a Func<> based
factory to create subsequent
components from.'.
UPDATE
I was trying to limit autofac's exposure to the rest of the projects in the solution. Nick, thanks for the hint, now my registration looks like this...
builder.Register<Func<Type,IRequestHandler>>(c =>
{
var handlers = c.Resolve<IIndex<Type,RequestHandler>>();
return request => handlers[request];
});
The c in this expression is a temporary, so this code while previously functional, is broken. Autofac 2.4.5 detects this problem while earlier versions silently ignored it.
To fix the issue, explicitly resolve IComponentContext:
builder.Register<Func<Type, IRequestHandler>>(c => {
var ctx = c.Resolve<IComponentContext>();
return request => (IRequestHandler)ctx.Resolve(request));
});
The functionality you're emulating here might be better represented using keys and indexes, e.g. see Interrupted chain of IoC or http://code.google.com/p/autofac/wiki/TypedNamedAndKeyedServices.
I had a similar problem as the user6130. I wanted to avoid using IIndex in my class implementation and pass in a service resolver into my constructor instead.
So now I have my service implementation with the following constructor:
public MvcMailer(Converter<string, MailerBase> mailerResolver)
{
_resolver = mailerResolver;
}
I wanted to used keyed services without directly relying on the Autofac namespace. I was getting the same error until I restructured the configuration as such.
1) Scan for all my mailer implementations and index via class name (could be improved)
builder.RegisterAssemblyTypes(System.Reflection.Assembly.GetExecutingAssembly())
.Where(t => t.Name.EndsWith("Mailer")).Keyed<Mvc.Mailer.MailerBase>(t => t.Name.Replace("Mailer", "").ToLower());
2) Register the converter in Autofac config
builder.Register<System.Converter<string,Mvc.Mailer.MailerBase>>(c => {
var all = c.Resolve<Autofac.Features.Indexed.IIndex<string,Mvc.Mailer.MailerBase>>();
return delegate(string key)
{
return all[key];
};
});
3) Register like other types of components and let Autofac handle the Converter injection
builder.RegisterType<Mailers.MvcMailer>().As<Mailers.IMailer>();

Is it possible in Autofac to resolve all services for a type, even if they were registered with a name or key?

In Autofac one can do the following to get all registered services:
IEnumerable<MyClass> all = Context.Resolve<IEnumerable<MyClass>>()
However, this does not include those which were registered as named services.
Looking at the Autofac source it seems this is because services are queried for resolution based on either a TypedService or a KeyedService.
Is there a way to resolve all services to an IEnumerable, irrespective of whether they were registered with a name or not?
The best option here is to register the items using both the key and the regular 'typed' service:
builder.Register<CupOfT>()
.As<IBeverage>()
.Keyed<IBeverage>("someKey");
You can then just resolve IEnumerable<IBeverage> to get the result you're after, while resolving them by key (or name) is also supported.
If you're concerned about maintaining a particular default registration for IBeverage just use PreseveExistingDefaults() on the others (or make sure your intended default is registered last).
HTH!
Nick
I've written a method which appears to work; I'd appreciate feedback if there's a built-in way to do this in Autofac. In the below example, the field _context is of type IComponentContext.
public IEnumerable<T> ResolveAll<T>()
{
// We're going to find each service which was registered
// with a key, and for those which match the type T we'll store the key
// and later supplement the default output with individual resolve calls to those
// keyed services
var allKeys = new List<object>();
foreach (var componentRegistration in _context.ComponentRegistry.Registrations)
{
// Get the services which match the KeyedService type
var typedServices = componentRegistration.Services.Where(x => x is KeyedService).Cast<KeyedService>();
// Add the key to our list so long as the registration is for the correct type T
allKeys.AddRange(typedServices.Where(y => y.ServiceType == typeof (T)).Select(x => x.ServiceKey));
}
// Get the default resolution output which resolves all un-keyed services
var allUnKeyedServices = new List<T>(_context.Resolve<IEnumerable<T>>());
// Add the ones which were registered with a key
allUnKeyedServices.AddRange(allKeys.Select(key => _context.ResolveKeyed<T>(key)));
// Return the total resultset
return allUnKeyedServices;
}
It would appear that you can combine the As<T>() methods and Named<T>() methods as shown below:
[TestMethod]
public void ResolveTests()
{
var builder = new ContainerBuilder();
builder.RegisterType<ClassA1>().As<IClassA>().Named<IClassA>("1");
builder.RegisterType<ClassA2>().As<IClassA>().Named<IClassA>("2");
builder.RegisterType<ClassA3>().As<IClassA>().Named<IClassA>("3");
var container = builder.Build();
var allInstances = container.Resolve<IEnumerable<IClassA>>();
allInstances.Count().Should().Be(3);
container.ResolveNamed<IClassA>("1").Should().BeAssignableTo<ClassA1>();
container.ResolveNamed<IClassA>("2").Should().BeAssignableTo<ClassA2>();
container.ResolveNamed<IClassA>("3").Should().BeAssignableTo<ClassA3>();
}