0

I am working for EF 4.2 code first and have been getting a lot of random context connection issues, and wondered if you could cast an eye over this implementation and then slap my wrists for doing it wrong (if it is wrong that is)

I have developed a repository pattern for this project.

I am sure the way I am doing it is the problem, but anyway here is the code:

Context Provider

public class ContextProvider
{
    private static MyContext context;
    public static MyContext Context
    {
        get
        {
            if (context == null)
            {
                context = new MyContext();
            }

            Database.SetInitializer<MyContext>(null);

            //create the DB if it doesn't exist
            if (!context.Database.Exists())
            {
                context.Database.Create();
                context = new MyContext();
            }

            return context;

        }
    }
 }

Here is my repository:

public class DataRepository
{
    protected MyContext Context;

    public DataRepository(MyContext context = null)
    {
        Context = context ?? ContextProvider.Context;
    }

    public ProviderBase<Foo> FooProvider { get { return new ProviderBase<Foo>(); } }
    public ProviderBase<Bah> BahProvider { get { return new ProviderBase<Bah>(); } }

}

ProviderBase Class

 public class ProviderBase<T> : IProviderBase<T> where T : BaseClass
    {
        public Boolean UseCaching { get; set; }

        public MyContext Context;

        public ProviderBase(Boolean useCaching = true, MyContext context = null)
        {
            Context = context ?? ContextProvider.Context;
            UseCaching = useCaching;
        }

        #region Implementation of IProviderBase<T>

        protected DbSet<T> DbSet
        {
            get
            {
                return Context.Set<T>();
            }
        }

        ... methods here for CRUD ....

    }

I think the issue is the static Context, am I right? and if so what is the solution?

Ladislav Mrnka
  • 349,807
  • 56
  • 643
  • 654
JamesStuddart
  • 2,393
  • 3
  • 25
  • 43

2 Answers2

4

Don't use static context.

To modify your code you can use this:

public class ContextProvider
{
    private const string ContextId = "EF.MyContext"; 

    // Call this only once in Application_Start in Global.asax
    public void InitializeDatabase() 
    {
        MyContext context = GetContext();
        if (!context.Database.Exists())
        {
            context.Database.Create(); 
        }
    }

    public MyContext GetContext()
    {
        MyContext context = HttpContext.Current.Items[ContextId] as MyContext;

        if (context == null)
        {
            context = new MyContext();
            HttpContext.Current.Items[CotnextId] = context; 
        }

        return context;
    }

    // Call this in EndRequest handler in Global.asax
    public void ReleaseContext()
    {
        MyContext context = HttpContext.Current.Items[ContextId] as MyContext;

        if (context != null)
        {
           context.Dispose();  
        } 
    }
 }

And your repository will look like:

public class DataRepository
{
    protected MyContext Context;

    // If you never need more than one instance of MyContext per repository you
    // can inject context directly and call provider in upper layer
    public DataRepository(ContextProvider provider)
    {
        Context = provider.GetContext();
    }

    public ProviderBase<Foo> FooProvider { get { return new ProviderBase<Foo>(); } }
    public ProviderBase<Bah> BahProvider { get { return new ProviderBase<Bah>(); } }
}
Community
  • 1
  • 1
Ladislav Mrnka
  • 349,807
  • 56
  • 643
  • 654
  • Thanks, this looks like the solution I was looking for, I have implemented it, just need to test it out. Thanks for your help. – JamesStuddart Mar 16 '12 at 10:56
  • I have just given this a test and I get the following error: The operation cannot be completed because the DbContext has been disposed. I know this is due to killing off the dbContext in the Application_EndRequest Any suggestions? – JamesStuddart Mar 16 '12 at 11:02
  • That means you have somewhere disposed the context. There should be no usage of the context after `EndRequest`. – Ladislav Mrnka Mar 16 '12 at 11:07
  • Yeah, I must be missing something as end request is called a lot when putting a break in there, between posts between pages. I may have to look at my query method implementation now. – JamesStuddart Mar 16 '12 at 11:18
  • End request will be called once per each request but you can have a lot of requests per single operation in your application but each of these requests will have its own context so it should not matter. As a workaround for investigation you can also assign `null` to `Item` representing the context in `ReleaseContext` method. In such case next `GetContext` call will create new context instance but you must find why is this happening. – Ladislav Mrnka Mar 16 '12 at 11:20
-1

For singleton patterm there is very usefull generic implementation Lazy:

public class ContextProvider 
{ 
    private static Lazy<MyContext> _context = new Lazy<MyContext> context(CreateContext); 

    private static MyContext CreateContext()
    {
            var context = new MyContext(); 

            Database.SetInitializer<MyContext>(null); 

            //create the DB if it doesn't exist 
            if (!context.Database.Exists()) 
            { 
                context.Database.Create(); 
            } 

            return context;
    }

    public static MyContext Context 
    { 
        get { return _context.Value;} 
    } 
 } 

Also as others already pointed to you - it is not recomended to use static Context, but rather Create\Dispose it for each operation.

Vitaliy Kalinin
  • 1,471
  • 11
  • 18