简单的注入器和[导出属性]

时间:2014-10-21 06:32:10

标签: c# wpf mvvm caliburn.micro simple-injector

在WPF应用程序中我正在使用Caliburn Micro for 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初始化的另一个问题是......我已经阅读了here如何注册懒惰...但是我是否必须调用Container.Verify()?

由于

4 个答案:

答案 0 :(得分:2)

此查询将查找标有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永远不是强制性的,但始终建议。 Diagnostic Services可以提供帮助。

答案 1 :(得分:2)

如果您明确注册了注册的延迟版和普通版,则对象图仍然可以完全验证。看看这个注册:

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

container.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的策略。这是一个坏主意和不好的做法。有关详情,请参阅thisthis

答案 2 :(得分:2)

使用ExportAttribute完整源代码只是为了注册所有类型听起来像违反了依赖性倒置原则。这本身就有问题,但肯定有几个缺点。

Simple Injector无需使用属性来查找要注册的类。它实际上是design principles之一 简单的注射器工作人员。

您可以轻松(很容易......取决于您当前的设计场景......)如果您遵循视图模型(和相应视图)的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,如果您愿意,它仍然可以组成一个非常大的复杂视图。

您可以使用batch registration使用Simple Injector非常轻松地注册这些类型:

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

作为此设计的一个奖励,您可以使用一个或多个装饰器包装您的视图模型,这些装饰器可用于某些真正的MVVM内容,例如查找视图,将其绑定到viewmodel并在窗口/页面中显示视图等。 如果你想了解更多关于装饰器的信息,结合简单的注入器,你可以找到一些不错的文章here(不要忘记各种链接)。

答案 3 :(得分:2)

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

  

我想我可以将它重构为

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