0

I have a pretty straightforward ASP.NET MVC project built with Entity Framework and Entities->Repositories->Services->Controllers->Views layered structure. Dependency Injection is utilized.

I need to do something like this in my controller logic (Simplified for the example sake):

           var keyWord = _keywordService.GetByName("blah"); // Service instances are registered by dependency injection
           var myFile = new MyFile()
            {
                FileName = "//FilePath/etc"
            };               
           var myContent = new MediaContent()
            {
                UploadedFile = myFile
            };
           myContent.KeyWords.Add(keyword);
           _MediaContentService.AddContent(myContent); // Service instances are registered by dependency injection

But I can't. This does not work because apparently every time a new instance of an object is created ( myFile, MyContent ) - a new session of DBContext gets created with it. So when I try to save myContent, from the example, multiple Contexts conflict and I end up getting one of the two error messages:

a) An entity object cannot be referenced by multiple instances of IEntityChangeTracker. OR b) The relationship between the two objects cannot be defined because they are attached to different ObjectContext objects.

I understand why this happens though. Every time a new instance of an EF entity is created my DBContext initializer is called and a new Context gets created. Can this be avoided ? Here is my custom DBContext:

public class MyCustomDBContext : IdentityDbContext<ApplicationUser>
{
    public MyCustomDBContext (): base("ConnStringName", throwIfV1Schema: false){}

    public static MyCustomDBContext Create()
    {
        return new MyCustomDBContext();
    }
            public DbSet<FirstEntity> FirstEntity{ get; set; }
            //Other entities
            .
            .
            .

}

And here is how that call is initialized at Startup.Auth initially. This is how the application knows where to go to create new Contexts per request:

   public void ConfigureAuth(IAppBuilder app)
    {
        // Configure the db context to use a single instance per request
        app.CreatePerOwinContext(MyCustomDBContext.Create);
    }

It looks like that Create() method keeps initializing new Context instances... I don't understand how to solve it without breaking the separation of concerns? I don't want to have to pass around an existing instance of DBContext around all layers of the application. Like This question, for example, provided a suggestion to create an instance of DBContext in the controller and then pass it down to services, repositories, etc. This suggestion does not work with built out MVC infrastructure.

As I tried in the first example, how can I create, process and link together new objects, and then send them down to services to be saved without getting lost in multiple Contexts? After all, I should only have one context per request.

InspiredBy
  • 4,271
  • 6
  • 40
  • 67
  • "Every time a new instance of an EF entity is created my DBContext initializer is called and a new Context gets created." have you confirmed this? it seems highly unlikely.. – Robbert Draaisma Dec 19 '17 at 07:54
  • @RobbertDraaisma Seems to me that way, based on debugging. Plus the error message literally says that "The relationship between the two objects cannot be defined because they are attached to different ObjectContext objects." Or do you have other suspicions/ideas? – InspiredBy Dec 19 '17 at 16:11
  • I agree the exception message points to multiple dbcontexts but I can’t believe simply creating an EF entity leads to more of them. My first thought would be to look at the IOC configuration. I think you can trust the owin extension method to create a single instance per request but MVC is middleware within the owin pipeline with it’s whole own pipeline. How are those services registered and you sure none of them are internally instantiating contexts on their own? – Robbert Draaisma Dec 20 '17 at 04:00

2 Answers2

0

What you should do, is use the using keyword for your solution, which will solve your problem. I use it with multiple contexes in my project:

using (MyFile myFile = new MyFile()) 
{
   using(MediaContent myContent = new MediaContent())
   {
     myFile.FileName = "//FilePath/etc";  
     myContent.UploadedFile = myFile;   

       myContent.KeyWords.Add(keyword);
      _MediaContentService.AddContent(myContent);  
   }
}
Barr J
  • 10,636
  • 1
  • 28
  • 46
  • A workaround but I guess it could work if no other solution is found. I would love to find a more solid solution though. – InspiredBy Dec 20 '17 at 03:49
  • 1
    You got me interested and I got up with this helpful article, take a look at it you might find some gold there: http://mehdi.me/ambient-dbcontext-in-ef6/ – Barr J Dec 20 '17 at 05:46
0

The solution was to create a mapping for DBContext in the dependency injection mapping file:

kernel.Bind<MyCustomDBContext>().ToSelf().InRequestScope();
InspiredBy
  • 4,271
  • 6
  • 40
  • 67