【问题标题】:Inject a property (recursively) without passing it as a parameter注入属性(递归)而不将其作为参数传递
【发布时间】:2017-04-03 15:12:31
【问题描述】:

我正在努力实现一些可能不可能的事情。

我们有一个大型的 MVC 5 应用程序。我创建了一个小型 MVC 项目来模拟和解释我想要应用到那个大型 MVC 项目中的内容。

我有一个具有唯一 ID 的控制器。在此示例项目中,将为每个请求重新生成唯一 ID。在 MVC 项目中,它有点复杂和不同。但是,它与本示例的范围无关。

public class FooController : Controller
{
    public string UniqueId = Guid.NewGuid().ToString("N");
    public ActionResult Index()
    {
        var worker = new WorkerA();
        worker.DoWork();
        return View();
    }
}

FooController 创建 WorkerA 再创建 WorkerB 再创建 WorkerC 等等。工人不一样。它们没有相同的接口/实现。为了使示例简单,我让它们看起来很相似。

这里是工人:

public class WorkerA
{
    public string UniqueId = string.Empty;

    public void DoWork()
    {
        var worker = new WorkerB();
        worker.DoWork();
        //...
        //...

    }
}
public class WorkerB
{
    public string UniqueId = string.Empty;

    public void DoWork()
    {
        var worker = new WorkerC();
        worker.DoWork();
    }
}

我想将属性 UniqueId 注入到 worker 中,而不必将其作为参数传递。

我想避免这样做:

public WorkerA(string uniqueId)
{
    UniqueId = uniqueId;
}

但我需要为所有其他工人做同样的事情。

编辑

有没有办法通过 ninject 实现这一目标?

【问题讨论】:

  • 初始化的时候不能设置属性吗?像这样var worker = new WorkerA() { UniqueId = uniqueId }
  • Worker 类的 uniqueId 是否与“父”FooController 类的原始 UniqueID 相同。还是所有 UniqueId 都是唯一的?
  • 为什么不想传递变量?
  • 将值注入每个Worker 比必须依赖上下文数据(如线程/http-context)更明智
  • @billybob 我认为您的问题的意义并不明确。目前尚不清楚您是否希望请求中的所有工作人员都具有相同的 ID。目前还不清楚是否也应该有未初始化(未注入)的 id。

标签: c# asp.net-mvc design-patterns asp.net-mvc-5 ninject


【解决方案1】:

您可以通过以下方式使用 Microsoft.Practices.Unity 实现您想要的:

 public class WorkerA
    {
        [Dependency]
        public string UniqueId { get; set; }

    }

    public class WorkerB
    {
        [Dependency]
        public string UniqueId { get; set; }
    }

然后:

 var container = new UnityContainer();
            container.RegisterType<WorkerA>(new InjectionProperty(nameof(WorkerA.UniqueId),"WorkerAValue"));
            container.RegisterType<WorkerA>(new InjectionProperty(nameof(WorkerB.UniqueId), "WorkerBValue"));

稍后,您可以从配置了所需属性的容器中请求实例:

var workerA = container.Resolve<WorkerA>();
var workerB = container.Resolve<WorkerB>();

【讨论】:

    【解决方案2】:

    你可以这样做:

    worker.GetType().GetField("prop")?.SetValue(worker, "guid");
    

    【讨论】:

      【解决方案3】:

      您可以创建一个单例类来管理 GUID 并以这种方式将其传递给子类。这样您仍然可以在构造函数中执行此操作,但不必将其作为参数传递

      public class GUIDManager
      {
      
          private static GUIDManager _instance;
          private Guid _activeGuid;
      
          public Guid ActiveGuid {
              get { return _activeGuid; }
              set { _activeGuid = value; }
      
          }
      
          private GUIDManager()
          {
              if (_activeGuid == null)
                  _activeGuid = new Guid();
          }
      
          public static GUIDManager GetInstance()
          {
      
              if(_instance == null)
              {
                  _instance = new GUIDManager();
              }
      
              return _instance;
          }
      
      }
      
      public class WorkerB
      {
      
          public string UniqueId = string.Empty;
      
          public WorkerB()
          {
              var manager = GUIDManager.GetInstance();
              UniqueId = manager.ActiveGuid.ToString();
          }
      
          public void DoWork()
          {
              var worker = new WorkerC();
              worker.DoWork();
          }
      }
      

      【讨论】:

      • 我已经考虑过那个解决方案,但是,我更喜欢使用注射来解决我的问题。
      【解决方案4】:

      从您的问题来看,我并不完全清楚同一请求中的所有工作人员是否获得相同的 ID。如果他们都应该获得相同的 ID,那很简单:

      将ID包装在一个类中并使用InRequestScope()

      public class BrowserTabId 
      {
          public string browserTabId;
          public BrowserTabId(string tabId)
          {
              if(string.IsNullOrEmpty(tabId))
              {
                  throw new NullArgumentException();
              }
              this.browserTabId = tabId;
          }
      
          public string Id { get { return this.browserTabId; } }
      }
      
      Bind<BrowserTabId>()
          .ToMethod(ctx => 
              new BrowserTabId(HttpContext.Items["BrowserTabId"] as string)))
          .InRequestScope();
      

      出于可测试性的原因,您还可以在接口IUniqueRequestId 上打耳光并为此创建绑定。

      这将导致在同一请求期间创建的所有工作人员/对象接收相同的BrowserTabId。如果您不想使用 c-tor 注入,您可以改用属性注入。如果您不想注入所有类型的值,则使用When(..) 条件来指定何时注入以及何时不注入。将此与 null-object pattern 结合使用,以防止 ninject 抱怨它无法注入请求的类型。

      属性注入

      如下调整工人:

      public class WorkerA
      {
          [Inject]
          public BrowserTabId BrowserTabId { get; set; }
      
          ....
       }
      

      但是请注意,要使其正常工作,就像普通的构造函数注入一样,WorkerA 必须由 ninject 实例化,或者 Ninject 必须由 Ninject.Inject(workerAInstance) 告知其存在

      范围界定

      由于您提到实际应用程序中 ID 的生命周期要复杂一些,我想您将不得不使用与 InRequestScope 不同的东西 - 可能会滚动您自己的范围(通过使用 InScope(...))。或者也许,InCallScope() 是可行的选择。但是,在不知道您需要什么的情况下,很难正确地为您提供建议。

      【讨论】:

      • 其实在主应用程序中,我们有一个AuthorizeAttribute。在该属性中,我们检索一个唯一 ID(特定于用户选项卡)。稍后,我想使用 browserTabId 并将其与 sessionId 结合使用。我现在有很多(数千)个班级都收到sessionId。我想避免这样做,因为他们拥有的唯一参数将是sessionIdbrowserTabId。请给我一个具体的例子如何使用它,好吗?
      • 顺便说一句,browserTabIdOnActionExecuting 函数中注入控制器。我是这样理解的:_browserTabId = HttpContext.Items["BrowserTabId"] as string;
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-08-04
      • 2017-11-19
      • 1970-01-01
      • 2011-08-15
      • 1970-01-01
      相关资源
      最近更新 更多