SimpleInjector and [Export attibute]

本文关键字:attibute Export and SimpleInjector | 更新日期: 2023-09-27 18:27:25

在WPF应用程序中,我使用Caliburn Micro进行MVVM模式。。。我想尝试另一个IoC,并希望重用大部分现有代码。。。

在我的应用程序中,我已经将所有可通过属性导出的类定义为

[Export(typeof(ITaggable))]
[Export(typeof(CorporateActionViewModel))]
[Export(typeof(IScreen))]
public class CorporateActionViewModel :...

我如何在不手动的情况下注册它们

ContainerInstance.Register<ITaggable, CorporateActionViewModel>();
ContainerInstance.Register<IScreen, CorporateActionViewModel>();
ContainerInstance.Register<CorporateActionViewModel, CorporateActionViewModel>();

另一个问题是关于Lazy初始化。。。我在这里读到了如何注册懒惰。。。但是我是否必须调用Container.Verify()?

感谢

SimpleInjector and [Export attibute]

在整个源代码中使用ExportAttribute来注册所有类型听起来违反了依赖反转原则。这本身就有问题,但肯定有几个缺点。

SimpleInjector不需要使用属性来查找要注册的类。这实际上是设计原则之一Simple Injector团队的成员。

如果您遵循视图模型(和相应视图)的SOLID原则,您可以很容易地(很容易…当然取决于您当前的设计…)删除该属性。

如果我们采用一个典型的LoB应用程序,其中我们在数据库中有一堆实体,我们可以将我们的视图模型/视图设计拆分为这些通用接口,您的视图模型将实现这些接口(一次一个):

//for a typical datagrid view of your entities with e.g. Add, Edit and Delete button
IWorkspace<TEntity>;
//for a typical edit view for one entity (including possible child entities)
IEditEntity<TEntity>;
//for choosing a specific foreign entity type from your edit view
//e.g. your editing an order and need to specify the customer
IChooseEntity<TEntity>

使用这些,我们将获得非常具体的视图模型,这些视图模型是SOLID的,如果你愿意,仍然可以为用户组成一个非常大的复杂视图。

您可以使用Simple Injector使用以下批量注册非常容易地注册这些类型:

container.RegisterManyForOpenGeneric(
   typeof(IChooseEntityViewModel<>), Assembly.GetExecutingAssembly());

这种设计的好处是,你可以用一个或多个装饰器包装你的视图模型,这些装饰器可以用于一些真正的MVVM,比如找到你的视图,将其绑定到视图模型,并在窗口/页面中显示视图等。如果你想阅读更多关于decorator的内容,结合简单的注入器,你可以在这里找到一些不错的文章(不要忘记各种链接)。

此查询将查找所有标有ExportAttribute 的类型

private IEnumerable<Type> GetExportedTypes()
{
    return from assembly in AppDomain.CurrentDomain.GetAssemblies()
           from type in assembly.GetTypes()
           where Attribute.IsDefined(type, typeof(ExportAttribute))
           select type;
}

此查询将查找为使用ExportAttribute 的类型发布的所有服务

private IEnumerable<Type> GetServicesFromType(Type type)
{
    return from attribute in Attribute
               .GetCustomAttributes(type, typeof(ExportAttribute))
           select ((ExportAttribute)attribute).ContractType;
}

这些查询可以像一样使用

var container = new Container();
foreach(var type in GetExportedTypes())
{
    foreach (var service in GetServicesFromType(type))
    {
        container.Register(service, type);
    }
}
container.Verify();

关于Verify()的问题?从不强制要求调用Verify,但始终建议这样做。诊断服务提供帮助。

如果显式注册注册的惰性版本和正常版本,则对象图仍然是完全可验证的。看看这个注册:

container.Register<ITaggable, CorporateActionViewModel>();
container.Register<Lazy<ITaggable>>(
    () => new Lazy<ITaggable>(container.GetInstance<ITaggable>));
container.Verify();

Verify将遍历所有显式注册,并尝试为每个注册创建一个实例。这意味着它将创建一个Lazy<ITaggable>实例。当然,能够创建Lazy<ITaggable>并不意味着可以创建CorporateActionViewModel,但是Simple Injector也将验证ITaggable注册。这两者结合在一起可以确保您的完整DI配置是可验证的。

然而,以下配置会给你一种虚假的安全感:

container.Register<Lazy<ITaggable>>(
    () => new Lazy<ITaggable>(container.GetInstance<CorporateActionViewModel>));
container.Verify();

这里,Lazy<ITaggable>注册使用GetInstance<CorporateActionViewModel>作为工厂方法,但CorporateActionViewModel没有明确注册。在验证过程中,Simple Injector将创建Lazy<ITaggable>,这显然会成功,但它不会自动为您调用Lazy<T>.Value属性(这是经过深思熟虑的,因为您可能有推迟创建对象图的原因)。

但是请重新考虑在整个代码库中注入Lazy的策略。这是个坏主意,也是个坏做法。请阅读此和此了解更多信息。

回答您的第二个问题。(你知道吗?你可以编辑你原来的问题。这可以让事情变得容易理解)

我想我可以把它重构成

  ContainerInstance.RegisterSingle<ISharedModuleObject>(
  new SharedModuleObject { DataLavorativa = DateTime.Today, 
  DataLavorativaPrecedente = DateTime.Today });

但是这样可以吗?

我不这么认为。你称它为工厂,所以RegisterSingle()注册一个单例实例是不好的。

我认为你的实现应该是:

public class SharedModuleObject : ISharedModuleObject
{
    public SharedModuleObject()
    {
        this.DataLavorativa = DateTime.Now;
        this.DataLavorativaPrecedente = DateTime.Now;
    }
    public DateTime DataLavorativaPrecedente { get; set; }
    public DateTime DataLavorativa { get; set; }
}

并将其注册为:

ContainerInstance.Register<ISharedModuleObject, SharedModuleObject>();

这将注册SharedModuleObject的一个瞬态实例。因此,每次从容器中解析时,都会得到一个新实例。

编辑:

从你的评论中,我理解你实际上需要一个单身汉。在这种情况下,你的代码还可以,但对我来说这似乎更干净了:

ContainerInstance.RegisterSingle<ISharedModuleObject, SharedModuleObject>();