问题
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
- Instantiate a new
ObjectContext
on the Page on each Request, passing it as a parameter to the constructor of theInviteService
class and then disposing it in theRender
method. - Same as above but instead of setting it via the constructor, passing it along as a parameter to each method.
- Create a separate
Objectcontext
in each method with ausing
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.
回答1:
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.
回答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
来源:https://stackoverflow.com/questions/8927779/managing-entity-framework-objectcontext-in-asp-net