Reputation: 79
I am not even sure the best way to ask this question. I would like a resolver to be able to take in multiple functions that create an object when requested. The problem I am having is that I do not know how to pass in multiple functions when registering the resolver with Autofac.
How would I register this Resolver with functions to create each of the activities? (The ActivityAggregate is just simple object registration. No help needed there.)
public class ActivityResolver
{
Func<ActivityAggregate, IActivity> _getActivityOne;
Func<ActivityAggregate, IActivity> _getActivityTwo;
Func<ActivityAggregate, IActivity> _getActivityThree;
ActivityAggregate _activityAggregate;
public ActivityResolver(Func<ActivityAggregate, IActivity> getActivityOne, Func<ActivityAggregate, IActivity> getActivityTwo, Func<ActivityAggregate, IActivity> getActivityThree,
ActivityAggregate aggregate)
{
_getActivityOne = getActivityOne;
_getActivityTwo = getActivityTwo;
_getActivityThree = getActivityThree;
_activityAggregate = aggregate;
}
public IActivity GetActivity(ActivityType activityType)
{
switch (activityType)
{
//The choice between activity one and two is config driven. Providing either one will then go to the configuration settings to make the final choice.
//We have this scenario to decide between using a legacy activity or the newly created version that may need to be switched off via config without a code push.
case ActivityType.One:
case ActivityType.Two:
if (_activityAggregate.ConfigReader.SelectBooleanValue("ConfigSettting.UseActivityOne", true))
return _getActivityOne(_activityAggregate);
else
return _getActivityTwo(_activityAggregate);
case ActivityType.Three:
return _getActivityThree(_activityAggregate);
default:
throw new NotImplementedException(string.Format("ActivityResolver does not recognize activity type {0}", activityType));
}
}
}
One of the funcs to create the activity object would look like this before being redesigned for autofac:
public static IActivity GetActivityOne(ActivityAggregate aggregate)
{
return new ActivityOne(aggregate);
}
EDIT: I found an example of how to do exactly what I want with Unity. Was hoping someone had seen a way to do the same thing with Autofac.
Here is the Unity example:
private static IUnityContainer BuildUnityContainer()
{
var container = new UnityContainer();
container.RegisterType<IExampleService, ExampleService>("default");
container.RegisterType<IExampleService, AnotherExampleService>("another");
container.RegisterType<Func<string, IExampleService>>(
new InjectionFactory(c =>
new Func<string, IExampleService>(name => c.Resolve<IExampleService>(name))));
container.RegisterControllers();
return container;
}
And then the sample call:
public class HomeController
{
private IExampleService _service;
public HomeController(Func<string, IExampleService> serviceFactory)
{
var exampleServiceImplementation = "default"; // TODO get dynamically
_service = serviceFactory(exampleServiceImplementation);
}
public ActionResult Index()
{
return View(_service.GetSomething());
}
}
Upvotes: 0
Views: 746
Reputation: 23934
The initial version of the question shows that you want an ActivityResolver
that has three different parameters of the same type:
public ActivityResolver(
Func<ActivityAggregate, IActivity> getActivityOne,
Func<ActivityAggregate, IActivity> getActivityTwo,
Func<ActivityAggregate, IActivity> getActivityThree,
ActivityAggregate aggregate)
Under that original request, there are challenges. From a typed parameter perspective you won't get what you want unless you register the ActivityResolver
with a lambda or otherwise very closely controlling the parameters (eg using Autofac ResolvedParameter
). All the parameter types are the same so basic reflection registration will always only give you three copies of the same value. That is, getActivityOne
, getActivityTwo
, and getActivityThree
will all be the same instance of the same thing because they're all the same type with no other differentiation.
This FAQ touches on what it appears you're trying to do there and may give you some additional ideas.
The updated version of your question changes the game. It definitely indicates how thinking about something in a different way can open up answers... and maybe change the way you ask questions in the future.
The updated version shows a controller that resolves a different object based on some input - it's not three different functions, it's a single function that just gets the right resolved object.
public HomeController(Func<string, IExampleService> serviceFactory)
That is totally covered in that FAQ I linked in the "keyed services" section which also points you to the section of the docs on implicit relationship types. Technically you could also use metadata.
Examples of both of these are all through the Autofac docs, but the easiest way to go is probably metadata.
First, register all the different things with a known piece of metadata.
builder.RegisterType<ActivityOne>()
.As<IActivity>()
.WithMetadata("activity", ActivityType.One);
builder.RegisterType<ActivityTwo>()
.As<IActivity>()
.WithMetadata("activity", ActivityType.Two);
builder.RegisterType<ActivityThree>()
.As<IActivity>()
.WithMetadata("activity", ActivityType.Three);
In your constructor you can take an IEnumerable<Meta<Lazy<IActivity>>>
parameter. That's a little daunting at first glance, but it's just Autofac built-in relationship support composing a bunch of things together for you. In this case, you want:
IActivity
types you've registered (IEnumerable<T>
)Meta<T>
)Lazy<T>
)public ActivityResolver(IEnumerable<Meta<Lazy<IActivity>>> activities)
Then in your resolver method, you can just use LINQ.
public IActivity GetActivity(ActivityType activityType)
{
return this._activities
.First(m => m.Metadata["activity"].Equals(activityType))
.Value // The value from the Meta<T> object is a Lazy<T>
.Value; // This resolves the T from Lazy<T>
}
Obviously handle errors as needed, but the point is that you can sidestep the question of how to inject three identical things into your constructor by changing the way you think about the problem. Keyed services, metadata, whatever.
Again, it is well worth cruising the Autofac docs. I know there's a lot. I wrote most of it. There's a lot because Autofac supports a lot of features. In almost every case we do our best to show examples right in the docs, but you should always consider the unit tests a good source of examples, too. Oh, and we have a whole repo that's dedicated to just showing examples.
Becoming familiar with the features will help you solve some of the more complex stuff it sounds like you're going to be running into and maybe help change the question rather than forcing a square peg into a round hole.
Upvotes: 1
Reputation: 3736
Well instead of passing multiple func as parameter just pass one func as a dependency in a constructor and that func can take in ActivityType and ActivityAggregate as input parameter and return IActivity. That way you will be injecting only one dependency into the constructor. The way to register the func with input parameter is following:-
services.AddScoped(factory =>
{
IHusbandryTaskMapper GetHusbandryTaskMapper(HusbandryTaskSubType subType)
{
switch (subType)
{
case HusbandryTaskSubType.AnimalCull:
return new HusbandryCullAnimalTaskMapper(factory.GetService<IUserIdentityResolver>(), factory.GetService<ILanguageProvider>());
case HusbandryTaskSubType.CageChange:
case HusbandryTaskSubType.CensusCheck:
return new CageTaskMapper(factory.GetService<IUserIdentityResolver>(), factory.GetService<ILanguageProvider>());
default:
throw new NotImplementedException();
}
}
return (Func<HusbandryTaskSubType, IHusbandryTaskMapper>)GetHusbandryTaskMapper;
});
then in constructor just inject as following
public ActivityResolver(Func<HusbandryTaskSubType, IHusbandryTaskMapper>)taskMapper,
ActivityAggregate aggregate)
{
_taskMapper = taskMapper;
_activityAggregate = aggregate;
}
Use it like below
public IActivity GetActivity(ActivityType activityType)
{
return _taskMapper(activity, _activityAggregate );
}
Upvotes: 2