Search code examples
asp.netlinq-to-sqldatacontextlifecycle

Best practice for managing life time of business layer, data layer, datacontext instance asp.net website


Our Asp.net web application is using LINQ-to-SQL (Stored Procs are dragged on dropped on dbml file to create classes) and 3 tier architecture is similar to the one below. I have just created rough methods to give reader proper idea so that he can answer well.

namespace MyDataLayer
{
    public class MyDataAccess
    {
        // global instance of datacontext
        MyDataModelDataContext myDB = new MyDataModelDataContext();     (#1)

        public void GetUserIDByUsername(string sUserName, ref int iUserID)
        {
            int? iUserIDout = 0;
            // this will make call to SP in SQL DB
            myDB.USP_RP_GETUSERIDBYUSERNAME(sUserName, "", ref iUserIDout);
            iUserID = (int)iUserIDout;
        }

        public List<USP_APP_USERDETAILSResult> GetUserDetails(string sUserIDs)
        {
            // this will make call to SP in SQL DB
            return myDB.USP_APP_USERDETAILS(sUserIDs).ToList();
        }
        ...
        ... // several CRUD methods
    }
}

namespace MyBusinessLayer
{
    public class SiteUser
    {
        // global DataAccess instance
        MyDataLayer.MyDataAccess myDA = new MyDataAccess();             (#2)
        public void GetUserIDByUsername(string sUserName, ref int iUserID)
        {
            myDA.GetUserIDByUsername(sUserName, ref iUserID);
        }
        public List<USP_APP_USERDETAILSResult> GetUserDetails(string sUserIDs)
        {
            // this will make call to SP in SQL DB
            return myDA.GetUserDetails(sUserIDs);
        }
        ...
        ... // several CRUD methods
    }
}

namespace MyWebApplication
{
    public class BaseWebPage : System.Web.UI.Page
    {
        // static business layer instance
        public static MyBusinessLayer.SiteUser UserBLInstance = new SiteUser();         (#3)                    
        ...             
    }
}
// Index.aspx.cs code fragment
namespace MyWebApplication
{
    public class Index : BaseWebPage
    {       
        public void PopulateUserDropDown()
        {
            // using static business layer instance declared in BaseWebPage
            List<USP_APP_USERDETAILSResult> listUsers = UserBLInstance.GetUserDetails("1,2,3");

            // do databinding and so on ...
        }
        ...             
    }
}

Questions

  • (Ref.#1) Is having global datacontext in DataAccess good approach? yes/no why?
  • If your suggestion is having datacontext per request what is the best practice for that
  • (Ref.#2) Is having global DataAccess instance in BusinessLayer good approach? yes/no why?
  • If your suggestion is having DataAccess instance per request what is the best practice for that
  • (Ref. #3) Is static business layer instance declared in BaseWebPage good approach? yes/no why?
  • Best approach to manage life time of BL instance and DL instance in general

We are facing periodic InvalidCastException on production server for a very simple method which works fine if I restart my application from IIS. When this problem is there we can access the same database from SQL Management Studio and can execute same SP

Our prime suspect about this issue is poor DataContext management and I have read many articles on net about managing life time of DataContext but I am now confused about various approach. That's why I have elaborated my questions so that many in same situation can get clear idea about problem/answer.


Solution

  • (Ref.#1) Is having global datacontext in DataAccess good approach? yes/no why?

    Yes.

    However, creating it manually inside the dataaccess class means that you can't control the lifetime of the datacontext. Instead, make it then a constructor parameter so that it is injected into the data access

    (Ref.#2) Is having global DataAccess instance in BusinessLayer good approach? yes/no why?

    Yes. But refer to 1. - make it injectable via the constructor.

    (Ref. #3) Is static business layer instance declared in BaseWebPage good approach? yes/no why?

    No. Avoid static for complex objects as usually such objects has non-trivial state. And this is when a lot of nasty issues can happen if you share such objects in a concurrent environment.

    To summarize.

    public class DataAccess {
       public DataAccess( DataContext context ) { ... }    
    }
    
    public class BusinessLayer {
       public BusinessLayer( DataAccess access ) { ... }    
    }
    
    public class MyPage : Page {
     ...
    
     var ctx = TheDataContext.Current;
     var bl = new BusinessLayer( new DataAccess( ctx ) );  
    }
    

    with data context shared in a request scope:

    public partial class TheDataContext {
    
      // Allow the datacontext to be shared in a request-scope
      public static TheDataContext Current {
         get {
             if ( HttpContext.Current.Items["context"] == null )
                HttpContext.Current.Items.Add( "context", new TheDataContext() );
    
             return (TheDataContext)HttpContext.Current.Items["context"];
         } 
      }
    }