Base solution for your next web application
Open Closed

DbContext has been disposed #1213


User avatar
0
arslanali created

I am getting above error as soon i am accessing the repository to get records in the SetSupplier() method. I am using IRepository<SupplierProfile> supplierRepository as constructor injection in my SupplierAppService Class. review code below: (My SupplierProfile Entity is having a one-one relationship with the Tenant, and it is also implementing IMusthaveTenant, Please guide if i can avoid the Where clause from my query as IMustHaveTenant should implement it. )

class SupplierAppServices: FixonClickAppServiceBase, ISupplierAppServices
    {
       
        private readonly IRepository<SupplierProfile> _supplierRepository;
        private SupplierProfile _supplierProfile;
        
        public SupplierAppServices(IRepository<SupplierProfile> supplierRepository)
        {
            _supplierRepository = supplierRepository;
            SetSupplier();
        }
        private void SetSupplier()
        {
            _supplierProfile = _supplierRepository.GetAll().Where(t=>t.TenantId==AbpSession.TenantId).FirstOrDefault();
        }

9 Answer(s)
  • User Avatar
    0
    hikalkan created
    Support Team

    Hi,

    You have 2 problems in your code:

    1. Repository's GetAll() method should always be used in a Unit Of Work. For short answer: Make SetSupplier "protected virtual" and add [UnitOfWork] attribute to this method. For long answer, please read the unit of work documentation to understand it better since you may have other problems if you don't understand it well. At least, read this section: <a class="postlink" href="http://www.aspnetboilerplate.com/Pages/Documents/Unit-Of-Work#DocRepositoryGetAll">http://www.aspnetboilerplate.com/Pages/ ... toryGetAll</a>

    2. It's not a good idea to make a database call in your constructor. This is a general rule for constructors. Not specific to AspNet Zero, but a general programming pattern. Why don't you lazy load the supplier and call it whenever you need?

  • User Avatar
    0
    arslanali created

    Hi Hikalkan , Yes I read that documentation last night and I think I should call ToList() method after the getall method.

    Anyways I need a little more help. Can you please give me sample code snippet to lazy load my supplier. Also note that my supplier and tenant entities are in one to one relationship having a navigation object in each entity class. Do you recommend calling a GetCurrentTenant () method in my AppService constructor to load my supplier object?

  • User Avatar
    0
    hikalkan created
    Support Team

    Additional info: You can write your code like that:

    _supplierRepository.FirstOrDefault(t=>t.TenantId==AbpSession.TenantId);
    

    Which is shorter and does not need an active UOW (since db connection is opened/closed in repository method).

    Actually, the lazy load I mention is not related to our framework. Just a general coding practice. You can define a Supplier property in your class. Then in first usage of this property, you get it from database and set a field. Then you get again from that field. Or you can directly use Lazy<T> class of .NET.

    Another note: You can extend Tenant entity to add supplier informations. One to One relations is not a good usage in general unless your case really requires it.

  • User Avatar
    0
    arslanali created

    I tried everything as mentioned, but I am getting different errors on your different suggestions. Let me list the two which I want to use: 1.Your suggestion to use

    _supplierRepository.FirstOrDefault(t=>t.TenantId==AbpSession.TenantId);
    

    is not retrieving the supplier object from db and Throwing a null object reference at the time of first usage in following method:

    public List<SupplierBusnsServiceDto> GetSupplierBusnsService()
            {
              
                return Mapper.Map<List<SupplierBusnsServiceDto>>(_supplierProfile.supplierbusnsServices);
            }
    

    I have created a one-one relation with Tenant because I am trying to build a Supplier Profile architecture, which will have its business services, photos , videos and other tables. All of these will be child tables of the SupplierProfile Entity. My db model looks like following:

    1. Please recommend if I may use like
    public SupplierAppServices(IRepository<SupplierProfile> supplierRepository)
            {
                _supplierRepository = supplierRepository;
                _supplierProfile = GetCurrentTenant().supplier; // or
                //_supplierProfile = supplierRepository.Get(AbpSession.TenantId.Value);
            }
    

    Note that all of this is in my AppService Class, where all methods of this class are UOW by default and should get the Repository using dependency injection. And by using AppServiceBase inheritance i should be able to get the TenantManager and UserManager methods like GetCurrentTenant and GetCurrentUser. But i am getting error on calling GetCurrentTenant() in my constructor that "Must set UnitOfWorkManager before use it" on following line in my AppServiceBase Class

    Line 60:             using (CurrentUnitOfWork.SetTenantId(null))
    Line 61:             {
    Line 62:                 return TenantManager.GetById(AbpSession.GetTenantId());
    

    Summary: First Try is not giving data because there is a null object reference errors because the repository object is not retrieving data from db. Second try is not giving tenant because UOW is not set where as i am calling the GetTenantId from my app service Constructor.

    I strongly hope that you will guide me with better programming logic and a permanent solution.

  • User Avatar
    0
    hikalkan created
    Support Team

    Hi,

    Do not use GetCurrentTenant or any other database related code in constructor. Because, database connection is not available in constructor. And as I said before, it's a bad practice in general. Your code should be something like that:

    public class SupplierAppServices : ISupplierAppServices
    {
        private SupplierProfile _supplierProfile;
        
        public SupplierAppServices(IRepository<SupplierProfile> supplierRepository)
        {
            _supplierRepository = supplierRepository;
        }
        
        public void MyPublicMethod()
        {      
            var supplier = GetSupplier();
            //use supplier here...
        }
    
        private SupplierProfile GetSupplier()
        {
            //lazy load logic
            if(_supplierProfile == null)
            {
                _supplierProfile = _supplierRepository.FirstOrDefault(t=>t.TenantId==AbpSession.TenantId);
            }
        
            return _supplierProfile;        
        }
    }
    
  • User Avatar
    0
    arslanali created

    Thanks bro. It did resolve my problem already.

  • User Avatar
    0
    marcosli created

    Hi Halil,

    The attribute [UnitOfWork] doesn't work in Unit Tests methods? Or i need to use IUnitOfWorkManager and begin a transaction explicitly?

  • User Avatar
    0
    hikalkan created
    Support Team

    Hi,

    It can not work for unit tests since unit tests are not dependency injected. Yes, you need to begin an explicit UOW if you need in unit tests.

  • User Avatar
    0
    marcosli created

    Thanks Halil!