Использование контекста Entity Framework в статических методах для ObjectDataSource

У меня есть существующий проект, в котором я хочу использовать ObjectDataSource для большого набора данных, который выводит результаты для DevExpress Grid.

Примеры кода показаны ниже.

У меня возникают проблемы с использованием контекста Entity Framework в статических методах, необходимых для ObjectDataSource.

Моя проблема в том, что я получаю это исключение:

System.ObjectDisposedException: экземпляр ObjectContext удален и больше не может использоваться для операций, требующих подключения.

Кто-нибудь знает, как я могу изменить приведенное ниже, чтобы поддерживать передачу экземпляра Entities со страницы на статические методы?

Это пример 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();
            }
        }
    }

person Pricey    schedule 01.05.2014    source источник


Ответы (2)


Как человек, который довольно часто использует сетку DevX в приложении VB.Net Webforms со статическими/общими методами для инкапсуляции различной логики доступа к данным, другим подходом будет пропустить создание ObjectDataSource в ASPX и вместо этого настроить DataSourceID сетки в Page_Load кода программной части. Я основал это гораздо более управляемым и предсказуемым.

Вот идея:

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

Я также создаю новый контекст EF для каждого запроса в Page_Load — я не обнаружил, что производительность, связанная с этим, является проблемой, и это упрощает поиск проблем в 10 раз. Вы можете подумать об этом, по крайней мере, для начала. Я видел, как другие в прошлом предлагали помещать ваш контекст в состояние сеанса и извлекать его оттуда, когда это необходимо, но мне кажется, что это может вызвать всевозможные трудно отслеживаемые проблемы, поэтому я никогда не был достаточно смел, чтобы попробуй сделай это.

В качестве примечания, если вы последуете моему предложению: если ваша первоначальная загрузка ASPxGridView работает, но вы получаете ошибки при переходе со страницы на страницу, сортировке столбцов и т. д., вам может потребоваться использовать некоторые .Includes для связанных сущностей, которые отображаются в ваша сетка.

person JimMSDN    schedule 01.05.2014
comment
Спасибо за предложение, я думаю, что это может быть то же самое, что я делал раньше. Я переключился на использование ObjectDataSource, потому что пытался найти способ заставить DevEx Grid не запрашивать все мои данные каждый раз, когда я изменяю элемент страницы, потому что они, похоже, не поддерживают установку PageCount вручную. Я не смог этого сделать. я сам, поэтому начал смотреть, какую поддержку имеют DevEx, это лучший пример, который я нашел devexpress.com/Support/Center/Example/Details/E2672, но это актуально только в том случае, если я использовал прямое соединение SQL, знаете ли вы, как это сделать? - person Pricey; 02.05.2014
comment
@Pricey Из вашего описания, если я вас правильно понимаю, это звучит так, будто в первый раз, когда приложению нужны определенные данные, вы должны создать необходимые DataTable (используя EF для получения данных) и .DataBind DataTable в сетке, сохранив их в Сессия (или где-то еще) на выходе из Page_Load, чтобы сохранить ее между обратными вызовами сетки. Затем в последующих запросах в Page_Load вы извлекаете DataTable из сеанса и .DataBind в сетку, чтобы он мог снова использовать данные без повторного запроса базовой базы данных с помощью EF. - person JimMSDN; 02.05.2014
comment
Да, я сделал это в некоторых других моих отчетах, которые меньше, но в этом случае данные составляют около миллиона записей, и это приложение уже слишком сильно раздувает сеанс, когда несколько пользователей запускают отчеты :-( поэтому сохраняйте его в сеансе ускорить последующую загрузку страниц в сетке было практически невозможно. - person Pricey; 02.05.2014
comment
@Pricey Ugh - я почти добавил к своему последнему комментарию ... если только размер данных не делает сохранение этих результатов невозможным ... 8-) Следующие решения могут выглядеть как перенос некоторой логики манипулирования данными в базу данных как SPrcs и использование EF для привязки этих результатов обратно к объектам, которые затем может использовать ASPxGridView — что-то вроде weblogs.asp.net/zeeshanhirani/archive/2010/11/02/ или msdn.microsoft.com/en-us/library/vstudio/cc716672.aspx - person JimMSDN; 02.05.2014
comment
Спасибо за ссылки, выглядит полезно, к сожалению, в этом конкретном случае мне приходится поворачивать данные в коде до их применения к сетке, если бы я мог изменить этот конкретный SQL, я бы использовал SQL PIVOT во время процесс загрузки и сохранить его в таблице, потому что эти данные обновляются ежедневно, но в моем случае я не могу в данный момент. Спасибо за помощь, взгляните на ответ, который я добавил, и посмотрите, что вы думаете. - person Pricey; 02.05.2014
comment
Отмечая это как ответ, потому что мой вопрос был о том, как использовать контекст со статическими методами, и лучший ответ, на мой взгляд, не так, но это также предлагает способ сделать это, сохранив контекст в сеансе, но это никогда не является хорошая идея, поэтому лучший подход - найти способ не делать это со статическими методами. - person Pricey; 02.05.2014

С тех пор я нашел этот пример: http://www.asp.net/web-forms/tutorials/continuing-with-ef/using-the-entity-framework-and-the-objectdatasource-control,-part-1-getting-started

что заставило меня понять, что мои методы в моем классе ReportService не должны быть статическими, чтобы это работало, что было моей главной проблемой в первую очередь. Я просто недостаточно опытен с 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);
    }

}
person Pricey    schedule 02.05.2014
comment
Для моей ясности: когда вы говорите о службе, вы имеете в виду отдельную службу/приложение типа демона, которое работает отдельно от вашего веб-приложения, веб-службу ASP.NET/службу WCF или просто концепцию класса обслуживания, который инкапсулирует функциональность в другие части вашего веб-приложения? - person JimMSDN; 02.05.2014
comment
Просто концепция класса, который обслуживает этот конкретный отчет, изначально он был просто в коде страницы, но ObjectDataContext ожидает TypeName для класса, который он будет использовать для своих конкретных методов. - person Pricey; 02.05.2014