ObjectContext Disposed Exception

This topic was automatically closed 365 days after the last reply. New replies are no longer allowed.
10 anni tempo fa
I have this Web Api Controller below. It operates in a Nop.Web.Api project with virtually no change of the underlying NopCommerce codebase (aside from a reference and a call to a register function on App_Start), which I want to preserve, if possible.


public class CatalogApiController : ApiController
    {
        #region Fields

        private readonly IProductService _productService;


        #endregion

        #region Constructor

        public CatalogApiController(IProductService productService)
        {
            this._productService = productService;

        }
        #endregion

        #region Utilities

        public T ShallowCopyEntity<T>(T source) where T : class, new()
        {

            // Get properties from EF that are read/write and not marked witht he NotMappedAttribute
            var sourceProperties = typeof(T)
                                    .GetProperties()
                                    .Where(p => p.CanRead && p.CanWrite);

            var newObj = new T();

            foreach (var property in sourceProperties)
            {
                 // Copy value
                    property.SetValue(newObj, property.GetValue(source, null), null);
             }

            return newObj;

        }

        public ICollection<T> ShallowCopyEntity<T>(ICollection<T> source) where T : class, new()
        {
            var collection = new List<T>();

            foreach (var t in source)
            {
                collection.Add(ShallowCopyEntity<T>(t));
            }
            return collection;
        }

        #endregion

        #region Methods

      
        [HttpGet]
        public Product GetProductById(int id)
        {
            return ShallowCopyEntity<Product>(_productService.GetProductById(id));
            // fails return _productService.GetProductById(id);
        }

        [HttpGet]
        public ICollection<ProductCategory> GetProductCategoriesById(int id)
        {
            return ShallowCopyEntity<ProductCategory>(_productService.GetProductById(id).ProductCategories.ToList());
            // fails return _productService.GetProductById(id);
        }

        #endregion
        
    }



The problem is the call to


_productService.GetProductById(id).ProductCategories.ToList()


Results in an ObjectContext Disposed exception. In other words, this code is executing outside the scope of the unit of work of the service, or at least the ObjectContext call. As you can see, I'm referring to the service directly and letting Autofac do its thing, but is... there another level of abstraction (unit of work) where I would have access to the proxies and lazy loading of the Entity Objects?
10 anni tempo fa
I believe the problem lies in your component registration. Perhaps you can show us how you registered your additional components? :)
10 anni tempo fa
Thanks for the response. You could be right, because I don't really understand the different registration settings Autofac provides. My registration section is pretty large, so I'll just post a couple of snippets which show some of the different ways I am registering things.

The Data & Entity Registration is at the bottom. Thanks in advance for any suggestions or hints!


public class WebApiConfig
    {
        public static void Register(HttpConfiguration config)
        {
            ... routes

            var builder = new ContainerBuilder();

            builder.RegisterApiControllers(Assembly.GetExecutingAssembly());

            // Service Registration....

            builder.RegisterType<ProductService>()
                      .AsImplementedInterfaces().InstancePerLifetimeScope();


           // Object Registration....
          
            builder.RegisterType<LocalizationSettings>();

           // Cached Items Registration

           builder.RegisterType<MemoryCacheManager>().
                As<ICacheManager>().Named<ICacheManager>("nop_cache_per_request").InstancePerLifetimeScope();

            builder.RegisterType<SettingService>().As<ISettingService>()
               .WithParameter(Autofac.Core.ResolvedParameter.ForNamed<ICacheManager>("nop_cache_per_request"))
               .InstancePerLifetimeScope();

            builder.RegisterType<LocalizationService>().As<ILocalizationService>()
                 .WithParameter(Autofac.Core.ResolvedParameter.ForNamed<ICacheManager>("nop_cache_per_request"))
                 .InstancePerLifetimeScope();

           // HttpContext Registration

           builder.Register(c => new HttpContextWrapper(HttpContext.Current))
                 .As<HttpContextBase>()
                 .InstancePerLifetimeScope();

            // Data & Entity Providers

            var dataSettingsManager = new DataSettingsManager();
            var dataProviderSettings = dataSettingsManager.LoadSettings();
            builder.Register(c => dataSettingsManager.LoadSettings()).As<DataSettings>();
            builder.Register(x => new EfDataProviderManager(x.Resolve<DataSettings>()))
                  .As<BaseDataProviderManager>().InstancePerDependency();

            builder.Register(x => (IEfDataProvider)x.Resolve<BaseDataProviderManager>().LoadDataProvider())
                  .As<IDataProvider>().InstancePerDependency();

            builder.Register(x => (IEfDataProvider)x.Resolve<BaseDataProviderManager>().LoadDataProvider())
                  .As<IEfDataProvider>().InstancePerDependency();

            if (dataProviderSettings != null && dataProviderSettings.IsValid())
            {
                var efDataProviderManager = new EfDataProviderManager(dataSettingsManager.LoadSettings());
                var dataProvider = (IEfDataProvider)efDataProviderManager.LoadDataProvider();
                dataProvider.InitConnectionFactory();

                builder.Register<IDbContext>(c => new NopObjectContext(dataProviderSettings.DataConnectionString))
                    .InstancePerLifetimeScope();
            }
            else
            {
                builder.Register<IDbContext>(c => new NopObjectContext(dataSettingsManager.LoadSettings().DataConnectionString))
                     .InstancePerLifetimeScope();
            }

            builder.RegisterGeneric(typeof(EfRepository<>))
                    .As(typeof(IRepository<>)).InstancePerLifetimeScope();
         }

      }
