【问题标题】:Unity.MVC4 lazy<T> is not working in ASP.NET MVC appUnity.MVC4 lazy<T> 在 ASP.NET MVC 应用程序中不起作用
【发布时间】:2014-02-11 18:18:56
【问题描述】:

我正在使用 ASP.NET MVC 4 应用程序。

Home 控制器的构造函数有 2 个参数(Iservice1 service1, Iservice2 service2) 并非所有代码路径都使用任何服务(service1、service2),仅在某些代码路径中我需要 service1 实例/对象或 service2 实例/对象。

我不想使用 container.Resolve<Lazy<IService1>>();

从此链接 (http://msdn.microsoft.com/en-us/library/dn178463(v=pandp.30).aspx) 我了解到 unity.mvc 4 使用具有延迟加载支持的 unity 3,但如何在 ASP.NET MVC 4 中执行此操作。

【问题讨论】:

  • 您是否测量过注入未使用的依赖项的性能开销,是否确定注入这两个服务实际上是一个瓶颈?
  • 是的,我们已经严重影响了性能,提高性能的要点之一是延迟加载对象。谁能告诉我问题的答案
  • 你在Service1的构造函数中做了什么样的初始化?服务的构建应该很快。
  • Service1 或 Service2 的构造很简单,但控制器依赖于 10 个或更多服务,并且并非所有代码路径都需要所有 10 个服务,我的问题是如何使用 Bootstrap Unity 或延迟加载服务Unity.MVC 4
  • Steven,首先我要感谢您花费的时间,Service1 或 Service2 的构建很简单,但是控制器依赖于 10 个或更多的服务,并不是所有的代码路径都需要全部10 个服务,我的问题是如何使用 Bootstrap Unity 或 Unity.MVC 4 延迟加载服务

标签: asp.net-mvc inversion-of-control lazy-loading unity-container deferred-loading


【解决方案1】:

一般来说,实例构造函数应该做的只是正确的空检查和存储传入的引用。这使得对象图的构建速度更快,reliable。任何初始化都应该推迟到以后的某个时刻,也就是第一次使用组件的时间。

在大多数情况下,这将避免延迟创建组件以提高性能的需要,如果这仍然是一个问题,您可能需要考虑一个具有 better performance 的容器。

对于那些需要延迟创建的空闲时间,不要为此使用Lazy&lt;T&gt;。注入Lazy&lt;T&gt;leaky abstraction,就像IDisposable 放在接口上时一样。注入Lazy&lt;T&gt; 泄漏,因为理论上,每个依赖项都可能很慢或可能需要推迟。为了避免在引入较慢的服务实现时必须在整个应用程序中进行彻底的更改,我们最好将应用程序中的每个依赖项预先设置为Lazy&lt;IDependency&gt;,因为这样可以避免我们以后进行更改。

但这当然是愚蠢和丑陋的。但是,即使应用程序足够小,可以承受如此彻底的改变,消费者为什么要知道或关心该服务需要延迟初始化的事实呢?这不是一个实现细节吗?为什么我们要把这种懒惰的行为加入到这个服务的契约中呢?这样做会使我们的代码和我们需要编写的测试更加复杂。这是不需要的accidental complexity

因此,与其注入 Lazy&lt;IService1&gt;,不如简单地注入 IService1 并实现和注册一个实现惰性行为的代理。这实际上很容易做到,如下所示:

public class LazyService1Proxy : IService1
{
    private Lazy<IService1> service;

    public LazyService1Proxy(Lazy<IService1> service) => this.service = service;

    void IService1.Method1() => this.service.Value.Method1();

    object IService1.Method2(string foo) => this.service.Value.Method2(foo);
}

这个代理可以注册如下:

container.Register<IService1>(new InjectionFactory(c => 
    new LazyService1Proxy(
        new Lazy<IService1>(
            () => c.Resolve<RealService1Impl>()))));

【讨论】:

  • 如何在Ninject中写入最后一个Register部分?
【解决方案2】:

由于我不知道具体如何实现DI注册级别的延迟加载,所以我经常使用这种方式来延迟加载服务。我会和你分享我的方法。

每个服务接口都会继承 IService 接口,它是所有服务接口的基础。

public interface IService
{
    // I just want to be sure I'm instantiating ONLY services types.
    // This is my base interface 
}

public interface IMyService : IService
{
    void DoSomeWork();
}

然后,每个服务都会实现自己的接口:

public class MyService : IMyService
{
    public void DoSomeWork()
    {
        // Some action performed.
    }
}

我的抽象 BaseController 类实现了这个逻辑

public abstract class BaseController : Controller
{
    public T LoadService<T>() where T : IService
    {
        return (T)System.Web.Http.GlobalConfiguration.Configuration.DependencyResolver.GetService(typeof(T));
    }
}

最后,在我继承 BaseController 的控制器中,我可以做到这一点:

public class TestController : BaseController
{
    // Only parameterless constructor exists created by the compiler.

    [HttpPost]
    public ActionResult PerformWork()
    {
        var testService = LoadService<IMyService>();
        testService.DoSomeWork();

        return View();
    }
}

我很想看到延迟加载服务的更好实现,或者了解如何使用 Unity 或 Ninject 在类型注册级别实现延迟加载。

【讨论】:

猜你喜欢
  • 2012-06-15
  • 2015-05-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-01-16
  • 2020-04-22
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多