Pregunta

I have an existing project where I want to use an ObjectDataSource for a large data set which pages the results for a DevExpress Grid.

Examples of the code are shown below.

I am having trouble using an Entity Framework context in static methods that are required for the ObjectDataSource.

My problem is that I get this exception:

System.ObjectDisposedException: The ObjectContext instance has been disposed and can no longer be used for operations that require a connection.

Does anyone know how I can change the below to support passing an Entities instance from the Page to the static methods?

This is the example of the 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>

This is my service class with static methods that can then be used by the 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
        ...
        }
    }

This is the code behind for the web forms page

    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)
        {
             ...
        }

    }

This is the global code, where the Entities context is being set on the begin and end of the request.

    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();
            }
        }
    }
¿Fue útil?

Solución

As someone who uses the DevX grid quite a bit in a VB.Net Webforms app with static/Shared methods to encapsulate various data access logic, another approach would be to skip creating a ObjectDataSource in the ASPX and instead set up the grid's DataSourceID in the code-behind's Page_Load. I founded this much more manageable and predictable.

Here's the idea:

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

I also create a new EF context for each request in Page_Load - I've not found the performance associated with this to be an issue and it makes hunting down problems 10x easier. You may want to consider this, at least to get started. I've seen others in the past suggest putting your context into Session state and fetching it from there when needed, but that feels like it could cause all sorts of hard-to-track-down badness so I've never been bold enough to try and make that work.

As a side note for if you follow my suggestion: if your initial load of the ASPxGridView works but you get errors as you move from page to page, sort columns, etc. then you may need to use some .Includes for associated entities that display in your grid.

Otros consejos

I have since found this example: http://www.asp.net/web-forms/tutorials/continuing-with-ef/using-the-entity-framework-and-the-objectdatasource-control,-part-1-getting-started

which made me realise that my methods on my ReportService class don't need to be static in order for this to work, which was my main problem in the first place.. I am just not experienced enough with ObjectDataSource and was following the examples that DevExpress gave from here: http://www.devexpress.com/Support/Center/Example/Details/E2672

So now a new EF ObjectContext can be created in the scope of the class and then Disposed of when that class is disposed and this seems to work for me.

Like shown below:

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);
    }

}
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top