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

3

我有一个通用工厂

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));
    }
}

我可以用它来解决在我的窗口类中的视图模型工厂问题:
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<>));

为了更好的可测试解决方案,最好将MRPersons替换为IMRPersons:
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 });
    ...
}

编辑2: 与Cyril Durand聊天期间,他提供了一种不需要引用ILifetimeScope的ViewModelFactory解决方案。以下是代码:

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个回答

1

如果您想将 ViewModelFactory<> 注册为 IViewModelFactory<>,可以使用 RegisterGeneric 方法实现。

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

那么你就可以解决IViewModelFactory<MRCar>,无需进行其他注册。

有关更多信息,请参见注册概念-开放式通用组件


针对第二个问题:
为了更好的可测试解决方案,甚至可以将MRPersons替换为IMRPersons
这并不容易,因为没有办法知道要使用哪个接口。您可以使用AsImplementedInterfaces,它等同于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<>,而只需要依赖于Func<TParam, T>


有没有办法通过自动代码在特定接口IMRPersons、IMRCar下注册MRPersons和MRCar呢?(当然前提是这些接口确实存在)。我的当前注册.AsSelf()现在已经足够了,但是对于更复杂的解决方案,类似于.As<IXXX>().AsSelf()这样的动态IXXX会很好。 - user2126375
@user2126375 为了解决 IViewModelFactory<MRPersons>,您不需要注册 MRPersonsAutofac 不需要这种类型。 - Cyril Durand
@user2126375 能否编辑您的问题(或发布一个新的问题),加入您的第二要求?这样更容易阅读,我会编辑我的回答 :) - Cyril Durand
Autofac知道类型约束而言,应该不需要注册此类型(请参阅https://dotnetfiddle.net/9OrQh6以获取实时演示)。也许您的情况有特殊要求。 - Cyril Durand
我创建了一个示例 https://dotnetfiddle.net/zm9FsA,它演示了如果调用该工厂的Create方法,则需要为MRPersons注册。 - user2126375
显示剩余5条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接