40

I have this view:

@model MatchGaming.Models.ProfileQuery
@{
    ViewBag.Title = "Index";
}

<h2>Index</h2>    
<script src="@Url.Content("~/Scripts/jquery.validate.min.js")" type="text/javascript"></script>
<script src="@Url.Content("~/Scripts/jquery.validate.unobtrusive.min.js")" type="text/javascript"></script>

@using (Html.BeginForm("Results", "Profiles")) {
    @Html.ValidationSummary(true)
    <fieldset>
        <legend>ProfileQuery</legend>
        @Html.EditorFor(model=>model.SearchString)
        <p>
            <input type="submit" value="Create" />
        </p>
    </fieldset>
}

<div>
    @Html.ActionLink("Back to List", "Index")
</div>

I have this controller for the HttpPost:

[HttpPost]
public ActionResult Results(ProfileQuery profileQuery)
{
    Debug.Write(profileQuery.SearchString);
    using(var db = new MatchGamingEntities())
    {
        var SearchUserName = db.Users.SingleOrDefault(a=> a.UserName.Contains(profileQuery.SearchString));
        var Users = from m in db.Users
                    join m2 in db.MyProfiles on m.UserId equals m2.UserId
                    where m.UserName == SearchUserName.UserName
                    select new UserViewModel
                    {
                        UserName = m.UserName,
                        LastActivityDate = m.LastActivityDate,
                        Address = m2.Address,
                        City = m2.City,
                        State = m2.State,
                        Zip = m2.Zip
                    };

        return View(Users.AsEnumerable());
    }
}

Here is the View for Results:

@model IEnumerable<MatchGaming.Models.UserViewModel>    
@{
    ViewBag.Title = "Results";
}

<h2>Results</h2>

<fieldset>
    <legend>UserViewModel</legend>
    @foreach (var item in Model){
    <div class="display-label">UserName</div>
    <div class="display-field">@item.UserName</div>

    <div class="display-label">LastActivityDate</div>
    <div class="display-field">@String.Format("{0:g}", item.LastActivityDate)</div>

    <div class="display-label">Address</div>
    <div class="display-field">@item.Address</div>

    <div class="display-label">City</div>
    <div class="display-field">@item.City</div>

    <div class="display-label">State</div>
    <div class="display-field">@item.State</div>

    <div class="display-label">Zip</div>
    <div class="display-field">@item.Zip</div>
    }
</fieldset>

I keep getting this error:

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

I can't figure out why.

abatishchev
  • 92,232
  • 78
  • 284
  • 421
anthonypliu
  • 11,459
  • 25
  • 85
  • 149

6 Answers6

51

I am guessing that the problem is that the execution of your LINQ query has been deferred until starting to access them on your view. At this point db has already been disposed.

Try this:

return View(Users.ToList());

Added ToList()

That will force the fetch from the DB before disposing db.

Philip Fourie
  • 96,182
  • 10
  • 57
  • 81
  • 1
    I tried that and then got this error: "Object reference not set to an instance of an object." This error happens RIGHT at the line that you posted me to change. That is what is throwing the exception – anthonypliu Mar 19 '11 at 06:12
  • @anthonypliu, I remove the not needed `AsEnumerable()` from my example as ToList() is already Enumerable. Looking at the view code it is possible that LastActivityDate might be null and cause a NullReferenceException because of the String.Format(). It should work. Otherwise step into the code and find the null reference - most likely the Users object or something in the view. – Philip Fourie Mar 19 '11 at 06:19
  • 1
    I tried that but it did not work, I think I know part of the problem, my first view is not properly posting the ProfileQuery model because I tried to print profileQuery.SearchString and I got a null exception, so the SearchString isnt even getting through to the HttpPost – anthonypliu Mar 19 '11 at 06:22
11

Your using clause is disposing of (read: destroying) the MatchGamingEntities db context before the View has a chance to use it. So while you can just enumerate the items before (or as) you pass the Users to the View, a better approach is to drop your use of the using clause and let natural garbage collection do its work after you truly done with it--which won't be until after the View is done.

For more information, see this question on the Entity Framework and Connection Pooling.

Community
  • 1
  • 1
witttness
  • 4,914
  • 4
  • 23
  • 24
10

The problem is this line:

return View(Users.AsEnumerable());

The enumeration is lazily evaluated, and since your MatchGamingEntities is disposed of before your view can cycle through the enumeration, the code dies when it tries to do just that.

You will either have to find a way to manage the lifetime of your db object in such a way that it lives beyond the controller method, or bring all your data into in-memory Model objects before passing them to the view.

see here for a similar explanation.

Community
  • 1
  • 1
Mike Marshall
  • 7,653
  • 4
  • 35
  • 61
9

A bad practice, but you can set

this.ContextOptions.LazyLoadingEnabled = false;

in the constructor of the Context

akjoshi
  • 14,589
  • 13
  • 94
  • 116
vothaison
  • 1,406
  • 13
  • 13
  • 3
    With strong emphasis on the "bad." If you get to a point where you think you need to do this, you need to rethink your SOC strategy. – Andrew May 30 '13 at 15:21
  • to make it less bad wrap in using statements for the context so the setting is destroyed after the command is called – kravits88 Dec 11 '13 at 03:37
1

The problem is that you are issuing a Shallow Copy when you do:

 var Users = from m in db.Users
                join m2 in db.MyProfiles on m.UserId equals m2.UserId
                where m.UserName == SearchUserName.UserName
                select new UserViewModel
                {
                    UserName = m.UserName,
                    LastActivityDate = m.LastActivityDate,
                    Address = m2.Address,
                    City = m2.City,
                    State = m2.State,
                    Zip = m2.Zip
                };

What you need to do is make a UsersCopy and then iterate through Users copying the values to the UsersCopy and then return UsersCopy in order to do a Deep Copy

Something like

List<User> UsersCopy = new List<User>();
foreach(user in Users){
  User u = new User();
  u.UserName = user.UserName;
  u.Address = user.Address;
  //...
  UsersCopy.Add(u);
}
return View(UsersCopy);
Travis J
  • 77,009
  • 39
  • 185
  • 250
1

In case you are having problem displaying child property i.e Line items. Here is the dirty fix that worked for me. Fixed my problem of not retrieving the child items of an object:

header.MachineDataLines = header.MachineDataLines;

Andy Singh
  • 29
  • 1