【问题标题】:Inject same DataContext instance across several types with Unity使用 Unity 在多种类型中注入相同的 DataContext 实例
【发布时间】:2009-05-22 10:15:12
【问题描述】:

假设我有 IRepository 接口及其实现 SqlRepository,它以 LINQ to SQL DataContext 作为参数。还假设我有 IService 接口及其实现服务,它需要三个 IRepository、IRepository 和 IRepository。演示代码如下:

public interface IRepository<T> { }

public class SqlRepository<T> : IRepository<T>
{
    public SqlRepository(DataContext dc) { ... }
}

public interface IService<T> { }

public class Service<T,T1,T2,T3> : IService<T>
{
    public Service(IRepository<T1> r1, IRepository<T2>, IRepository<T3>) { ... }
}

在创建 Service 类时是否可以通过相同的 DataContext 注入所有三个存储库?

【问题讨论】:

    标签: c# dependency-injection unity-container datacontext


    【解决方案1】:

    您需要做的就是确保在向 Unity 容器注册 Datacontext 时,在配置中使用 PerResolveLifetimeManager

    <type type="<namespace>.DataContext, <assembly>">
        <lifetime type="Microsoft.Practices.Unity.PerResolveLifetimeManager, Microsoft.Practices.Unity" />
    </type>
    

    或在代码中:

    container.RegisterType<DataContext>(new PerResolveLifetimeManager());
    

    然后,每当容器解析 Service 时,任何还需要 DataContext 的依赖项都将提供完全相同的依赖项。但下一个解析Service 的请求将创建一个新的DataContext

    【讨论】:

      【解决方案2】:

      我想我知道你想做什么。我在同一条船上,正在尝试提出解决方案。

      我的服务层对传入的请求执行操作,它的作用取决于内容。它将它传递给一系列责任类链。我希望在调用的服务方法的生命周期内将相同的上下文传递给所有类

      您可以指定 PerResolveLifetimeManager。到目前为止,它似乎正在处理我的测试用例:

      服务类:

      public interface IServiceClass
      {
          void DoService();
      }
      
      class ServiceClass : IServiceClass
      {
          private IHandler Handler { get; set; }
      
          public ServiceClass(IHandler handler)
          {
              Handler = handler;
          }
      
          public void DoService()
          {
              Handler.HandleRequest();
          }
      }
      

      IHandler 由两个类实现,并执行责任链模式:

          public interface IHandler
      {
          void HandleRequest();
      }
      
      class Handler : IHandler
      {
          private IDataContext DataContext { get; set; }
          public Handler(IDataContext dataContext)
          {
              DataContext = dataContext;
          }
      
          public void HandleRequest()
          {
              DataContext.Save("From Handler 1");
          }
      }
      
      class Handler2 : IHandler
      {
          private IDataContext DataContext { get; set; }
          private IHandler NextHandler { get; set; }
      
          public Handler2(IDataContext dataContext, IHandler handler)
          {
              DataContext = dataContext;
              NextHandler = handler;
          }
      
          public void HandleRequest()
          {
              if (NextHandler != null)
                  NextHandler.HandleRequest();
      
              DataContext.Save("From Handler 2");
          }
      }
      

      如您所见,两个处理程序都接受一个 IDataContext 实例,我希望它们在它们中都相同。 Handler2 也接受一个 IHandler 的实例来传递控制权(这里两者都是为了演示,但实际上,只有一个会处理请求......)

      IDataContext。在构造函数中,我初始化了一个 Guid,并在其运行期间输出它,以便我可以查看它的调用是否使用相同的实例:

      public interface IDataContext
      {
          void Save(string fromHandler);
      }
      
      class DataContext : IDataContext
      {
          private readonly Guid _guid;
      
          public DataContext()
          {
              _guid = Guid.NewGuid();
          }
      
          public void Save(string fromHandler)
          {
              Console.Out.WriteLine("GUI: [{0}] {1}", _guid, fromHandler);
          }
      }
      

      最后是服务的注册和调用:

          private IUnityContainer container;
          private void InitializeUnity()
          {
              container = new UnityContainer();
              container.RegisterType<IHandler, Handler2>("Handler2",
                  new InjectionConstructor(new ResolvedParameter<IDataContext>(), new ResolvedParameter<IHandler>("Handler1")));
              container.RegisterType<IHandler, Handler>("Handler1");
              container.RegisterType<IDataContext, DataContext>(new PerResolveLifetimeManager());
              container.RegisterType<IServiceClass, ServiceClass>("MyClass", new InjectionConstructor(new ResolvedParameter<IHandler>("Handler2")));
          }
      
          private void CallService()
          {
              var service = container.Resolve<ServiceClass>("MyClass");
              service.DoService();
      
              // Resolving and calling again to simulate multiple resolves:
              service = container.Resolve<ServiceClass>("MyClass");
              service.DoService();
          }
      

      这是我得到的输出:

      GUI: [f2250055-8a5f-4f80-a1b6-bcc5574138cf] From Handler 1
      GUI: [f2250055-8a5f-4f80-a1b6-bcc5574138cf] From Handler 2
      GUI: [22a5c0a3-3c5c-4683-807d-bf2b43f3cd0a] From Handler 1
      GUI: [22a5c0a3-3c5c-4683-807d-bf2b43f3cd0a] From Handler 2
      

      希望这堵文字墙回答了您的问题...如果不是很抱歉,它确实激发了我需要实施的解决方案...

      【讨论】:

        【解决方案3】:

        如果我正确理解了您的问题(并且如果您使用的是统一...我想您这样做是因为您已将其标记为统一),您可以执行以下操作:

        在您的存储库实现中,

        [InjectionConstructor]
        public SqlRepository(
            [Dependency] DataContext ctx)
        

        但是您必须以相同的方式标记服务构造器并使用容器来解析您的服务以及存储库。 DataContext 也必须在容器中才能使其工作。

        另一种方法是对您的存储库执行类似的操作:

        [InjectionMethod]
        public void Initialize(
            [Dependency] DataContext ctx
        

        如果你想在你的服务构造函数中使用 Unity 和 BuildUp 方法,这将告诉 unity 调用这个方法......像这样:

        unitycontainer.BuildUp<IRepository>(repository);
        

        我想这不是您想要的,但请告诉我我是否走在正确的轨道上,我会看看能否进一步帮助您...

        干杯/J

        【讨论】:

        • 感谢您的尝试,但实际上,我想要完成的是在服务类初始化期间使用相同的 DataContext 实例。这是支持 Repository1、Repository2 和 Repository3 的事务所必需的。 ContainertLifeTime 在我的情况下不起作用,因为我想为每个新的服务实例都有新的 DataContext。
        • 其实我觉得他的做法和我上面的#2差不多。他正在注入 Initialize 方法并在那里创建容器,然后在调用后处理它。像这样:--> 预调用。使用 ContainerControlledLifetimeManager 初始化 Unity Container,使它们成为单例。 --> Service Initialize call:解析datacontext,只要precall中创建的Container在范围内,就会一直是同一个datacontext。 --> 调用后:处理容器...
        【解决方案4】:

        您是否尝试过对统一容器使用 RegisterInstance() 方法?这样的事情可能会起作用:

        公共静态 UnityContainer CreateContainer() { UnityContainer 容器 = 新 UnityContainer();

                try
                {
                    var section = ConfigurationManager.GetSection("unity") as UnityConfigurationSection;
        
                    if (section != null)
                    {
                        section.Containers[0].Configure(container);
                    }
                }
                catch (Exception ex)
                {
                    TraceLogger.LogMessage("Configurarion Error for Unity Container", ex.Message, TraceEventType.Critical);
                    Environment.Exit(1);
                }
        
        
                container.RegisterInstance(new DataContext());
                return container;
            }
        

        现在,每次此容器尝试构建需要 DataContext 的对象时,都会传递相同的实例。您甚至可以在注册它的实例之前配置 DataContext。

        更新: 一个选择(现在,我不知道这是否真的是一个好习惯,但这对我有用)是为您要创建的每个对象创建一个不同的容器。比如:

        UnityContainer container1 = ContainerFactory.CreateContainer();
        UnityContainer container2 = ContainerFactory.CreateContainer();
        UnityContainer container3 = ContainerFactory.CreateContainer();
        MyObject1 object1 = container1.Resolve<MyObject1>();
        MyObject2 object2 = container2.Resolve<MyObject2>();
        MyObject3 object3 = container3.Resolve<MyObject3>();
        

        或者更概括的方式:

        MyObject1 object1 = ContainerFactory.CreateContainer().Resolve<MyObject1>();
        MyObject1 object2 = ContainerFactory.CreateContainer().Resolve<MyObject2>();
        MyObject1 object3 = ContainerFactory.CreateContainer().Resolve<MyObject3>();
        

        嗯,有很多方法可以做到这一点,创建一个列表,使用工厂模式。希望对你有帮助

        【讨论】:

        • 感谢您的建议。不幸的是,只有在使用多个 DataContext 参数创建单个对象时,我才需要相同的数据上下文。对于每个新的服务类,我都需要新的 DataContext。
        猜你喜欢
        • 1970-01-01
        • 2018-03-10
        • 1970-01-01
        • 2014-11-23
        • 1970-01-01
        • 1970-01-01
        • 2013-06-30
        • 2011-12-06
        • 1970-01-01
        相关资源
        最近更新 更多