Unity: Implicit ResolvedParameter for unnamed registrations

The UserService constructor has two parameters: a IUnitOfWork and a IUserRepository :

 public UserService(IUnitOfWork unitofWork, IUserRepository userRepository) { ... } 

I use named registration to distinguish multiple instances of IUnitOfWork , so when registering a UserService with a Unity container, I need to explicitly specify using the InjectionConstructor :

 container.RegisterType<IUserService, UserService>( new InjectionConstructor( new ResolvedParameter<IUnitOfWork>("someContext"), new ResolvedParameter<IUserRepository>() ) ); 

Is it possible to omit new ResolvedParameter<IUserRepository>() ? I would like Unity to implicitly output this parameter, since there is no need for the named registration. The code will look like this:

 container.RegisterType<IUserService, UserService>( new InjectionConstructor( new ResolvedParameter<IUnitOfWork>("someContext") ) ); 

This will be done anyway when I do not need to use the InjectionConstructor .

+6
source share
2 answers

Based on the InjectionConstructor , I came up with this RequiredInjectionConstructor. It allows you to specify any set of arguments and try to find a constructor that should have (at least) the passed set of injection parameters. If there are several constructors that meet these criteria, he selects the constructor with the least number of parameters. The remaining constructor parameters are considered unidentified allowed parameters.

I have not yet completed a complete set of unit tests, so let me know if you encounter any problems.

 /// <summary> /// A class that holds the collection of minimum required /// parameters for a constructor, so that the container can /// be configured to call this constructor. /// </summary> public class RequiredInjectionConstructor : InjectionMember { private readonly List<InjectionParameterValue> _requiredParameterValues; /// <summary> /// Create a new instance of <see cref="RequiredInjectionConstructor"/> that looks /// for a constructor with a minimum of the given required set of parameters. /// </summary> /// <param name="requiredParameterValues">The values for the parameters, that will /// be converted to <see cref="InjectionParameterValue"/> objects.</param> public RequiredInjectionConstructor(params object[] requiredParameterValues) { _requiredParameterValues = InjectionParameterValue.ToParameters(requiredParameterValues).ToList(); } /// <summary> /// Add policies to the <paramref name="policies"/> to configure the /// container to call this constructor with the required parameter values. /// </summary> /// <param name="serviceType">Interface registered, ignored in this implementation.</param> /// <param name="implementationType">Type to register.</param> /// <param name="name">Name used to resolve the type object.</param> /// <param name="policies">Policy list to add policies to.</param> public override void AddPolicies(Type serviceType, Type implementationType, string name, IPolicyList policies) { ConstructorInfo ctor = FindConstructor(implementationType, _requiredParameterValues); IEnumerable<InjectionParameterValue> selectedConstructorParameterValues = GetSelectedConstructorParameterValues(ctor, _requiredParameterValues); policies.Set<IConstructorSelectorPolicy>( new SpecifiedConstructorSelectorPolicy(ctor, selectedConstructorParameterValues.ToArray()), new NamedTypeBuildKey(implementationType, name)); } private static ConstructorInfo FindConstructor(Type typeToCreate, IEnumerable<InjectionParameterValue> requiredInjectionParameters) { var typeToCreateReflector = new ReflectionHelper(typeToCreate); var matchedConstructors = typeToCreateReflector.InstanceConstructors. Where(ctor => { var constructorParameterTypes = ctor.GetParameters().Select(info => info.ParameterType); return requiredInjectionParameters.All(required => constructorParameterTypes.Any(required.MatchesType)); }); if (matchedConstructors.Any()) { // Prefer the constructor that has the least number of arguments. // Other preference models could be implemented here. return matchedConstructors.OrderBy(ctor => ctor.GetParameters().Count()). FirstOrDefault(); } string signature = string.Join(", ", requiredInjectionParameters.Select(required => required.ParameterTypeName).ToArray()); throw new InvalidOperationException( string.Format("Unable to find a constructor with the minimum required parameters. Type: {0}, RequiredParameters: {1}", typeToCreate.FullName, signature)); } private static IEnumerable<InjectionParameterValue> GetSelectedConstructorParameterValues(ConstructorInfo ctor, IEnumerable<InjectionParameterValue> requiredInjectionParameters) { var injectionParameterValues = new List<InjectionParameterValue>(); foreach (var parameter in ctor.GetParameters()) { var existingInjectionParameter = requiredInjectionParameters.FirstOrDefault(required => required.MatchesType(parameter.ParameterType)); injectionParameterValues.Add(existingInjectionParameter ?? new ResolvedParameter(parameter.ParameterType)); } return injectionParameterValues; } } 
+7
source

Would you like to decorate your constructor with the Unity DependencyAttribute attribute? This solution is direct, built-in and allows you to select and select a named dependency. But it "pollutes" your constructor with Unity goo.

 public UserService( [Dependency("someContext")]IUnitOfWork unitofWork, IUserRepository userRepository) { ... } 

Another solution would be to write a custom BuilderStrategy and UnityContainerExtension. This can be done with even more work.

+2
source

All Articles