Showing posts with label HybridHttpOrThreadLocalScoped. Show all posts
Showing posts with label HybridHttpOrThreadLocalScoped. Show all posts

Saturday, September 8, 2012

Structuremap scopes and life cycles with example

This article is a detailed version of Basic structuremap scopes. I am trying to keep this article to focus generic DI/IOC container and not just for structuremap. While using any DI/IOC container you must be careful on two things.

  1. Request context
  2. From which context you are requesting object from the structuremap. Basically there are two contexts in a web application you can make request from. The first one is HttpContext and the other is Thread (ThreadContext).

  3. Object's scope
  4. In general the scope defines lifetime of an object. To be accurately with reference to DI/IOC container it is not the lifetime, it is better to say the object's accessiblity. That is, the object created in one scope cannot be referred in another scope. Also the DI/IOC container itself will not dispose the object at the end of the scope. We should explicitly dispose them or GC may take care at later point. For further reading How to dispose objects created by structuremap.


Let's look at the structuremap life cycles. Download the project for this example. I am using windows authentication in this project. Give your machine credentials to login the application. This project UI will not display any result. I am using log4net for results. Results are stored at "_ApplicationLogs\general.txt".

In this example I am using 'RuntimeHelpers.GetHashCode' to get the object hash code for each object. Since we write results in log file for objects comparison, the object hash code for each object helps us to make sure whether they are same object or different object.

  1. Example - transient objects - a.k.a. non-singleton
    • Registry class
    • In registry class, if you don't mention the life cycle, by default the structuremap creates transient object. That is, new object for each structuremap request.
      Code:
      public class ApplicationRegistry : Registry
      {
          public ApplicationRegistry()
          {
              Scan(scanner => 
              { 
                  For<IVehicle>().Use<Car>();                
              });            
          }
      }
      
    • Consumer code
    • In the following code, we are requesting structuremap two times. It returns different object each time. Have a look at the log result
      Code:
      public partial class  _Default : System.Web.UI.Page
      {
          ILog logger = log4net.LogManager.GetLogger("GeneralLog");
          protected void Page_Load(object sender, EventArgs e)
          {
              IVehicle Car = ObjectFactory.GetInstance<IVehicle>();
              logger.Info("First Car :" + Car.GetVehicleID() + " Login user:" + User.Identity.Name);
      
              Car = ObjectFactory.GetInstance<IVehicle>();
              logger.Info("Second Car :" + Car.GetVehicleID() + " Login user:" + User.Identity.Name);
          }
      }
      
    • Log result (log4net log result)
    • 09/08/2012 20:54:08.816 [12] INFO  GeneralLog 
      Message: First Car :3473973 Login user:My-PC\Pandian
      Exception: 
      
      09/08/2012 20:54:08.840 [12] INFO  GeneralLog 
      Message: Second Car :27605492 Login user:My-PC\Pandian
      Exception:
      

  2. Explicit call - transient objects - way 1
  3. The following approach works exactly similar way as explained in example-1.
    • Registry class
    • Code:
      For<IVehicle>().Transient().Use<Car>();
      

  4. Explicit call - transient objects - way 2
  5. This approach too works exactly similar way as explained in example-1 and example-2. The word “request” in the below code doesn't mean HttpRequest. The structuremap returns transient object for each request arrived at it.
    • Registry class
    • Code:
      For<IVehicle>().LifecycleIs(new UniquePerRequestLifecycle()).Use<Car>();
      

  6. Singleton objects
  7. Have a look at the following code and the log, it is very clear that the structuremap creates singleton object across requests. The object hash code 14145203 is same for all the four requests.
    • Registry class
    • Code:
      For<IVehicle>().Singleton().Use<Car>();
      
      For this example I logged in from two different browsers with two different credentials.
    • Log result (log4net log result)
    09/08/2012 22:15:52.277 [5] INFO  GeneralLog 
    Message: First Car :14145203 Login user:My-PC\Pandian
    Exception: 
    
    09/08/2012 22:15:52.313 [5] INFO  GeneralLog 
    Message: Second Car :14145203 Login user:My-PC\Pandian
    Exception: 
    
    09/08/2012 22:16:01.072 [5] INFO  GeneralLog 
    Message: First Car :14145203 Login user:My-PC\TestUser
    Exception: 
    
    09/08/2012 22:16:01.073 [5] INFO  GeneralLog 
    Message: Second Car :14145203 Login user:My-PC\TestUser
    Exception: 
    

  8. Explicit call - Singleton objects
  9. This method also creates singleton object across requests as explained in example 4. The only difference is that you can also pass constructor argument.
    Code:
    For<IVehicle>().Use(new Car());
    

  10. Tricky, it creates singleton object
  11. When we mention the instance in "Use" function it creates singleton object as I said in the 5th example. The "Trainsient()" request is ignored here.
    Code:
    For<IVehicle>().Transient().Use(new Car());
    

  12. Transient objects with constructor arguments.
  13. As opposed to example 5, the following statement in registry creates transient object and it lets you to pass constructor argument. Note, you should modify Car class to accept constructor argument.
    Code:
    For<IVehicle>().Use(context => new Car());
    

  14. Singleton per HttpRequest
  15. The following statement in registry class creates singleton object per request.I am logging in from two different machines. If you look at the log, you can see that it uses only one object per request.
    Code:
    For<IVehicle>().LifecycleIs(new HttpContextLifecycle()).Use<Car>();
    
    In the below log, the first two and the second two results are same. That means it creates new object for every request.
    09/08/2012 22:31:59.870 [19] INFO  GeneralLog 
    Message: First Car :19320046 Login user:My-PC\Pandian
    Exception: 
    
    09/08/2012 22:31:59.888 [19] INFO  GeneralLog 
    Message: Second Car :19320046 Login user:My-PC\Pandian
    Exception: 
    
    09/08/2012 22:32:07.944 [19] INFO  GeneralLog 
    Message: First Car :57289035 Login user:My-PC\TestUser
    Exception: 
    
    09/08/2012 22:32:07.945 [19] INFO  GeneralLog 
    Message: Second Car :57289035 Login user:My-PC\TestUser
    Exception: 
    

  16. Singleton per HttpSession
  17. Very similar to example 8, but it creates singleton object per session.
    Code:
    For<IVehicle>().LifecycleIs(new HttpSessionLifecycle()).Use<Car>();
    

  18. Thread context
  19. The following statement in registry class creates singleton object per thread.
    Code:
    For<IVehicle>().LifecycleIs(new ThreadLocalStorageLifecycle()).Use<Car>();
    
    I have changed a bit in the application's consumer code. There are three threads here. One parent thread and the other two threads are created by me. So there are three threads and three distinct objects.
    Code:
    public partial class  _Default : System.Web.UI.Page
    {
        ILog logger = log4net.LogManager.GetLogger("GeneralLog");
        //CLR runs this method as part of parent thread's execution
        protected void Page_Load(object sender, EventArgs e)
        {
            IVehicle Car = ObjectFactory.GetInstance<IVehicle>();
            logger.Info("First Car :" + Car.GetVehicleID() + "  Main Thread id: " + Thread.CurrentThread.ManagedThreadId);
    
            Car = ObjectFactory.GetInstance<IVehicle>();
            logger.Info("Second Car :" + Car.GetVehicleID() + "  Main Thread id: " + Thread.CurrentThread.ManagedThreadId);
      
            Thread thread1 = new Thread(new ThreadStart(ThreadFunction));
            thread1.Start();
    
            Thread thread2 = new Thread(new ThreadStart(ThreadFunction));
            thread2.Start(); 
        }
        private void ThreadFunction()
        {
            IVehicle Car = ObjectFactory.GetInstance<IVehicle>();
            logger.Info("First Car :" + Car.GetVehicleID() + " Thread id: " + Thread.CurrentThread.ManagedThreadId);
    
            Car = ObjectFactory.GetInstance<IVehicle>();
            logger.Info("Second Car :" + Car.GetVehicleID() + " Thread id: " + Thread.CurrentThread.ManagedThreadId);
        }
    }
    
    In the following log, there are three threads 6, 7 and 11. The three objects created for each thread. This is singleton per thread.
    09/08/2012 22:53:41.562 [6] INFO  GeneralLog 
    Message: First Car :14509978 Main Thread id: 6
    Exception: 
    
    09/08/2012 22:53:41.815 [6] INFO  GeneralLog 
    Message: Second Car :14509978 Main Thread id: 6
    Exception: 
    
    09/08/2012 22:53:41.998 [7] INFO  GeneralLog 
    Message: First Car :15366892 Thread id: 7
    Exception: 
    
    09/08/2012 22:53:41.999 [7] INFO  GeneralLog 
    Message: Second Car :15366892 Thread id: 7
    Exception: 
    
    09/08/2012 22:53:42.000 [11] INFO  GeneralLog 
    Message: First Car :7995840 Thread id: 11
    Exception: 
    
    09/08/2012 22:53:42.047 [11] INFO  GeneralLog 
    Message: Second Car :7995840 Thread id: 11
    Exception: 
    

  20. Hybrid context
  21. I have a special requirement for this example. Considering the above example-10, I need the first object (which is created in the parent thread) should be HttpContext scoped. The other two objects created inside the thread should be ThreadLocal scoped. In hybrid context the structuremap should automatically identify the request scope and it should return the object for the same scope. Generally HttpContext supersedes Thread context. This requirement can be solved as follows.
    Code:
    For<IVehicle>().HybridHttpOrThreadLocalScoped().Use<Car>();
    

    Also it is worth mentioning that, if you look at the HttpContext inside the main thread, it is available; it is not null. But it is not available for child threads; HttpContext is null.

    You can't find any difference in the log result, but the objects created for HttpContext are cached. Objects created for thread local scope are not cached.

