Upgrade plugin from 3.30 to 3.40 - The operation cannot be completed because the DbContext has been disposed.

This topic was automatically closed 365 days after the last reply. New replies are no longer allowed.
9 years ago
It seems that we've solved it, and I'd like to share here some thoughts hoping they will be useful for somebody.

Just for context

We've made a lot of customizations to Nop, mostly externalized via Plugin. In this case, we were having problems with one plugin that we have for data importation, that works inside a thread. The first that we faced, was the DbContext disposed problem. Scanning the forum, I saw this thread.

As Entity Framework context tracks the entities and our process takes a lot of time, we needed to be able to clean and resolve the EF context several times during the process, to keep it clean. We were unable in previous versions of Autofac. I don't know exactly why, but BeginLifetimeScope didn't work as we expected. Now, with the update of Autofac we were able to use BeginLifetimeScope, and it seemed to be working ok. We came back to this approach when we saw rkotulan post, but as I said before, we were unable to resolve IProductService (No scope with a Tag matching 'AutofacWebRequest' is visible from the scope in which the instance was requested.).

Solving the problem

The technique that I would suggest to resolve this kind of problems would be grab all the dependencies specified in the service that you can't resolve and try to resolve them individually:

using (var scope = EngineContext.Current.ContainerManager.Container.BeginLifetimeScope())
{
    var cacheManager=Resolve<ICacheManager>(string.Empty, scope);
    var productRepository= Resolve<IRepository<Product>>(string.Empty, scope);
     ...
     ...
     ...
    var relatedProductRepository= Resolve<IRepository<RelatedProduct>>(string.Empty, scope);
}

You will get to a point where one of these dependencies will fail, and then, recursively you will need to try to resolve the dependencies of the failing service, till you get to something revealing.

What was happening to me? As I was saying, we've done several customizations. One of them is a payment replacement service that changes a bit nopCommerce native behaviour. I didn't remember when this issue started, but we inject this service and replace the default one. This service was declared in a plugin where we have an old DependencyRegistrar which still had InstancePerHttpRequest (which is deprecated now).

And that was all, a plugin badly configured. My advice would be, check and double check your DependencyRegistrar.cs, and be sure that you have updated them to be compliant with last Autofac version.

Thanks for your help!
9 years ago
Ouch - same issue different place

