简体   繁体   中英

ASP.NET Boilerplate - “The ObjectContext instance has been disposed” exception

I'm trying to use ASP.NET Boilerplate to handle my project and I have one serious problem.

I have 2 Models : Photo and Comment:

public class Comment : Entity<int>
{
    [DataType(DataType.MultilineText)]
    public string Text { get; set; }
    public string Author { get; set; }

    public int ItemID { get; set; }
    public virtual Item Item { get; set; }
}

public class Item : Entity<int>
{
    public string Title { get; set; }

    public string Description { get; set; }

    public ItemSourceType SourceType { get; set; }

    public byte[] PhotoBytes { get; set; }

    public string Url { get; set; }

    public virtual ICollection<Comment> Comments { get; set; }

}

Additionally I have created default OOB repository based on RepositoryBase<Item> and same for Comment .

The problem exists when I'm trying to get Item like this:

    public ActionResult Details(int? id)
    {
        if (id == null)
        {
            return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
        }

        Item item = _repoItems.Get(id.Value);
        if (item == null)
        {
            return HttpNotFound();
        }
        return View(item);
    }

When I'm debugging this code I can see that item has this exception in Comments property.

Am I missing something from ASP.NET Boilerplate or what?

Thanks for helping!

//Edit: Full exception message:

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

I just stumbled across the same problem. Seems to be that the UnitOfWork implementation creates and disposes a new DbContext for each "UnitOfWork"

So to fix that particular problem, try to inject "IUnitOfWorkManager" and call

public ActionResult Details(int? id)
    {
        if (id == null)
        {
            return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
        }

        using(var uow = _unitOfWorkManager.Begin())
        {
            try
            {
                Item item = _repoItems.Get(id.Value);
                if (item == null)
                {
                    return HttpNotFound();
                }
                return View(item);
            }
            finally
            {
               uow.Complete()
            };
        }
    }

if that works, consider calling "Begin()" in the constructor of your ApiController and "Complete()" in its "Dispose()" override. Hope that helps!

No. The problem is most likely in your repository, the code of which you unfortunately have not included. If I had to guess, I'd say you're using something like the following in that repository method:

using (var context = new ApplicationContext())
{
    // fetch something
}

Your Comments property is virtual, which means by default, Entity Framework will lazy load it, only actually issuing the query for that to the database once you try to access the property. However, by that point, you context has been disposed because the repository method has already finished its work, and your context was only available inside the using block.

There's a number of ways to fix this. You could eagerly load comments inside the the using block:

return context.Items.Include("Comments").Find(id);

However, that really just glosses over the problem. The best thing you can do is just not use using . Ideally, your context should be instantiated once and only once for each request. The easiest way to do that is to use a dependency injection container and add a constructor to your repository that accepts the context:

public class MyAwesomeRepository
{
    private readonly ApplicationContext context;

    public MyAwesomeRepository(ApplicationContext context)
    {
        this.context = context;
    }
}

The configuration for your DI container will vary depending on which you choose to go with, but generally, you want to make sure that you bind your context class to request scope.

I had the similar issue with ASP.NET Boilerplate and figured out that this framework does all this DI magic properly only if you name your interfaces and classes in accordance with their naming conventions. You can somehow do it manually though, but you have to drill down into ABP architecture much deeper than you want.

A link posted by @ChrisPratt (see comments in his answer) says:

Naming conventions are very important here. For example you can change name of PersonAppService to MyPersonAppService or another name which contains 'PersonAppService' postfix since the IPersonAppService has this postfix. But you can not name your service as PeopleService. If you do it, it's not registered for IPersonAppService automatically (It's registered to DI framework but with self-registration, not with interface), so, you should manually register it if you want.

In my case, I had a service called ProductService implementing interface IProductAppService. It failed for me with ObjectDisposedException exception until I renamed my service to ProductAppService.

I don't think that OP still have this issue, but hopefully it will save few hours for the folks struggling with ABP like me. :-)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM