为实现接口的所有类型注册通用工厂

时间:2015-07-28 11:39:15

标签: c# generics dependency-injection autofac

我有通用工厂

public interface IViewModelFactory<T> where T : IViewModel
{
    T Create<TU>(TU par);
}

public class ViewModelFactory<T> : IViewModelFactory<T> where T : IViewModel
{
    private readonly ILifetimeScope _scope;

    public ViewModelFactory(ILifetimeScope scope)
    {
        _scope = scope;
    }

    public T Create<TU>(TU par)
    {
        return _scope.Resolve<T>(new TypedParameter(typeof(TU), par));
    }
}

我可以用来解析我的窗口类中的viewmodel工厂:

public WRPersons(IViewModelFactory<MRPersons> viewModelFactory)
{
    var viewModel = viewModelFactory.Create(new MRPersonsUseCaseParams { Filter = 2 });
    ...
}

ViewModel通过以下代码实现

public class MRPersons : IViewModel
{
    public MRPersons(MRPersonsUseCaseParams par)
    {
        _filter = par.Filter;
    }
}

public class MRPersonsUseCaseParams
{
    public int Filter { get; set; }
}

在我的作文根中注册如下:

var builder = new ContainerBuilder();
builder.RegisterType<ViewModelFactory<MRPersons>>().As<IViewModelFactory<MRPersons>>();
builder.RegisterType<MRPersons>();

正如您可以看到的每个新的ViewModel(现在只有它的MRPerson),我需要在我的组合根中创建两个条目。因此,对于MRCar,它将是:

builder.RegisterType<ViewModelFactory<MRCar>>().As<IViewModelFactory<MRCar>>();
builder.RegisterType<MRCar>();

我想以某种方式自动化这些注册。我尝试了RegisterAssemblyTypes / AsClosedTypesOf但没有成功。有人可以帮帮我吗?

修改

基于答案代码行

builder.RegisterType<ViewModelFactory<MRPersons>>().As<IViewModelFactory<MRPersons>>();

替换为

builder.RegisterGeneric(typeof(ViewModelFactory<>)).As(typeof(IViewModelFactory<>)); 

全自动注册如下:

builder.RegisterAssemblyTypes(Assembly.GetEntryAssembly()).Where(x => iViewModelType.IsAssignableFrom(x) && x.IsClass).AsSelf();
builder.RegisterGeneric(typeof(ViewModelFactory<>)).As(typeof(IViewModelFactory<>));

对于更好的可测试解决方案,甚至可以通过IMRPersons替换MRPersons:

public class MRPersons : IViewModel, IMRPersons
{
    public MRPersons(MRPersonsUseCaseParams par)
    {
        _filter = par.Filter;
    }
}

public class MRPersonsUseCaseParams
{
    public int Filter { get; set; }
}

public interface IMRPersons 
{
}

因此,组合根中的注册看起来像(需要被纠正)

builder.RegisterAssemblyTypes(Assembly.GetEntryAssembly()).Where(x => iViewModelType.IsAssignableFrom(x) && x.IsClass).As<??????>.AsSelf();

这将允许我以下列方式将工厂传递给构造函数:

public WRPersons(IViewModelFactory<IMRPersons> viewModelFactory)
{
    var viewModel = viewModelFactory.Create(new MRPersonsUseCaseParams { Filter = 2 });
    ...
}

EDIT2: 在与Cyril Durand聊天期间,他为ViewModelFactory提供了解决方案,而没有参考ILifetimeScope。这是一个代码:

public interface IViewModelFactory2<T, TU> where T : IViewModel
{
    T Create(TU par);
} 
public class ViewModelFactory2<T, TU> : IViewModelFactory2<T, TU> where T : IViewModel
{
    private readonly Func<TU, T> _factory;

    public ViewModelFactory2(Func<TU, T> factory)
    {
        _factory = factory;
    }

    public T Create(TU par)
    {
        return _factory(par);
    } 
}

我的原始工厂也很好,因为它在组合根中呈现,可以使用对DI容器的强引用。

1 个答案:

答案 0 :(得分:1)

您希望将ViewModelFactory<>注册为IViewModelFactory<>,您可以使用RegisterGeneric方法进行注册。

builder.RegisterGeneric(typeof(ViewModelFactory<>)).As(typeof(IViewModelFactory<>)); 

然后,您无需任何其他注册即可解决IViewModelFactory<MRCar>

有关详细信息,请参阅Registration Concepts - Open Generic Components

问题的第二部分:

  

为了获得更好的可测试解决方案,甚至可以MRPersons替换IMRPersons

这并不容易,因为无法知道使用哪个界面。您可以使用等同于AsImplementedInterfaces的{​​{1}},但如果您有大量已实现的界面,则可能会出现问题。

As<IMRPersons>().As<IViewModel>()

或者您可以使用将所有builder.RegisterAssemblyTypes(Assembly.GetEntryAssembly()) .Where(x => iViewModelType.IsAssignableFrom(x) && x.IsClass) .AsImplementedInterfaces(); 注册为X的约定,但我并不是此类注册的忠实粉丝。

IX

顺便说一句,在聊天之后,我们发现你根本不需要builder.RegisterAssemblyTypes(Assembly.GetExecutingAssembly()) .Where(x => iViewModelType.IsAssignableFrom(x) && x.IsClass) .As(t => t.GetInterfaces().Where(i => i.Name.EndsWith(t.Name))); ,但你只需依赖IViewModelFactory<>