【问题标题】:SimpleInjector and [Export attibute]简单注射器和[导出属性]
【发布时间】:2014-10-21 06:32:09
【问题描述】:

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

另一个问题是关于延迟初始化...我已经阅读了here 如何注册延迟...但是我是否必须调用 Container.Verify()?

谢谢

【问题讨论】:

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


    【解决方案1】:

    在你的完整源代码中使用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 东西,比如找到你的视图,将它绑定到视图模型并在窗口/页面中显示视图等。 如果你想了解更多关于装饰器的信息,结合简单的注入器,你可以找到一些不错的文章here(不要忘记各种链接)。

    【讨论】:

      【解决方案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 随时为您提供帮助。

      【讨论】:

      • 我让导出工作,但每次我尝试使用 Ioc.Get 解决我的代码中的依赖项时,我得到“IoC 未初始化”......我在我的代码中使用很多 IoC.Get。在我的代码中,我根据cshandler.com/2013/03/basics-of-caliburn-micro-with-simple.html 覆盖了 GetInstance、BuildUp、GetAllInstances 方法
      • @advapi 随时将该问题作为您的代码的另一个问题发布,我们将非常乐意提供帮助...
      【解决方案3】:

      如果您同时显式注册惰性版本和普通版本的注册,您的对象图仍将是完全可验证的。看看这个注册:

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

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

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

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

      这里Lazy&lt;ITaggable&gt;注册使用GetInstance&lt;CorporateActionViewModel&gt;作为工厂方法,但CorporateActionViewModel没有显式注册。在验证过程中,Simple Injector 将创建Lazy&lt;ITaggable&gt;,这显然会成功,但它不会自动为您调用Lazy&lt;T&gt;.Value 属性(这是故意的,因为您推迟创建对象图可能是有原因的) .

      但是请重新考虑在整个代码库中注入 Lazy 的策略。这是一个坏主意和坏习惯。请阅读thisthis 了解更多信息。

      【讨论】:

        【解决方案4】:

        回答你的第二个问题。 (你知道你可以编辑你原来的问题。这样可以理解)

        我想我可以将它重构为

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

        【讨论】:

        • 我不知道我可以编辑帖子,我的错...我需要它是一个单例,因为它是整个代码之间共享的对象,应该只有一个...
        • 如果您需要单身人士,您可以将我的代码更改为:ContainerInstance.RegisterSingle 或使用您已有的代码。他的名字工厂把我吹错了方向
        猜你喜欢
        • 2017-11-13
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-09-07
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多