如何通过两种不同类型的参数数组统一注册?

How to register in unity passing a params array of two distinct types?

我是 Unity 新手。所以希望我问的是正确的事情,因为我仍在努力掌握实现 DI 和 IoC 的正确方法。我正在尝试创建一个使用特定构造函数初始化控制器的 MVC5 站点。

这就是我想要做的。我有一个基本控制器,它有一个构造函数,该构造函数采用特定类型的参数数组。

public abstract class DataModelController<TModel, TContext> : DataController<TContext>
    where TContext : OpenAccessContext,
                     new()
{
    public DataModelController(params IDataTransformer<TContext>[] dataTransformers) : base()
    {
        //...
    }
}

我有两个 IDataTransformer 的实现,我想一直使用它们来注入。

public class TrackCreatedTransformer : IDataTransformer<EntitiesModel>
{
    //...
}

public class TrackChangedTransformer : IDataTransformer<EntitiesModel>
{
    //...
}

随后我有 IDataTransformer 的其他实现,我需要为特定实现注入这些实现。

public class TrackClosedTransformer : IDataTransformer<EntitiesModel>
{
    //...
}

public class TrackSignedTransformer : IDataTransformer<EntitiesModel>
{
    //...
}

下面是我的抽象基控制器的三个实现:

public NormalModelController : DataModelController<NormalModel, EntitiesModel>
{
    public NormalModelController(params IDataTransformers<EntitiesModel>[] dataTransformers) : base()
    {
        //...
    }
}

public SpecialModelController : DataModelController<SpecialModel, EntitiesModel>
{
    public SpecialModelController(params IDataTransformers<EntitiesModel>[] dataTransformers) : base()
    {
        //...
    }
}

public AnotherSpecialModelController : DataModelController<AnotherSpecialModel, EntitiesModel>
{
    public AnotherSpecialModelController(params IDataTransformers<EntitiesModel>[] dataTransformers) : base()
    {
        //...
    }
}

所以我一直在尝试弄清楚如何统一处理参数数组,但总而言之,基本上是像这样注入的地方:

new NormalModelController(new TrackCreatedTransformer(), new TrackChangedTransformer());

在其他情况下,例如:

new SpecialModelController(new TrackCreatedTransformer(), new TrackChangedTransformer(), new TrackClosedTransformer());
new AnotherSpecialModelController(new TrackCreatedTransformer(), new TrackChangedTransformer(), new TrackSignedTransformer());

希望这个重构对你有用。在这种情况下使用组合而不是继承将有利于 Unity 注册。这也是@Steven 评论的补充。

所以这里有一个管理器,它是复合变压器。您会注意到它实现了 IDataTransformer。这不是严格要求的,但有助于确保复合管理器可以执行它委托给复合项的每个操作。构造函数接受一个选择器(在下一节中描述)以将转换器过滤为仅适用于此 TModel 的转换器。它还接受 IDataTransformer 的参数数组。如后所述,您将获得每个 IDataTransformer 的实例,但对象的构造应该很便宜并且这对于性能来说应该可以忽略不计。

public interface IDataTransformerManager<TModel, TContext> {}
public class DataTransformerManager<TModel, TContext> : 
    IDataTransformerManager<TModel, TContext>,
    IDataTransformer<TContext>
    where TContext : OpenAccessContext, new()
{
    private readonly IEnumerable<IDataTransformer<TContext>> _transformers;

    public DataTransformerManager(
        IDataTransformerSelector<TModel, TContext> dataTransformerSelector, 
        params IDataTransformer<TContext>[] allTransformers)
    {
        _transformers = dataTransformerSelector.SelectTransformers(allTransformers);
    }
}

接下来是变压器选择器。请根据需要自定义它,但它的工作是将所有转换器的集合过滤到仅适用于给定 TModel.

的转换器
public interface IDataTransformerSelector<TModel, TContext>
    where TContext : OpenAccessContext, new()
{
    IEnumerable<IDataTransformer<TContext>> 
        SelectTransformers(IEnumerable<IDataTransformer<TContext>> allTransformers);
}

public class DataTransformerSelector<TModel, TContext> :
    IDataTransformerSelector<TModel, TContext>
    where TContext : OpenAccessContext, new()
{
    public IEnumerable<IDataTransformer<TContext>> 
        SelectTransformers(IEnumerable<IDataTransformer<TContext>> allTransformers)
    {
        // Custom logic to map the model type to the transformers that apply
        if (typeof(TModel) == typeof(NormalModel))
        {
            return allTransformers.Where(transformer =>
                transformer.GetType() == typeof(TrackCreatedTransformer) ||
                transformer.GetType() == typeof(TrackChangedTransformer)).ToArray();
        }

        if (typeof(TModel) == typeof(SpecialModel))
        {
            return allTransformers.Where(transformer =>
                transformer.GetType() == typeof(TrackCreatedTransformer) ||
                transformer.GetType() == typeof(TrackChangedTransformer) ||
                transformer.GetType() == typeof(TrackClosedTransformer)).ToArray();
        }

        if (typeof (TModel) == typeof (AnotherModel))
        {
            return allTransformers.Where(transformer =>
                transformer.GetType() == typeof (TrackCreatedTransformer) ||
                transformer.GetType() == typeof (TrackChangedTransformer) ||
                transformer.GetType() == typeof (TrackClosedTransformer) ||
                transformer.GetType() == typeof (TrackSignedTransformer)).ToArray();
        }

        throw new InvalidOperationException("Unknown model type: " + typeof(TModel));
    }
}

现在你的控制器变得非常干净(在我看来)。您不再依赖于从自定义类型派生,您的控制器的构造函数也不需要知道转换器管理器构造函数的依赖关系。这也将使使用 mock 进行单元测试变得更加容易。

public class NormalModelController
{
    public NormalModelController(
        IDataTransformerManager<NormalModel, EntitiesModel> dataTransformerManager)
    {}
}

public class SpecialModelController
{
    public SpecialModelController(
        IDataTransformerManager<SpecialModel, EntitiesModel> dataTransformerManager)
    {}
}

public class AnotherModelController 
{
    public AnotherModelController(
        IDataTransformerManager<AnotherModel, EntitiesModel> dataTransformerManager)
    {}
}

最后是Unity注册。 IDataTransformerManager 和 IDataTransformerSelector 是使用开放泛型注册的,因此 Unity 可以解析您选择的任何泛型类型参数。然后使用名称注册 IDataTransformer,以允许使用同一接口进行多次注册,并允许在管理器构造函数中解析 params 数组以获取所有实例。

private static void UnityRegistration(IUnityContainer container)
{
    container.RegisterType(
        typeof(IDataTransformerSelector<,>), 
        typeof(DataTransformerSelector<,>));
    container.RegisterType(
        typeof(IDataTransformerManager<,>),
        typeof(DataTransformerManager<,>));

    container.RegisterType<IDataTransformer<EntitiesModel>, TrackCreatedTransformer>
        ("TrackCreatedTransformer");
    container.RegisterType<IDataTransformer<EntitiesModel>, TrackChangedTransformer>
        ("TrackChangedTransformer");
    container.RegisterType<IDataTransformer<EntitiesModel>, TrackClosedTransformer>
        ("TrackClosedTransformer");
    container.RegisterType<IDataTransformer<EntitiesModel>, TrackSignedTransformer>
        ("TrackSignedTransformer");
}