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);
}
}