Friday, August 24, 2012

Ways to prevent connection/resource leaks - Part 2

You may use these techniques to handle any expensive resource leaks, not just for connection leaks. In Part 1, I am explaining how to fix resource leaks in simpler ways. In this article I am explaining how to fix resource leaks with structuremap and how the structuremap object creation machnism works in multithreaded environment. Structuremap scopes explained here. Also I am explaining evils of singleton object in a multi-threaded environment.

  1. Fix for heterogeneous application - 1

  2. a. If your application is partially/not dependent on ORM.
    b. If your application uses DI/IOC tools like structure map.
    c. If your application is single threaded.

    Solution


    The fact is that singleton objects created by structuremap for HttpContext scope are cached. To create singleton object use the following line of code in your structuremap registry.
    Code:
    For<SqlConnection>().HttpContextScoped().Use(new SqlConnection());
    
    Since it is cached, you can easily dispose it in the global.asax file's Application_EndRequest event by calling the following method.
    Code:
    ObjectFactory.ReleaseAndDisposeAllHttpScopedObjects();
    

  3. Fix for heterogeneous application - 2

  4. a. If your application is partially/not dependent on ORM.
    b. If your application uses DI/IOC tools like structure map.
    c. If your application is multi-threaded, but uses limited number of threads.

    Solution


    The following line in structuremap registry creates object for both thread scope and http context scope.
    Code:
    For<SqlConnection>().HybridHttpOrThreadLocalScoped().Use(new SqlConnection());
    

    It requires two operations. One is thread scope cleanup and the other is HttpContext scope cleanup.

    In structuremap, thread scoped singleton objects are not cached. The above line of code creates one singleton object for each thread. The thread scoped singleton objects must be disposed at your own cost. That means you must explicitly dispose at the end of every thread in your application. Such as

    Code:
    ObjectFactory.GetInstance<SqlConnection>().Dispose();
    

    Now, the HttpContext scoped objects can be disposed by calling the following method in Application_EndRequest event.

    Code:
    ObjectFactory.ReleaseAndDisposeAllHttpScopedObjects();
    

  5. Fix for heterogeneous application - 3

  6. a. If your application is partially/not dependent on ORM.
    b. If your application uses DI/IOC tools like structure map.
    c. If you are restoring HttpContext.Current within the thread. That is, within a thread you cannot access HttpContext variables, it will be null. But in some cases you may need HttpContext data within a thread and you pass HttpContext.Current as parameter to the thread. This way you restore the HttpContext within a thread. Such as
    Code:
    //Thread method, this method will be executed in parallel by more than 1 threads. ThreadParameters is a class created by me.
    private void BindDataThread(ThreadParameters parameter)
    {
     //I need to access data from HttpContext, restoring it.
     HttpContext.Current = parameter.CurrentHttpContext;
     var connection = ObjectFactory.GetInstance<SqlConnection>();
    }
    

    If you use the above line, it will create a singleton object for HttpContextScope and the same object will be served for various threads from structuremap. Since SqlConnection object is not thread safe it will throw random errors. That means, all threads will be using the same SqlConnection object, one thread will be in retrieving data from the database and the other will be in opening the connection, the third one will be in closing the connection by using the same connection object.

    So what happens to the thread local scope? That I mentioned “HybridHttpOrThreadLocalScoped” in the structuremap registry to create object for thread local scope. Well, in structuremap the HttpContext scope takes precedence by checking HttpContext.Current != null. As soon as you assign "HttpContext.Current = parameter.HttpContext", it will only create object for HttpContext scope within a thread; but it supposed to create object for thread local scope!!

    So what can be the solution?

    Code:
    //For().HybridHttpOrThreadLocalScoped().Use(new SqlConnection());
    For<IVehicle>().LifecycleIs(new ThreadLocalStorageLifecycle()).Use<Car>();
    

    You may try by using the second option (the uncommented) instead of the first option (the commented) as I mentioned above.

    How it will work?

    This creates a singleton object for each thread. That is, the structuremap creates objects per thread basis, no matter for which context the object is requested.

    On the other hand, it can be overhead. Objects created in the page handlers/controllers are run by main thread. We have to dispose them too.

    We can use HttpModules or Application_EndRequest to dispose objects created in main thread.

    Otherwise, if we have an option in DI/IOC container to specify to take ThreadLocalScope lifecyle as precedence, it can be bit more easier.

    Note: If you are using nested DataReader this approach will not work. See the code below

    Code:
    
    int departmentCode;
    //The following statement opens an SqlConnection and going to be active throughout the scope of "using" statement.
    using (SqlDataReader departmentReader = GetDepartmentReader()) 
    {
     while (departmentReader.Read())
     {
      departmentCode = Convert.ToInt32(departmentReader.GetValue(0));
      //The following line will try to use an another reader in the same thread, Since we made the SqlConnection as singlton per thread, this code will again open the same connection and will throw error.
      using (SqlDataReader employeeReader = GetEmployeeReader(departmentCode))
      {
       ...
       ...
      }
     }
    }
    

    All this approaches may raise following questions in your mind.

  7. Why can't you use transient objects?
  8. Transient objects are not cached anywhere in structuremap, you can’t reference back and dispose them.

  9. Why can’t you use nested containers?
  10. For the situations that we discussed so far, I feel the nested containers are overhead. I can simply use “using” statement instead. Both provides the similar solution.


Back to Part-1