10 anni tempo fa
mattsoundworld wrote:
Thanks for the response. You could be right, because I don't really understand the different registration settings Autofac provides. My registration section is pretty large, so I'll just post a couple of snippets which show some of the different ways I am registering things.

The Data & Entity Registration is at the bottom. Thanks in advance for any suggestions or hints!


public class WebApiConfig
    {
        public static void Register(HttpConfiguration config)
        {
            ... routes

            var builder = new ContainerBuilder();

            builder.RegisterApiControllers(Assembly.GetExecutingAssembly());

            // Service Registration....

            builder.RegisterType<ProductService>()
                      .AsImplementedInterfaces().InstancePerLifetimeScope();


           // Object Registration....
          
            builder.RegisterType<LocalizationSettings>();

           // Cached Items Registration

           builder.RegisterType<MemoryCacheManager>().
                As<ICacheManager>().Named<ICacheManager>("nop_cache_per_request").InstancePerLifetimeScope();

            builder.RegisterType<SettingService>().As<ISettingService>()
               .WithParameter(Autofac.Core.ResolvedParameter.ForNamed<ICacheManager>("nop_cache_per_request"))
               .InstancePerLifetimeScope();

            builder.RegisterType<LocalizationService>().As<ILocalizationService>()
                 .WithParameter(Autofac.Core.ResolvedParameter.ForNamed<ICacheManager>("nop_cache_per_request"))
                 .InstancePerLifetimeScope();

           // HttpContext Registration

           builder.Register(c => new HttpContextWrapper(HttpContext.Current))
                 .As<HttpContextBase>()
                 .InstancePerLifetimeScope();

            // Data & Entity Providers

            var dataSettingsManager = new DataSettingsManager();
            var dataProviderSettings = dataSettingsManager.LoadSettings();
            builder.Register(c => dataSettingsManager.LoadSettings()).As<DataSettings>();
            builder.Register(x => new EfDataProviderManager(x.Resolve<DataSettings>()))
                  .As<BaseDataProviderManager>().InstancePerDependency();

            builder.Register(x => (IEfDataProvider)x.Resolve<BaseDataProviderManager>().LoadDataProvider())
                  .As<IDataProvider>().InstancePerDependency();

            builder.Register(x => (IEfDataProvider)x.Resolve<BaseDataProviderManager>().LoadDataProvider())
                  .As<IEfDataProvider>().InstancePerDependency();

            if (dataProviderSettings != null && dataProviderSettings.IsValid())
            {
                var efDataProviderManager = new EfDataProviderManager(dataSettingsManager.LoadSettings());
                var dataProvider = (IEfDataProvider)efDataProviderManager.LoadDataProvider();
                dataProvider.InitConnectionFactory();

                builder.Register<IDbContext>(c => new NopObjectContext(dataProviderSettings.DataConnectionString))
                    .InstancePerLifetimeScope();
            }
            else
            {
                builder.Register<IDbContext>(c => new NopObjectContext(dataSettingsManager.LoadSettings().DataConnectionString))
                     .InstancePerLifetimeScope();
            }

            builder.RegisterGeneric(typeof(EfRepository<>))
                    .As(typeof(IRepository<>)).InstancePerLifetimeScope();
         }

      }


