I'm using Autofac to register named instances. I have to translate xml transactions into objects.
First, I have an enum.
public enum TransactionType
{
Unknown = 0,
[XmlNode("MyNodeA")]
TypeA = 1,
[XmlNode("MyNodeA")]
TypeB = 2
}
I have a method that creates an IDictionary<string, TransactionType> using the XmlNode attribute on the enum.
Here is my autofac mapping
var mappings = TransactionTypeHelper.GetDictionary();
foreach (var mapping in mappings)
{
builder.Register(ctx => {
return mapping.Key;
})
.Named<TransactionType>(mapping.Value)
.InstancePerLifetimeScope();
}
Then, I have a TransactionTypeFactory for getting the TransactionType based on the xml node.
public TransactionType GetTransactionType(string rootNode)
{
return _container.Resolve<TransactionType>(rootNode?.ToLower());
}
My problem is that I want to pass through any unknown xml nodes as unknown transactions so that I can process new transactions without making any code changes. The problem is that _container.Resolve throws an error if the node passed in has not been registered.
What I want to do is make autofac return the enum default if the named instance is not found instead of throwing an error. The funny thing is, I have unit tests where this container is mocked, and they all pass, but Autofac specifically blows up on this call.
I know this question is rather old, but I'd like to share a solution I have learned in the meantime in the hopes it will help someone with the same issue.
With autofac, you can register a function that can resolve using logic.
First, you would register each named instance. In the question I was doing this with a helper and iterating through a collection, but the essence is to map each value of the enum to an instance.
builder.Register<TransactionAClass>(ctx =>
{
//get any instances required by ConcreteClass from the ctx here and pass into the constructor
return new TransactionAClass();
})
.Named<Interfaces.ITransactionInterface>($"{TransactionType.TypeA:f}")
.InstancePerLifetimeScope();
Once you have all your registrations for known values, then we register a resolver function.
builder.Register<Func<TransactionType, Interfaces.ITransactionInterface>>(ctx =>
{
//you must resolve the context this way before being able to resolve other types
var context = ctx.Resolve<IComponentContext>();
//get the registered named instance
return (type) =>
{
var concrete = context.ResolveNamed<Interfaces.ITransactionInterface>($"{type:f}");
if (concrete == null)
{
//return a default class or throw an exception if a valid registration is not found
return new TransactionAClass();
}
return concrete;
}
});
Then, you can use the resolver like this
public class MyClass
{
private readonly ITransactionInterface transaction;
public MyClass(Func<TransactionType, Interfaces.ITransactionInterface> transactionResolver)
{
transaction = transactionResolver.Invoke(TransactionType.TypeA);
}
}
Related
I'm using Autofac with ASP.NET Core.
My dependency is a Reporter:
public class Reporter {
public Reporter (bool doLogging) { DoLogging = doLogging ; }
public string DoLogging { get; set; }
// other stuff
}
I need to use it like this:
public class Foo
{
public Foo(Func<bool, Reporter> reporterFactory) { _reporterFactory = reporterFactory; }
private readonly Func<bool, Reporter> _reporterFactory;
}
And I want it to resolve like this:
_reporterFactory(false) ---> equivalent to ---> new Reporter(false)
_reporterFactory(true) ---> equivalent to ---> new Reporter(true)
I want the same instance per request (i.e. Autofac's InstancePerLifetimeScope), for the same bool parameter. When I call _reporterFactory(false) multiple times, I want the same instance. And when I call _reporterFactory(true) multiple times, I want the same instance. But those two instances must be different to each other.
So I register it like this:
builder
.Register<Reporter>((c, p) => p.TypedAs<bool>() ? new Reporter(true): new Person(false))
.As<Reporter>()
.InstancePerLifetimeScope(); // gives "per HTTP request", which is what I need
However, when I resolve I get the same instances regardless of the bool argument:
var reporter = _reporterFactory(false);
var reporterWithLogging = _reporterFactory(true);
Assert.That(reporter, Is.Not.SameAs(reporterWithLogging)); // FAIL!
The documentation for "Parameterized Instantiation" says
resolve the object more than once, you will get the same object instance every time regardless of the different parameters you pass in. Just passing different parameters will not break the respect for the lifetime scope.
Which explains the behavior. So how do I register it correctly?
As mentioned in comments, you could use keyed services to achieve your goal:
builder.Register(c => new Reporter(true)).Keyed<IReporter>(true).InstancePerLifetimeScope();
builder.Register(c => new Reporter(false)).Keyed<IReporter>(false).InstancePerLifetimeScope();
The thing is, if you want to inject it to another class, you would have to inject it with IIndex<bool, IReporter>:
public class Foo
{
public Foo(IIndex<bool, IReporter> reporters)
{
var withLogging = reporters[true];
var withoutLogging = reporters[false];
}
}
IIndex is Autofac's interface, which makes your component tight coupled with the container, and this may not be desirable. To avoid this, you could additionally register the factory, like this:
builder.Register<Func<bool, IReporter>>((c,p) => withLogging => c.ResolveKeyed<IReporter>(withLogging)).InstancePerLifetimeScope();
public class Foo
{
public Foo(Func<bool, IReporter> reporters)
{
var withLogging = reporters(true);
var withoutLogging = reporters(false);
}
}
Now you have the working solution without coupling to the container itself.
My first time working with Autofac to inject AutoMapper's IMapper interface into classes that have an object mapping requirement. I have made some progress, with a little help, getting the various dependencies added to AutoMapper's register using Assembly Scanning:
builder.RegisterAssemblyTypes(AppDomain.CurrentDomain.GetAssemblies())
.AsClosedTypesOf(typeof(ITypeConverter<,>))
.AsImplementedInterfaces();
builder.RegisterAssemblyTypes(typeof(AutoMapperExtensions).Assembly)
.AssignableTo<Profile>().As<Profile>();
builder.Register(context => {
var profiles = context.Resolve<IEnumerable<Profile>>();
return new MapperConfiguration(x => {
foreach (var profile in profiles) x.AddProfile(profile);
});
}).SingleInstance().AutoActivate().AsSelf();
builder.Register(context => {
var componentContext = context.Resolve<IComponentContext>();
var config = componentContext.Resolve<MapperConfiguration>();
return config.CreateMapper();
}).As<IMapper>();
This works perfectly for an ITypeConverter<,> that doesn't have any injected dependencies:
public class SourceToDestinationTypeConverter : ITypeConverter<SourceModel, DestinationModel> {
public DestinationModel Convert(SourceModel source, DestinationModel destination, ResolutionContext context) {
if (source.Items == null) {
return null;
}
return new DestinationModel {
FirstItem = source.Items.FirstOrDefault(),
LastItem = source.Items.LastOrDefault()
};
}
}
However from the moment I add a dependency, in this contrived example, a validator:
public class SourceToDestinationTypeConverter : ITypeConverter<SourceModel, DestinationModel> {
private readonly IValidator<SourceModel> _validator;
public SourceToDestinationTypeConverter(IValidator<SourceModel> validator) {
_validator = validator;
}
public DestinationModel Convert(SourceModel source, DestinationModel destination, ResolutionContext context) {
if (!_validator.Validate(source)) return null;
return new DestinationModel {
FirstItem = source.Items.FirstOrDefault(),
LastItem = source.Items.LastOrDefault()
};
}
}
The following exception is thrown:
Application.TypeConverters.SourceToDestinationTypeConverter needs to have a constructor with 0 args or only optional args
It seems clear to me that AutoMapper needs to be told to use Autofac to fulfil the dependencies. However, I haven't been able to find out how to tell it to do so.
The full solution is available on GitHub if further clarification of the error is required.
NOTE: Travis Illig has provided a hollistic answer to the question which I am marking as the answer as it answers the question in a broad and generic way. However, I also wanted to document the specific solution to my question.
You need to be fairly careful of how you wire up the dependency resolver to AutoMapper, to be precise you must resolve the component context within the closure - failing to do so will result in the context being disposed before AutoMapper ever gets a chance to resolve it's dependencies.
Solution #1
In my example, the following code block that registers the IMapper using the previously defined MapperConfiguration:
builder.Register(c => {
var context = c.Resolve<IComponentContext>();
var config = context.Resolve<MapperConfiguration>();
return config.CreateMapper();
}).As<IMapper>();
Can be trivially adapted by using an overload of MapperConfiguration.CreateMapper() that accepts a Func<Type, object> as an argument named serviceCtor that AutoMapper will use to construct dependencies:
builder.Register(c => {
var context = c.Resolve<IComponentContext>();
var config = context.Resolve<MapperConfiguration>();
return config.CreateMapper(context.Resolve);
}).As<IMapper>();
It's essential that the Component Context context is used as it is declared within the closure, attempting to use c will result in the following exception:
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.
Solution #2
Using a very similar technique to Solution #1 it is possible to use the IMapperConfiguration.ConstructServiceUsing(Func<Type, object>) which provides for more readable code. The original code:
builder.Register(c => {
var profiles = c.Resolve<IEnumerable<Profile>>();
return new MapperConfiguration(x => {
foreach (var profile in profiles) x.AddProfile(profile);
});
}).SingleInstance().AsSelf();
And the updated code with the call to x.ConstructServiceUsing(constructor):
builder.Register(c => {
var profiles = c.Resolve<IEnumerable<Profile>>();
var context = c.Resolve<IComponentContext>();
return new MapperConfiguration(x => {
foreach (var profile in profiles) x.AddProfile(profile);
x.ConstructServicesUsing(context.Resolve);
});
}).SingleInstance().AsSelf();
Again if you fail to create an instance of IComponentContext within the closure / lambda the context will have been disposed before the Mapper creates the dependencies.
I'm guessing you forgot to add the call to ConstructServicesUsing during AutoMapper configuration. Make sure to do this
Exactly how to integrate Autofac with your app really depends on what kind of app you have (Windows Service? MVC? Web API? Windows Forms? UAP?) and what your expectations around lifetime scope usage are. None of that was included in your question. However, if you search the web for "autofac constructservicesusing" you come up with plenty of examples including several other StackOverflow questions on the same topic.
Here's a simple example that shows pretty much exactly what you're doing. If you're using an MVC or Web API app and need per-request-scope support, I have a full blog walkthrough on that.
By way of a note, I'm not sure if the AutoActivate call is really necessary. SingleInstance is thread-safe and it doesn't actually take that long to build an AutoMapper profile lazily. You may want to try without it, especially if AutoMapper itself isn't executing that part until after AutoActivate has run.
As detailed in InstancePerApiControllerType not working, I am unable to use the InstancePerApiControllerType to configure my solution. The answer provided there works so long as I am directly injecting a ConnectionContext into the controller, or otherwise know that a class is only used by a specific controller. Unfortunately that is not the case in my situation:
ControllerA -> EngineA -> RepositoryA -> GenericEntityAccessor
ControllerB -> EngineB -> RepositoryB -> GenericEntityAccessor
The issue is when we come in through ControllerA, GenericEntityAccessor needs "string A" and from ControllerB it needs "string B".
Of course, the real situation is a little more complicated and there are some bad practices such as code that directly "news"-up a ConnectionContext (it's legacy code). I'm currently exploring providing another component that provides the connection string that is injected via Autofac and configured in the controller using Lazy, but the bad practices are causing problems there also (i.e. once I start to change things in the interface, all the dominoes start to fall over and I end up 15 classes later wondering how I got there).
Are there any patterns, techniques, etc. that address this type of thing? I can't imagine it's all that uncommon.
UPDATE:
To provide a few more specifics, since I'm having some trouble getting this to work, in general we have the following hierarchy, showing which scopes I've applied
Controller -> InstancePerApiRequest()
I*Repository -> ?
I*Manager -> ?
I*Builder -> ?
I*Adapter -> ?
ISqlServerConnectionContext -> ?
IConnectionContextCache -> InstancePerApiRequest()
I've got a number of components that directly take ISqlServerConntectionContext and I'm trying to provide it like so:
container.Register(c =>
{
var connectionContextCache = c.Resolve<IConnectionContextCache>();
var connection = (ISqlServerConnectionContext)connectionContextCache.CurrentConnectionContext;
return connection;
}).As<ISqlServerConnectionContext>().InstancePerDependency();
Unfortunately at that point I'm getting a null for CurrectConnectionContext. My guess at this point is I've got some component that isn't rooted from the controller and I'm currently going through the dependencies manually attempting to find it (AFAIK the isn't a way for my to find out which object triggered Autofac to attempt to provide the ISqlServerConnectionContext when I'm debugging).
UPDATE 2:
It turns out I did have some issues where I was registering things improperly, and creating a dependency on ISqlServerConnectionContext for DocumentController, even though it did not have one (this was created through the delegate for something it did depend on).
Now I've got a circular reference that I'm pretty sure I've created myself in the registrations:
container.Register(x =>
{
if (x.IsRegistered<HttpRequestMessage>())
{
var httpRequestMethod = x.Resolve<HttpRequestMessage>();
var tokenHelper = x.Resolve<ITokenHelper>();
var token = tokenHelper.GetToken(httpRequestMethod);
return token ?? new NullMinimalSecurityToken();
}
return new NullMinimalSecurityToken();
}).As<IMinimalSecurityToken>().InstancePerApiRequest();
container.Register(c =>
{
var connectionContextCache = c.Resolve<IConnectionContextCache>();
var token = c.Resolve<IMinimalSecurityToken>();
var connection = (ISqlServerConnectionContext)connectionContextCache.CurrentConnectionContext;
connection.Token = token;
return connection;
}).As<ISqlServerConnectionContext>().InstancePerApiRequest();
The problem is ISqlServerConnectionContext has a property of type IMinimalSecurityToken which is optional, and definitely not used when the ISqlServerConnectionContext is being used to look up IMinimalSecurityToken, which depends on ISqlServerConnectionContext through ITokenHelper.
UPDATE 3:
For completeness, in order to solve my circular reference problem I needed to use named services, and use a SqlServerConnectionContext that did not have the IMinimalSecurityToken property set for the IOAuthTokenManager registration. Now I'm getting the dreaded
No scope with a Tag matching 'AutofacWebRequest' is visible
error, but I think that warrants a new question if I'm not able to solve it.
container.Register(c =>
{
var productId = WellKnownIdentifierFactory.Instance.GetWellKnownProductIdentifier(WellKnownProductIdentifiers.RESTSearchService);
var connectionString = ConfigurationManager.AppSettings[AppSettingsNames.DatabaseConnection];
var newConnectionContext = new SqlServerConnectionContext(connectionString) { ProductID = productId };
newConnectionContext.Open();
return newConnectionContext;
}).Named<ISqlServerConnectionContext>("OAuthTokenConnectionContext").InstancePerApiRequest();
container.Register(c => new SqlServerBuilderFactory(c.ResolveNamed<ISqlServerConnectionContext>("OAuthTokenConnectionContext"))).Named<IBuilderFactory>("OAuthTokenBuilderFactory").InstancePerApiRequest();
container.Register(c =>new OAuthTokenManager(c.ResolveNamed<IBuilderFactory>("OAuthTokenBuilderFactory"))).As<IOAuthTokenManager>().InstancePerApiRequest();
This can be solved using AutoFac's support for object graph lifetime scoping.
Cache the current SqlServerConnectionContext in an object scoped to the lifetime of your controller.
Within the SqlServerConnectionContext factory type, once the connection is created assign it to the backing field of the current lifetime-scoped cache
Any types scoped within the lifetimes scope of a controller can then access the connection associated with that controller through the cache
The only complexities I can think of are:
If the controller is not actually the root of a lifetime scope for all types with a dependency on a specific connection. I.e. if they fall outside the lifetime of the controller.
If any of the dependencies are registered as single instance. In which case they will not be able to resolve the Cache as it is currently implemented as it is PerApiRequest.
For example:
public interface ISqlServerConnectionContextCache
{
ISqlServerConnectionContext CurrentContext { get; set; }
}
public class SqlServerConnectionContextScopeCache : ISqlServerConnectionContextCache
{
public ISqlServerConnectionContext CurrentContext { get; set; }
}
public interface ISqlServerConnectionContextFactory
{
ISqlServerConnectionContext Create();
}
// The factory has the cache as a dependancy
// This will be the first use of the cache and hence
// AutoFac will create a new one at the scope of the controller
public class SqlServerConnectionContextFactory : ISqlServerConnectionContextFactory
{
private string _connectionString;
private ISqlServerConnectionContextCache _connectionCache;
public SqlServerConnectionContextFactory(ISqlServerConnectionContextCache connectionCache,
string connectionString)
{
_connectionCache = connectionCache;
_connectionString = connectionString;
}
public ISqlServerConnectionContext Create()
{
var connectionContext = new SqlServerConnectionContext(_connectionString);
connectionContext.Open();
_sqlServerConnectionContextProvider.CurrentContext = connectionContext;
return connectionContext;
}
}
public class MyController : ApiController
{
private ISqlServerConnectionContext _sqlServerConnectionContext;
public MyController(Func<string, ISqlServerConnectionContextFactory> connectionFactory)
{
_sqlServerConnectionContext = connectionFactory("MyConnectionString");
}
}
// As the cache is lifetime scoped it will receive the single instance
// of the cache associated with the current lifetime scope
// Assuming we are within the scope of the controller this will receive
// the cache that was initiated by the factory
public class MyTypeScopedByController
{
public MyTypeScopedByController(ISqlServerConnectionContextCache connectionCache)
{
var sqlServerConnectionContext = connectionCache.CurrentContext;
}
}
// AutoFac wiring
builder.RegisterType<SqlServerConnectionContextScopeCache>()
.As<ISqlServerConnectionContextCache>()
.InstancePerApiRequest();
builder.RegisterType<SqlServerConnectionContextFactory>()
.As<ISqlServerConnectionContextFactory>()
.InstancePerDependency();
I have set up an assembly catalog:
private CompositionContainer GetContainer() {
// initialize directory info
ExtensionDirectory = new DirectoryInfo(settings.ExtensionsPath);
// directory catalog
var dirCatalog = new DirectoryCatalog(ExtensionDirectory.FullName);
return new CompositionContainer(dirCatalog);
}
The contents of the container will load up all the assemblies in the directory as expected. I do not want to actually compose anything yet because I have constructors that will be injected with dependencies.
What I want to do is use the AssemblyCatalog as a repository; query for a specific export, pass the constructor dependency, then compose only the parts involved in this process.
From what I understand, if I were to call
_container.ComposeParts(this);
...without providing exports for the [ImportingConstructor]s, then none of the parts would be included in the _container.
In order to facilitate queries to the container, I have a method as follows:
public Lazy<IEntity> GetPart(Func<Lazy<IEntity, IEntityMetaData>, bool> selector) {
var entity = _container.GetExports<IEntity, IEntityMetaData>()
.Where(selector)
.Select(e => e as Lazy<IEntity>)
.FirstOrDefault();
return entity; // this will be passed up to the composition service
}
It seems that GetExports<T, M>() will not return an export containing an [ImportingConstructor] if the part which would satisfy the dependency is not included in the container.
My approach is to have an extension container/catalog at a low level; a higher level composition service will receive all parts and compose the final object. I decided on this approach so we would be able to add/extend the types of catalogs available in the future.
I think these concerns are already separated: discovery is handled by catalogs, and composition is done by export providers.
In the typical case, you just pass a catalog directly to the container and for convenience it will automatically take care of creating an CatalogExportProvider for it.
But you can also create one or more export providers yourself and pass them to the container with this constructor overload. (You may also have to set the SourceProvider to point back at the container after that, so that the export providers can use each other.)
You can create your own ExportProvider implementations, and they don't even have to be backed by catalogs.
In order to satisfy the requirements, I created 3 classes:
public sealed class CompositionFactory {
[Import("Provider")]
private IProvider importProvider;
/* MEF initialization */
}
[Export("Provider")]
public sealed class AssemblyProvider : IProvider {
private CatalogExportProvider _provider;
}
internal sealed class ComposableAggregate { }
The CompositionFactory initializes MEF to discover the AssemblyProvider. When the provider initializes:
private CatalogExportProvider InitializeProvider() {
// directory catalog
var dirCatalog = new DirectoryCatalog(ExtensionDirectory.FullName);
return new CatalogExportProvider(dirCatalog);
}
...we return a CatalogExportProvider. I can now use an API to the CompositionFactory:
public ISomething GetSomething(string ContractName, object ContractParam) {
// implementation
}
...to query for the correct composable part using a contract name:
public ComposablePartDefinition GetPartDefinition(string ContractName) {
return _provider.Catalog.Parts
.Where(p => p.ExportDefinitions
.Select(e => e.ContractName)
.Any(c => c == ContractName))
.FirstOrDefault();
}
The work is then completed in the ComposableAggregate helper class:
internal ISomething Value {
get {
return _container.GetExport<IEntity>(_contractName).Value;
}
}
private CompositionBatch CreateBatch() {
CompositionBatch batch = new CompositionBatch();
// create composable part from definition
ComposablePart importDef = CreatePart(_contractName);
batch.AddPart(importDef);
return batch;
}
private ComposablePart CreatePart(string ContractName) {
// get part definition from catalog
return _provider.GetPartDefinition(ContractName).CreatePart();
}
Currently in code i have used an object factory to return me a processor based of a string tag, which has severed its purpose up until now.
using Core;
using Data;
public static class TagProcessorFactory
{
public static ITagProcessor GetProcessor(string tag)
{
switch (tag)
{
case "gps0":
return new GpsTagProcessor();
case "analog_manager":
return new AnalogManagerTagProcessor();
case "input_manager":
return new InputManagerTagProcessor();
case "j1939":
return new J1939TagProcessor(new MemcachedProvider(new[] { "localhost" }, "DigiGateway"), new PgnRepository());
default:
return new UnknownTagProcessor();
}
}
}
Calling Code
var processor = TagProcessorFactory.GetProcessor(tag.Name);
if (!(processor is UnknownTagProcessor))
{
var data = processor.Process(unitId, tag.Values);
Trace.WriteLine("Tag <{0}> processed. # of IO Items => {1}".FormatWith(tag.Name, data.Count()));
}
as you can see one of my items has dependencies and im trying to execute testing code and i want to pass in mock repositories and cache providers but i can seem to think of a way to do this.
Is this a bad design or anyone have any ideas to fix it to make my factory testable?
Thanks
Since you are using Autofac, you can take advantage of the lookup relationship type:
public class Foo
{
private readonly IIndex<string, ITagProcessor> _tagProcessorIndex;
public Foo(IIndex<string, ITagProvider> tagProcessorIndex)
{
_tagProcessorIndex = tagProcessorIndex;
}
public void Process(int unitId, Tag tag)
{
ITagProcessor processor;
if(_tagProcessorIndex.TryGetValue(tag.Name, out processor))
{
var data = processor.Process(unitId, tag.Values);
Trace.WriteLine("Tag <{0}> processed. # of IO Items => {1}".FormatWith(tag.Name, data.Count()));
}
}
}
See the TypedNamedAndKeysServices wiki article for more information. To register the various processors, you would associate each with its key:
builder.RegisterType<GpsTagProcessor>().Keyed<ITagProcessor>("gps0");
builder.RegisterType<AnalogManagerTagProcessor>().Keyed<ITagProcessor>("analog_manager");
builder.RegisterType<InputManagerTagProcessor>().Keyed<ITagProcessor>("input_manager");
builder
.Register(c => new J1939TagProcessor(new MemcachedProvider(new[] { "localhost" }, new PgnRepository()))
.Keyed<ITagProcessor>("j1939");
Notice we don't register UnknownTagProcessor. That was a signal to the caller of the factory that no processor was found for the tag, which we express using TryGetValue instead.
Using something like StructureMap you could use the ObjectFactory which, when configured would return you a named concrete instance.
http://structuremap.net/structuremap/index.html
I suggest you look through another SO post. It solves several problems at once, including how to replace contructor values - without a mess. Specifically, the parameters to the constructor simply become static fields of a "Context" class, which are read by the constructor of the interior class.