6

UserServiceコンストラクターには、 aIUnitOfWorkと aの2 つのパラメーターがありIUserRepositoryます。

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

の複数のインスタンスを区別するために名前付き登録を使用しているため、Unity コンテナーに をIUnitOfWork登録するときは、UserServiceを使用してパラメーターを明示的に指定する必要がありますInjectionConstructor

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

new ResolvedParameter<IUserRepository>()省略可能ですか?名前付き登録は必要ないので、Unity に暗黙的にこのパラメーターを推測してもらいたいと思います。 コードは次のようになります。

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

を使用する必要がない場合はいつでも、これが行われますInjectionConstructor

4

2 に答える 2

7

InjectionConstructorに基づいて、この RequiredInjectionConstructor を思いつきました。引数の任意のセットを指定でき、渡された注入パラメーターのセットを (少なくとも) 持つ必要があるコンストラクターを見つけようとします。この基準を満たすコンストラクターが複数ある場合は、パラメーターの数が最も少ないコンストラクターが選択されます。コンストラクターの残りのパラメーターは、名前のない解決済みパラメーターであると見なされます。

まだ単体テストの完全なスイートを実行していないので、問題が発生した場合はお知らせください。

/// <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;
    }
}
于 2014-02-26T02:53:17.890 に答える
2

Unity の DependencyAttribute でコンストラクターをデコレートしてもよろしいですか? このソリューションは単純で組み込みであり、名前付き依存関係を選択して選択できます。ただし、Unity goo を使用してコンストラクターを「ダーティ」にします。

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

別の解決策は、カスタムの BuilderStrategy と UnityContainerExtension を作成することです。これは、もう少し作業を行うことで実現できます。

于 2014-02-25T07:10:37.937 に答える