【问题标题】:Using an Entity Framework Context in static methods for an ObjectDataSource在 ObjectDataSource 的静态方法中使用实体框架上下文
【发布时间】:2014-05-01 18:55:39
【问题描述】:

我有一个现有项目,我想将 ObjectDataSource 用于大型数据集,该数据集对 DevExpress 网格的结果进行分页。

代码示例如下所示。

我在 ObjectDataSource 所需的静态方法中使用实体框架上下文时遇到问题。

我的问题是我得到了这个异常:

System.ObjectDisposedException:ObjectContext 实例已被 处置,不能再用于需要一个操作 连接。

有谁知道我可以如何更改以下内容以支持将 Entities 实例从 Page 传递到静态方法?

这是ObjectDataSource的例子

        <dx:ASPxGridView ID="DefinedReportGrid" runat="server" EnableTheming="True" Theme="Office2010Blue" EnableViewState="False" 
            ClientInstanceName="DefinedReportGrid" DataSourceForceStandardPaging="True" DataSourceID="ReportDataSource">
            <SettingsPager PageSize="30"></SettingsPager>
        </dx:ASPxGridView>

        <asp:ObjectDataSource ID="ReportDataSource" runat="server" EnablePaging="True" 
            StartRowIndexParameterName="startRecord" MaximumRowsParameterName="maxRecords" SelectCountMethod="GetPageCount" SelectMethod="GetData" 
    TypeName="ReportService">
        </asp:ObjectDataSource>

这是我的带有静态方法的服务类,ObjectDataSource 可以使用这些方法

 public class ReportService
    {
        [DataObjectMethod(DataObjectMethodType.Select, true)]
        public static DataTable GetData(int startRecord, int maxRecords)
        {
        // Use EF DbContent and LINQ Query to fetch paged data, and return a DataTable
        // The data is PIVOTED in the code before returning it as a DataTable

            return outputTable;

        }

        public static List<int> GetWeeks()
        {
        // This also requires use of the EF DbContent
        ...
        }

        public static int GetPageCount()
        {
        // This also requires use of the EF DbContent
        // Used to count the full data for applying the correct total Page Count
        ...
        }
    }

这是网页表单页面的代码

    public partial class DefinedReport : System.Web.UI.Page
    {
        private Entities _modelContext = ((Global)HttpContext.Current.ApplicationInstance).Entities;

        protected void Page_Init(object sender, EventArgs e)
        {
             ...
        }

        protected void Page_Load(object sender, EventArgs e)
        {
             ...
        }

    }

这是全局代码,在请求开始和结束时设置实体上下文。

    public class Global : HttpApplication
    {
        public Entities Entities { get; set; }

    ...

        private void Application_BeginRequest(object sender, EventArgs e)
        {
            Entities = new Entities();
        }

        private void Application_EndRequest(object sender, EventArgs e)
        {
            if (Entities != null)
            {
                Entities.Dispose();
            }
        }
    }