I have a plugin that adds a tab to the Product Edit page, I am using a FilterProvider to get my fields out of the submitted form data using - NameValueCollection form = controllerContext.HttpContext.Request.Unvalidated.Form; then in a service I am retrieving a record from the database using an Id that is on the form, but I get this error as well and it won't get the record form the database.


    class POSTProvider : IFilterProvider
    {
        private readonly IDbContext _dbContext;
        private readonly IActionFilter _actionFilter;
        private readonly IListModuleService _listmoduleService;

        public POSTProvider(IDbContext dbContext, IActionFilter actionFilter, IListModuleService listmoduleService)
        {
            this._dbContext = dbContext;
            this._actionFilter = actionFilter;
            this._listmoduleService = listmoduleService;
        }

        public IEnumerable<Filter> GetFilters(ControllerContext controllerContext, ActionDescriptor actionDescriptor)
        {

            //List Module Product Data for Edit Product
            if (actionDescriptor.ControllerDescriptor.ControllerType == typeof(Nop.Admin.Controllers.ProductController) && actionDescriptor.ActionName.Equals("Edit") && controllerContext.HttpContext.Request.HttpMethod == "POST")
            {

                NameValueCollection form = controllerContext.HttpContext.Request.Unvalidated.Form;

                if (form["ListModuleId"] != null)
                {
                    _listmoduleService.UpdateRecord(null, form);
                }

...





public virtual void UpdateRecord(ListModuleRecordViewModel model = null, NameValueCollection form = null)
        {
            ListModuleRecord rec = new ListModuleRecord();
            
            ListModule module = new ListModule();

            if (model != null)
            {
                if (model.ListModuleRecordId != 0)
                {
                    rec = GetListModuleRecordById(model.Id);
                    module = rec.ListModule;
                }
                else
                {
                    rec = new ListModuleRecord();
                    module = GetListModuleById(model.ListModuleId);
                }
            }
            else
            {
                int ListModuleId = int.Parse(form["ListModuleId"]);
                int ListModuleRecordId = int.Parse(form["ListModuleRecordId"]);

                if (ListModuleRecordId != 0)
                {
                    rec = GetListModuleRecordById(ListModuleRecordId);
                    module = GetListModuleById(ListModuleId);
                }
                else
                {
                    rec = new ListModuleRecord();
                    module = GetListModuleById(ListModuleId);
                }

            }

            Dictionary<string, string> formItems = new Dictionary<string, string>();

...




        public virtual ListModule GetListModuleById(int Id)
        {
            return _listmoduleRepository.GetById(Id);
        }



It crashes on GetListModuleById()
9 years ago
swapping the DependecyRegister.cs to have .InstancePerLifetimeScope() instead has not resolved the issue.
9 years ago
ok, to get my IFilterProvider working I've done as suggested and resolved the dependency manually so I removed


private readonly IListModuleService _listmoduleService;


from the constructors and added


var _listmoduleService = EngineContext.Current.Resolve<IListModuleService>();


into the class. I'm surprised that I have to do this, as it shows me that basically the IFilterProvider no longer has a dbcontext and they can no longer use the Constructors to access services etc., but it's an admin page so it shouldn't cause any slow downs for the customers, and I don't fully understand what's happened yet - but I have noted that the ActionFilters run at least 15 times for each page view, so I've made sure it's not trying to resolve these services every time it is called - it doesn't seem as smooth as it was, but I can live with it :-)

I was also surprised that this change didn't affect my importer - but I'm using the dbcontext directly as it's much sexier and gives me very fast bulk imports

var _test = _dbContext.Set<ProductCategory>().Where(x => x.ProductId == product.Id);
9 years ago
I am facing the same issue. I am also using filter provider and fetch the product using GetProductById() method. But it throws the same error DBContext has been disposed on GetById method. Here is my code:

In FilterProvider class:

public IEnumerable<Filter> GetFilters(ControllerContext controllerContext, ActionDescriptor actionDescriptor)
        {
            if (actionDescriptor.ControllerDescriptor.ControllerType == typeof(ProductController) &&
                      (actionDescriptor.ActionName.Equals("Edit")) && actionDescriptor.GetCustomAttributes(true).Count() > 0 &&
                _pluginFinder.GetPluginDescriptorBySystemName("MyPluginName") != null)
            {
                return new Filter[]
                    {
                        new Filter(this._actionFilter, FilterScope.Action, 101)
                    };
            }
            
            return new Filter[] { };
        
        }


In ActionFilterproductUpdate class:

  public class ActionFilterProductUpdate : ActionFilterAttribute, IActionFilter
    {
        private readonly IProductService _productService = EngineContext.Current.Resolve<IProductService>();
      
        public void OnActionExecuted(ActionExecutedContext filterContext)
        {
            var id = filterContext.RouteData.Values["id"];
            var product = _productService.GetProductById(Convert.ToInt32(id));
          
        }
     }

It throws an error on GetProductById method.
Can anyone help to resolve this?
Thanks.
9 years ago
Anki wrote:
I am facing the same issue. I am also using filter provider and fetch the product using GetProductById() method. But it throws the same error DBContext has been disposed on GetById method. Here is my code:


The other posts in the thread show what you need:

You need to change your DependencyRegistrar.cs to have .InstancePerLifetimeScope()

and you'll need to remove the Constructors from the top of the class and add a manual entry to the _productService:

var _productService = EngineContext.Current.Resolve<IProductService>();
9 years ago
Thank You, Marc
9 years ago
Hi,

I encounter similar problems in my webservice from NOP 3.2 to NOP 3.4.
I do not see the extension method (string.empty, scope).

Where is it?

Is this the solution since I already have my constructor empty () and then I use engine resolve. However, I use caching and there it goes wrong. I had similar issue in NOP 3.2 but could resolve it, however, now there is more to fix.

J.


var cacheManager=Resolve<ICacheManager>(string.Empty, scope);
9 years ago
Use

var cacheManager = EngineContext.Current.ContainerManager.Resolve<ICacheManager>(string.Empty, scope);


Rudolf
9 years ago
rkotulan wrote:

var cacheManager = EngineContext.Current.ContainerManager.Resolve<ICacheManager>(string.Empty, scope);


No overload method of Resolve takes 2 arguments
This topic was automatically closed 365 days after the last reply. New replies are no longer allowed.