Unity.MVC4 lazy<T> is not working in ASP.NET MVC app

僤鯓⒐⒋嵵緔 提交于 2019-11-28 12:39:36

In general, instance constructors should do nothing more than do the proper null checks and store the incoming references. This makes construction of object graphs fast and reliable. Any initialization should be postponed to a later moment in time, the time the component is used for the first time.

This will in -most cases- prevent the need from delaying creation of a component for performance and if this really still is a problem, you might want to consider a container that has better performance.

For those spare moments that you need to delay the creation, don't use a Lazy<T> for this. Injecting Lazy<T> is a leaky abstraction, just as IDisposable is when placed on interfaces. Injecting a Lazy<T> leaks, because in theory, every dependency could be slow or could need to be postponed. To prevent having to make sweeping changes throughout the applications when a slower implementation of a service is introduced, we'd better make every dependency in the application a Lazy<IDependency> up front, because this saves us from having to make changes later on.

But that's of course silly and ugly. But even when the application is small enough that such sweeping change is affordable, why should the consumer know or care about the fact that that service needs lazy initialization? Isn't that an implementation detail? Why are we baking this lazy behavior into the contract of this service? Doing so makes our code and the tests we need to write more complicated. That's unneeded accidental complexity.

So instead of injecting a Lazy<IService1>, you should simply inject an IService1 and implement and register a proxy that implements lazy behavior. That's actually really easy to do as follows:

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) {
        return this.service.Value.Method2(foo);
    }
}

This proxy can be registered as follows:

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

Since I don't know how exactly to implement the lazy loading on DI registration level, I'm often using this way to lazy load services. I would share my approach with you.

Each service interface will inherit the IService interface which is base for all service interfaces.

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

Then, every service will implement his own interface:

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

My abstract BaseController class implements this logic

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

And finally, in my controller which inherits the BaseController I would be able to do this:

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

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

        return View();
    }
}

I'm curious to see better implementations for lazy loading services or get to know how to implement lazy loading on types registration level using Unity or Ninject.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!