Search code examples
c#asp.netentity-frameworkrepository-patterndata-access-layer

Usage of ObjectDataSource.Select() method eventually results to exception "The operation cannot be completed because the DbContext has been disposed."


I am new to EF and the Repository pattern. I understand where the problem is coming from in my code but I am not sure what is the right resolution. Here is the problem:

I have a WebForm where I am querying the DB to get one record to display. I have implemented by Business Logic Layer (BLL) and the Database Access Layer (DAL). In the WebForm I am using the following ObjectDataSource:

<asp:ObjectDataSource ID="EmployerObjectDataSource" runat="server" TypeName="IkaManagerWeb.BLL.IkaMgrBL"
                      DataObjectTypeName="IkaManagerWeb.BLL.IkaMgrBL"
                      SelectMethod="GetEmployer"
                      OnSelecting ="EmployerObjectDataSource_Selecting">
    <SelectParameters>
        <asp:Parameter Name ="username" Type="String" />
        <asp:Parameter Name="version" Type="UInt16" />
    </SelectParameters>
</asp:ObjectDataSource>

So at the code behind the WebForm I do the following:

    public partial class WebForm1 : System.Web.UI.Page
    {
        private string _username;
        private short _version;

        protected void Page_Load(object sender, EventArgs e)
        {
            _username = "Lefteris";
            _version = 1;

            if (!Page.IsPostBack)
            {
                //  InputStyle style = txtFirstName.DisabledStyle;
                populateFields();
            }
        }

        protected void EmployerObjectDataSource_Selecting(object sender, ObjectDataSourceSelectingEventArgs e)

        {
            e.InputParameters["username"] = _username;
            e.InputParameters["version"] =_version;
        }    

        private bool populateFields()
        {

            IEnumerable<Employer> empl = (IEnumerable<Employer>)EmployerObjectDataSource.Select();


            System.Threading.Thread.Sleep(1000);
            if (empl.Count() == 1)
            {
                Employer employer = empl.First();
                txtAme.Text = employer.AME.ToString();
            ...

The problem is that as soon as I hit the line:

if (empl.Count() == 1)

above, I get an exception that the DBContext has been disposed. By stepping through the code in BLL and DAL I see that the DBContext is indeed disposed. In fact, as

EmployerObjectDataSource.Select(); 

returns the result, the BLL is disposed, which in turn is causing the DAL and therefore the DBContext to be disposed.

What I am thinking as a solution is to maintain a reference to the BLL for the whole execution of the PopulateFields method. So here is in the question:

Is this the right way (perhaps with a "using" block)? And if it is, then how can I get a reference to the BLL object that the EmployerObjectDataSource is instantiating in order to perform the Select?

EDIT: By stepping through the code I saw the following order of events:

  1. By calling the Select() method of the ObjectDataSource, the BLL object is instantiated.
  2. The BLL is instantiating the DAL object.
  3. The DAL object is performing the underlying database query
  4. The BLL is entering Dispose()
  5. During the BLL Dispose() the DAL Dispose() is called - this removes the DBContext
  6. The Select() method of the ObjectDataContext returns
  7. As soon as I call empl.Count() the empl object is enumerated and since the DBContext has been removed I get the exception.

EDIT: Following the request of Dmytro below, I am also providing the GetEmployer method of the BLL:

    public IEnumerable<Employer> GetEmployer(string username, short version)
    {
        DateTime today = DateTime.Today;
        IEnumerable<Employer> employers = ikaRepository.GetEmployers(username, today, version);

        Debug.Assert(employers.Count() <= 1, "This is a logical Error - Can we have more than one active Employer records per user?");
        return employers;
    }

Note that this retrieves the record correctly.

Thank you in advance,

Lefteris


Solution

  • It looks like your line

    IEnumerable<Employer> empl = (IEnumerable<Employer>)EmployerObjectDataSource.Select();
    

    returns IQueryable type from Select method. This type doesn't contains real data and doesn't fetch data from db. Try use something like this

    var empl = ((IEnumerable<Employer>)EmployerObjectDataSource.Select()).ToList();