20

I know that this Question is asked so many times. I have read and implemented all solution but didn't get success. I am getting this error when I retrieve data from database using EF and binds with model after that use this model on View.

My controller code is

using System.Linq;
using System.Web.Mvc;
using JsonRenderingMvcApplication.Models;

namespace JsonRenderingMvcApplication.Controllers
{
    public class PublisherController : Controller
    {
        public ActionResult Index()
        {
            PublisherModel model = new PublisherModel();
            using (DAL.DevelopmentEntities context = new DAL.DevelopmentEntities())
            {               
                model.PublisherList = context.Publishers.Select(x =>
                                        new SelectListItem()
                                        {
                                            Text = x.Name,
                                            Value = x.Id.ToString()
                                        }); ;
                           }
            return View(model);
        }

    }
}

My View code is

@model JsonRenderingMvcApplication.Models.PublisherModel

@{
    ViewBag.Title = "Index";
}
<div>
    @Html.DisplayFor(model=>model.Id)
    @Html.DropDownListFor(model => model.Id, Model.PublisherList);
</div>
<div id="booksDiv">

</div>

My model code is

using System.Collections.Generic;
using System.Web.Mvc;
using System.ComponentModel.DataAnnotations;

namespace JsonRenderingMvcApplication.Models
{
    public class PublisherModel
    {
        public PublisherModel()
        {
            PublisherList = new List<SelectListItem>();
        }

        [Display(Name="Publisher")]
        public int Id { get; set; }
        public IEnumerable<SelectListItem> PublisherList { get; set; }
    }
}

My entity code is

namespace JsonRenderingMvcApplication.DAL
{
    using System;
    using System.Collections.Generic;

    public partial class Publisher
    {
        public Publisher()
        {
            this.BOOKs = new HashSet<BOOK>();
        }

        public int Id { get; set; }
        public string Name { get; set; }
        public string Year { get; set; }

        public virtual ICollection<BOOK> BOOKs { get; set; }
    }
}     

Yes this entity has a navigation property but I don't want to that entity data so I don't want to include that.

Thanks

Sandeep Shekhawat
  • 685
  • 1
  • 9
  • 19
  • What line throws the exception? – Wagner DosAnjos Jan 19 '14 at 03:26
  • @wdosanjos This line " @Html.DropDownListFor(model => model.Id, Model.PublisherList);" throws an exception as Title of post – Sandeep Shekhawat Jan 19 '14 at 07:57
  • Though not directly linked with this problem but I had interesting finding for same error. I had this unitOfWork class (having dbcontext object) and class ABC. Class ABC was using dbContext object through unitOfWork class. Both class ABC and unitOfWork were getting instantiated through windsor castle DI container. I was getting very same error and the issue was in the way I had registered class ABC in the initialization of DI container. I had written code like Register(Component.For().ImplementedBy() **.LifeStyle.Transient** ) I had missed the code in bold to end up with this issue. – RBT Feb 12 '16 at 19:10

1 Answers1

64

The problem you're experiencing is due to LINQ's deferred execution. It's quite the gotcha for developers who haven't yet realized how LINQ works under the hood. I have a great blog post about it, but the core concept is that you must force an enumeration on the collection to cause the LINQ code to run immediately instead of later. This means changing this:

model.PublisherList = context.Publishers.Select(x =>
    new SelectListItem()
    {
        Text = x.Name,
        Value = x.Id.ToString()
    });

to this:

model.PublisherList = context.Publishers.Select(x =>
    new SelectListItem()
    {
        Text = x.Name,
        Value = x.Id.ToString()
    }).ToList();

Note the .ToList() there which forces the enumeration.

Your LINQ query is deferred meaning that it is not being run at your controller but instead afterwards, probably in your view where you loop over the collection (which forces the enumeration and thus runs the LINQ). Because you're using the using statement to dispose of your DB context (which is of course good practice), the context is disposed of before you reach the view, which executes the code against the disposed context. Forcing the enumeration within the using statement will run the code at that time, instead of later when the context is disposed, and prevent this issue.

Haney
  • 32,775
  • 8
  • 59
  • 68
  • Thanks for response. Can you please explain more that what does '.ToList()'? +1 to you. – Sandeep Shekhawat Jan 19 '14 at 04:03
  • 2
    Excellent answer. @Sandeep. If you are curious as to what .ToList() does, I highly recommend reading the documentation. http://msdn.microsoft.com/en-us/library/bb342261%28v=vs.110%29.aspx – David L Jan 19 '14 at 04:12
  • 1
    @Sandeep for sure, see David's comment on what `.ToList()` does. It basically creates a list of the objects in your enumerable. – Haney Jan 19 '14 at 04:23
  • 1
    Thanks @DavidL to share this link. If I am not correct then please correct me about this. I am getting that ".ToList() is an extension method of Enumerable<> collection and returns a list of IEnumerable<> type. Thanks – Sandeep Shekhawat Jan 19 '14 at 04:24
  • 2
    @Sandeep exactly. However `List` does not support deferred execution and so the code is executed immediately. `IEnumerable` supports deferred execution, which is essential to the efficiency of LINQ across multiple projections/filters/queries. This is why LINQ almost always works against the `IEnumerable` or `IQueryable` interface. – Haney Jan 19 '14 at 04:26
  • @DavidHaney After implementation of it. I am getting this error "LINQ to Entities does not recognize the method 'System.String ToString()' method, and this method cannot be translated into a store expression." – Sandeep Shekhawat Jan 19 '14 at 05:04
  • 1
    @Sandeep - this is because .ToString() has no representation in SQL. To get rid of this error, move your .ToList() to before the .Select(). `db.Publishers.ToList().Select(x=>new SelectListItem...)` – Tommy Jan 19 '14 at 07:23
  • @Tommy Thanks.It's superb!!+1 – Sandeep Shekhawat Jan 19 '14 at 07:33
  • I tried this, but now I'm getting this in my Error Log: "The ObjectContext instance has been disposed and can no longer be used for operations that require a connection." – Christine May 09 '16 at 21:20
  • So, ToList() creates a cache of results? – Guilherme de Jesus Santos Jul 04 '16 at 13:58