Managing Entity Framework ObjectContext in ASP.NET
Asked Answered
L

2

4

I'm using the Entity Framework for an ASP.NET Web Forms application and I'm wondering how I should deal with ObjectContext and it's lifetime. For example, I have an InviteService class that manages invites such as creating and accepting invites. The class itself is in another project/namespace from the Web project. An InviteUsers() method creates Invite entities for a list of users, calls a repository to save them to the database and mails each user an invite link.

The method is called from the Page when a user clicks the invite button.

I would like to know how I should use the ObjectContext

  1. Instantiate a new ObjectContext on the Page on each Request, passing it as a parameter to the constructor of the InviteService class and then disposing it in the Render method.
  2. Same as above but instead of setting it via the constructor, passing it along as a parameter to each method.
  3. Create a separate Objectcontext in each method with a using block.

Option one seems best to me based on the answer of Ladislav here: Entity Framework and Connection Pooling But option 3 seems valid as well since as far as I know, no new database connections are made because of connection pooling.

Latt answered 19/1, 2012 at 14:30 Comment(0)
L
6

It is not unusual to create a single ObjectContext per web request. I do this in my web applications. However, IMO, the page should know nothing about the ObjectContext.

Since you are already talking about injecting the context in the constructor of the service, take a look at dependency injection (if you aren't using that already). When you use a dependency injection container, you can let the container create that service for you and inject the object context in that container. The only thing your page has to do is request that service from the container (ideally, you would even let that service be injected in the constructor of that page, but this is not possible with web forms).

Your page would look like this:

public class MyPage : Page
{
    private readonly IMyService service;

    public MyPage()
    {
        this.service = Global.GetInstance<IMyService>();
    }

    protected void Btn1_OnClick(object s, EventArgs e)
    {
        this.service.DoYourThing(this.TextBox1.Text);
    }
}

In the startup path (Global.asax) of your application, you can configure the Dependency Injection framework like this:

private static Container Container;

public static T GetInstance<T>() where T : class
{
    return container.GetInstance<T>();
}

void Application_Start(object sender, EventArgs e) 
{
    var container = new Container();

    string connectionString = ConfigurationManager
        .ConnectionStrings["MyCon"].ConnectionString;

    // Allow the container to resolve your context and
    // tell it to create a single instance per request.
    container.RegisterPerWebRequest<MyContext>(() =>
        new MyContext(connectionString));

    // Tell the container to return a new instance of
    // MyRealService every time a IMyService is requested.
    // When MyContext is a constructor argument, it will
    // be injected into MyRealService.
    container.Register<IMyService, MyRealService>();

    Container = container;
}

In these examples I used the Simple Injector dependency injection container, although any DI container will do. The RegisterPerWebRequest is not part of the core library, but is available as (NuGet) extension package. The package ensures that your ObjectContext is disposed when the web request ends.

This might seem complex at first, but this way the web page doesn't have to worry at all about any details of creating and disposing an ObjectContext.

Further, place the logic that executes a use case in a single class: a command. Let the command (or the system) ensure atomicness of that operation. Don't let the page be responsible for this, and don't commit on the end of the request, because at that point you won't know if it is even OK to call commit. No, let the command handle this itself. Here is an article about writing business commands.

This advice holds for ASP.NET MVC as well, although you should not call Global.GetInstance<IMyService>() inside the Controller's constructor, but simply use constructor injection (since MVC has great support for this) and use the MVC3 Integration package.

Also take a look at this Stackoverflow question, which talks about choosing between a IObjectContextFactory or having a ObjectContext per request.

Luella answered 19/1, 2012 at 14:37 Comment(3)
+1 for creating/deleting on each request. Same way I do it and keeps it simple and clean.Baedeker
I used to inject an IDbContextFactory into my command handlers and let the handler dispose a context itself, but this lead to a model where I had to pass around the context from method to method, from class to class to get everything run in the same context, which became a maintenance nightmare.Luella
Thanks a lot for the answer. Dependency Injection has been on my "to read" list for a while now. I'll use option 1 while I read up on DI. The logic for executing operations is contained in separate classes where each command commits the data and based on the result of the commit, further action can be taken.Latt
E
2

1 is best solution. In NHibernate world is called session-per-request.

You can instantiate ObjectContext in BeginRequest and flush/commit it in EndRequest.

1 is better than 3 because you start working with ORM (Entity Framework in your case) when a request arrives. Then you add object to the tree, modify class, delete and so on during ALL page lifecycle.

Only during EndRequest you commit ALL your changes in one batch only.

EDIT: as @Steven says is not perfect for dealing with exception during commit.

So suppose you have a web form with a save button:

  • create ObjectContext during BeginRequest
  • call commit in Save command handler
  • simply close/dispose ObjectContext during EndRequest
Eighty answered 19/1, 2012 at 14:54 Comment(5)
How do you know when it is ok to commit during EndRequest?Luella
Maybe I don't understand your question. When you commit you tell the ORM: "please insert/update/delete all pending changes". So in EndRequest call Commit without conditions. Is the ORM that commits considering the state of each tracked object.Eighty
But when an operation failed (i.e. threw an exception), in that case you should not call commit during EndRequest. Calling commit in that case would break the atomicy of the system. How do you prevent commit from being called in that case?Luella
I see, so is a bit different. Suppose you have a Save button on your web form. In this case commit operation is on the Save command handler where you can manage exception and maybe rollback the transaction. In EndRequest you need only to close/dispose the ObjectContext.Eighty
That was exactly were I was getting at ;-)Luella

© 2022 - 2024 — McMap. All rights reserved.