【问题讨论】:

    标签: c# entity-framework webforms devexpress objectdatasource


    【解决方案1】:

    作为一个在 VB.Net Webforms 应用程序中使用 DevX 网格并使用静态/共享方法来封装各种数据访问逻辑的人,另一种方法是跳过在 ASPX 中创建 ObjectDataSource 而是设置代码隐藏的 Page_Load 中的网格 DataSourceID。我创建的这个更易于管理和预测。

    这就是想法:

    Private Sub Page_Load(sender As Object, e As EventArgs) Handles Me.Load
        Using eFContext As New ApplicationEntities()
            Dim requestedTransactionId As Guid = GetAndCheckQueryStringValueGuid("TransactionId")
            Dim requestedTransaction As Transaction = Transaction.GetAndConfirm(eFContext, requestedTransactionId)
    
            Dim requestedCustomers As IQueryable(Of Object) =
                Customer.GetCustomersForCustomersGrid(eFContext, requestedTransaction)
    
            CustomersGridView.DataSource = requestedCustomers
            CustomersGridView.DataBind()
    End Sub
    

    我还在 Page_Load 中为每个请求创建一个新的 EF 上下文 - 我没有发现与此相关的性能是一个问题,它使查找问题变得容易 10 倍。您可能需要考虑这一点,至少要开始。我过去曾看到其他人建议将您的上下文置于 Session 状态并在需要时从那里获取它,但感觉这可能会导致各种难以追踪的不良情况,所以我从来没有足够大胆试着让它发挥作用。

    如果您遵循我的建议,请注意:如果您的 ASPxGridView 初始加载有效,但在从页面移动到页面、对列进行排序等时出现错误,那么您可能需要使用一些 .Includes显示在您的网格中的关联实体。

    【讨论】:

    • 感谢您的建议,我认为这可能与我之前所做的相同。我切换到使用ObjectDataSource,因为我试图找到一种方法,让 DevEx Grid 在每次更改页面项目时都不会请求我的所有数据,因为它们似乎不支持手动设置 PageCount 我无法自己做,所以开始看看 DevEx 有什么支持,这是我找到的最好的例子 devexpress.com/Support/Center/Example/Details/E2672 但它只有在我使用直接 SQL 连接时才相关,你知道解决这个问题的方法吗?
    • @Pricey 根据您的描述,如果我对您的理解正确的话,这听起来像是应用程序第一次需要特定数据时,您将创建必要的 DataTable(使用 EF 获取数据)和.DataBind 将 DataTable 保存到网格中,在离开 Page_Load 的过程中将其保存到 Session(或其他地方)中,以便在网格回调之间保持它。然后,在后续请求中,在 Page_Load 中,您将从 Session 中提取 DataTable 并将 .DataBind 提取到网格中,以便它可以再次使用数据,而无需使用 EF 重新查询底层数据库。
    • 是的,我已经在其他一些较小的报告中这样做了,但在这种情况下,数据大约有一百万条记录,当多个用户运行报告时,此应用程序已经使会话膨胀太多:-( 因此将其存储在会话中以使网格中的后续分页加载更快是不可行的。
    • @Pricey Ugh - 我几乎在我的最后一条评论中添加了“......除非数据的大小使得保持这些结果不可行”......8-) 下一个解决方案可能看起来像移动一些数据操作逻辑作为 SProcs 到数据库中,并使用 EF 将这些结果绑定回 ASPxGridView 可以使用的对象 - 例如weblogs.asp.net/zeeshanhirani/archive/2010/11/02/…msdn.microsoft.com/en-us/library/vstudio/cc716672.aspx
    • 感谢链接,看起来很有用,不幸的是,在这种特殊情况下,如果我能够更改此特定 SQL,我必须在将代码应用到网格之前对代码中的数据进行透视然后我会在加载过程中使用 SQL PIVOT 并将其存储在表中,因为这些数据每天都会刷新,但在我的情况下,我现在不能。感谢您的帮助,看看我添加的答案,看看您的想法。
    【解决方案2】:

    我后来发现了这个例子: http://www.asp.net/web-forms/tutorials/continuing-with-ef/using-the-entity-framework-and-the-objectdatasource-control,-part-1-getting-started

    这让我意识到我在 ReportService 类上的方法不需要是 static 才能使其工作,这首先是我的主要问题.. 我是只是对ObjectDataSource 的经验不够,并且正在遵循 DevExpress 从这里提供的示例:http://www.devexpress.com/Support/Center/Example/Details/E2672

    所以现在可以在类的范围内创建一个新的 EF ObjectContext,然后在释放该类时将其释放,这似乎对我有用。

    如下图:

    public class ReportService : IDisposable
    {
        private bool disposedValue = false;
        private Entities _modelContext = new Entities();
    
        public DataTable GetData(int startRecord, int maxRecords)
        {
        ...
        // use _modelContext and process the data
            return outputTable;
    
        }
    
        public List<int> GetWeeks()
        {
        ...
        // use _modelContext and process the data
        }
    
        public int GetPageCount()
        {
        ...
        // use _modelContext and process the data
        }
    
        protected virtual void Dispose(bool disposing)
        {
            if (!this.disposedValue)
            {
                if (disposing)
                {
                    _modelContext.Dispose();
                }
            }
            this.disposedValue = true;
        }
    
        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }
    
    }
    

    【讨论】:

    • 为了我自己的清楚:当你说“服务”时,你是指一个单独的服务/守护程序类型的应用程序,它在你的 Web 应用程序之外独立运行,一个 ASP.NET Web 服务/WCF 服务,或者只是一个“服务”类的概念,它将功能封装到您的网络应用程序的其他部分?
    • 只是为这个特定报告提供服务的类的概念,它最初只是在后面的页面代码中,但 ObjectDataContext 期望 TypeName 用于它将用于其特定方法的类
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-05-24
    • 2010-10-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多