Why don't you implement the IDependencyRegistrar interface? That interface will be taken up by nopCommerce engine automatically and will be registered together with other components. Now it appears that you are registering these components separately from nopCommerce's registration. :)
10 anni tempo fa
Yes, you are correct, it is a separate registration. For whatever reason, this second registration is required for Web Api applications to work. The default registration has the .InstancePerHttpRequest(), which does not work.

I wasn't aware the IDependencyRegistrar interface would be automatically picked up, so I'll definitely refactor the registrations to a separate implementation. Thanks!
10 anni tempo fa
mattsoundworld wrote:
Yes, you are correct, it is a separate registration. For whatever reason, this second registration is required for Web Api applications to work. The default registration has the .InstancePerHttpRequest(), which does not work.

I wasn't aware the IDependencyRegistrar interface would be automatically picked up, so I'll definitely refactor the registrations to a separate implementation. Thanks!


I am not very familiar with WebApi, but I don't think it's correct to say that InstancePerHttpRequest() doesn't work. Have you referred to article such as this one: http://weblogs.asp.net/cibrax/archive/2012/02/29/doing-di-with-autofac-in-asp-net-web-api.aspx?
10 anni tempo fa
I hadn't seen that article, but I had trouble resolving dependencies, and they went away when I re-registered with InstancePerLifetimeScope(). You may be right that that isn't the root cause of my problems, but that does appear to be a solution that has worked in a lot of different scenarios I have tested.

Remember that I am not embedding the Api controllers IN Nop.Web, and I am actually referencing it from an external project, so perhaps that is why this case is slightly different(?) I don't know.

Will do more research, and thanks for the info!
10 anni tempo fa
So I think I know why the second registration is required. All the initializers I have seen call the following code, which generates a NEW container and associates the AutofacWebApiDependencyResolver with that. Which works, so I can't knock it too much.



            var builder = new ContainerBuilder();

            builder.RegisterApiControllers(Assembly.GetExecutingAssembly());

            var container = builder.Build();

            var resolver = new AutofacWebApiDependencyResolver(container);
            config.DependencyResolver = resolver;


So the problem with implementing IDependencyRegistrar is that it uses the main builder that eventually becomes the official container for everything in NopCommerce. In order to tap into that, I think I need to call the following Update, rather than Build. I just need to figure out how and where to properly call the existing container.



            var container = builder.Update(//...existing container);

10 anni tempo fa
Success! No need for IDependencyRegistrar or anything. The secret was EngineContext.Current.ContainerManager.Container in the Nop.Core.Infrastructure space.



using System;
using System.Web.Http;
using System.Reflection;
using Autofac;
using Autofac.Integration.WebApi;
using Nop.Core.Infrastructure;

namespace Nop.Web.Api
{
    public class WebApiConfig
    {
        public static void Register(HttpConfiguration config)
        {
            // Route for POST method

            config.Routes.MapHttpRoute(
            name: "DefaultApi2",
            routeTemplate: "api/{controller}/{action}/{id}",
            defaults: new { id = RouteParameter.Optional }
            );

            //   Route  GET method

            config.Routes.MapHttpRoute(
               name: "DefaultApi1",
               routeTemplate: "api/{controller}/{action}/{id}",
               defaults: new { action = "get", id = RouteParameter.Optional }
            );

           var builder = new ContainerBuilder();

            builder.RegisterApiControllers(Assembly.GetExecutingAssembly());

            //Update existing, don't create a new container
            builder.Update(EngineContext.Current.ContainerManager.Container);

            //Feed the current container to the AutofacWebApiDependencyResolver
            var resolver = new AutofacWebApiDependencyResolver(EngineContext.Current.ContainerManager.Container);
            config.DependencyResolver = resolver;

        }

    }
}
9 anni tempo fa
Hello,

I wanted to know how this is going for you at the moment. I work a lot with Web API, Breeze and JS Frameworks for front end development on many projects I have developed. Please I am not very familiar with nopCommerce, I wanted to know how I can create a separate Web API project for nopCommerce and at the same time leverage other current Web API technologies.

Please your help or suggestions be very appreciated. Thanks.
This topic was automatically closed 365 days after the last reply. New replies are no longer allowed.