在ASP.NET中管理Entity Framework ObjectContext

4
我正在为一个ASP.NET Web Forms应用程序使用Entity Framework,想知道如何处理ObjectContext及其生命周期。 例如,我有一个InviteService类来管理邀请,比如创建和接受邀请。该类本身位于Web项目的另一个项目/命名空间中。 InviteUsers()方法为用户列表创建Invite实体,调用存储库将它们保存到数据库并向每个用户发送邀请链接。
当用户单击邀请按钮时,该方法从Page中调用。
我想知道应该如何使用ObjectContext:
1.在每个请求上在Page上实例化一个新的ObjectContext,将其作为参数传递给InviteService类的构造函数,然后在Render方法中进行处理。
2.与上述相同,但不是通过构造函数设置它,而是将其作为参数传递给每个方法。
3.在每个方法中使用using块创建单独的Objectcontext。

根据Ladislav在这里的答案,选项一似乎对我来说是最好的选择: Entity Framework and Connection Pooling 但是,选项3也是有效的,因为据我所知,由于连接池,不会产生新的数据库连接。

2个回答

6
在web请求中创建单个ObjectContext并不罕见。我在我的Web应用程序中也这样做。但是,在我看来,页面不应该知道ObjectContext的存在。
既然您已经在讨论将上下文注入服务的构造函数中,请查看依赖注入(如果您尚未使用)。当您使用依赖注入容器时,可以让容器为您创建该服务并将对象上下文注入该容器。您的页面唯一需要做的就是从容器中请求该服务(理想情况下,您甚至可以让该服务被注入到该页面的构造函数中,但这在Web表单中不可能)。
您的页面将如下所示:
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);
    }
}

在您的应用程序启动路径(Global.asax)中,您可以像这样配置依赖注入框架:
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;
}

在这些示例中,我使用了Simple Injector依赖注入容器,但任何DI容器都可以。 RegisterPerWebRequest不是核心库的一部分,但可用作(NuGet)扩展包。该包确保在Web请求结束时处理您的ObjectContext
起初可能会感到复杂,但这样网页就无需担心创建和处理ObjectContext的任何细节。
此外,将执行用例的逻辑放置在单个类中:命令。让命令(或系统)确保该操作的原子性。不要让页面负责此事,并且不要在请求结束时进行提交,因为此时您甚至不知道是否可以调用提交。不,让命令自己处理这个问题。这里有关于编写业务命令的文章
这个建议同样适用于ASP.NET MVC,尽管您不应该在控制器的构造函数中调用Global.GetInstance<IMyService>(),而只需使用构造函数注入(因为MVC对此有很好的支持),并使用MVC3 Integration package
还可以查看此Stackoverflow问题,其中讨论了选择IObjectContextFactory或每个请求使用ObjectContext之间的区别。

每次请求时创建/删除+1。我也是这样做的,保持简单和清晰。 - E.J. Brennan
我曾经在我的命令处理程序中注入了一个 IDbContextFactory,并让处理程序自己处理上下文的释放,但这导致了一种模式,即我必须从方法到方法,从类到类传递上下文,以便在同一上下文中运行所有内容,这变成了一个维护噩梦。 - Steven
非常感谢您的回答。依赖注入已经在我的“待读”清单上有一段时间了。在我阅读 DI 的相关资料时,我将使用选项 1。执行操作的逻辑包含在单独的类中,每个命令都会提交数据,并根据提交的结果采取进一步的行动。 - Freek

2

1是最好的解决方案。在NHibernate世界中被称为session-per-request。

您可以在BeginRequest中实例化ObjectContext,并在EndRequest中刷新/提交它。

之所以选择1而不是3,是因为当请求到达时,您就可以开始使用ORM(在您的情况下是Entity Framework),然后在整个页面生命周期内添加对象到树中,修改类,删除等操作。

只有在EndRequest期间,您才会将所有更改作为一个批次进行提交。

编辑:正如@Steven所说,在提交期间处理异常并不完美。

因此,假设您有一个带有保存按钮的Web表单:

  • 在BeginRequest期间创建ObjectContext
  • 在Save命令处理程序中调用commit
  • 在EndRequest期间简单地关闭/释放ObjectContext

在EndRequest期间何时可以提交,您如何知道它是否可以? - Steven
也许我没有理解你的问题。 当您提交时,您告诉ORM:“请插入/更新/删除所有待处理更改”。 因此,在EndRequest调用Commit而没有条件。 ORM将考虑每个跟踪对象的状态来提交。 - Be.St.
但是,当操作失败(即抛出异常)时,在这种情况下,您不应在EndRequest期间调用commit。在这种情况下调用commit会破坏系统的原子性。如何防止在这种情况下调用commit? - Steven
1
我明白了,这有点不同。假设您在Web表单上有一个保存按钮。在这种情况下,提交操作位于保存命令处理程序上,您可以在其中管理异常并可能回滚事务。在EndRequest中,您只需要关闭/处理ObjectContext即可。 - Be.St.
那正是我想表达的意思;-) - Steven

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接