Open Closed

Instances cannot be resolved and nested lifetimes cannot be created from this LifetimeScope as it (or one of its parent scopes) has already been disposed. #1698


User avatar
0
murat.yuceer created
  • ABP Framework version: v4.3.0
  • UI type: Blazor
  • DB provider: EF Core

Hello, I try to use devexpress xtrareport with their objectdatasource but i get error "Instances cannot be resolved and nested lifetimes cannot be created from this LifetimeScope as it (or one of its parent scopes) has already been disposed."

This is my objectdatasource class, i bind to report get method

    [HighlightedClass]
    public class ObjectDataSource : ITransientDependency
    {

        private readonly IServiceScopeFactory _serviceScopeFactory; // <== HERE DISPOSED = TRUE

        public ObjectDataSource( IServiceScopeFactory serviceScopeFactory){
              _serviceScopeFactory = serviceScopeFactory;
        }

        [HighlightedMember]
        public async Task<IEnumerable<DataModel>> Get(Guid id)
        {
            var scope = _serviceScopeFactory.CreateScope(); // _serviceScopeFactory is disposed here

...

            return dataModel;
        }
    }
}

At first I implemented this interface "IWebDocumentViewerReportResolver" to override xtrareport's creating progress (xtrareport contain objectdatasource)

public class WebDocumentViewerReportResolver : IWebDocumentViewerReportResolver
{
    ObjectDataSourceInjector DataSourceInjector { get; }
    ReportStorageWebExtension ReportStorageWebExtension { get; }

    public WebDocumentViewerReportResolver(
        ReportStorageWebExtension reportStorageWebExtension,
        ObjectDataSourceInjector dataSourceInjector)
    {
        DataSourceInjector = dataSourceInjector ?? throw new ArgumentNullException(nameof(dataSourceInjector));
        ReportStorageWebExtension = reportStorageWebExtension ?? throw new ArgumentNullException(nameof(reportStorageWebExtension));
    }

    public XtraReport Resolve(string reportEntry)
    {
        using (MemoryStream ms = new MemoryStream(ReportStorageWebExtension.GetData(reportEntry)))
        {
            var report = XtraReport.FromStream(ms);
            DataSourceInjector.Process(report); // HERE I resolve object datasource services
            return report;
        }
    }
}

Here, i resolved services as devexpress recommend

namespace Kuys.Shared.AspNetCore.DevExp.Reporting.HttpApi
{
    public class ObjectDataSourceInjector
    {
        private readonly IServiceProvider _serviceProvider;

        public ObjectDataSourceInjector(IServiceProvider serviceProvider)
        {
            _serviceProvider = serviceProvider ?? throw new ArgumentNullException(nameof(serviceProvider));
        }

        public void Process(XtraReport report)
        {
            var dse = new UniqueDataSourceEnumerator();
            ((IServiceContainer)report).ReplaceService(typeof(IReportProvider), _serviceProvider.GetRequiredService<IReportProvider>());
            foreach (var dataSource in dse.EnumerateDataSources(report, true))
            {
                if (dataSource is ObjectDataSource ods && ods.DataSource is Type dataSourceType)
                {
                    ods.DataSource = _serviceProvider.GetRequiredService(dataSourceType); // <== DISPOSE = FALSE
                }
            }
        }
    }
}

18 Answer(s)
  • User Avatar
    0
    maliming created
    Support Team Fullstack Developer

    hi

    Can you provide a minimum project?

  • User Avatar
    0
    murat.yuceer created

    Actualy they have working example https://github.com/DevExpress-Examples/Reporting-Entity-Framework-Core-In-AspNet-Core They have explanation about dependecy injection scope, if you look maybe you can understand problem

    *This project demonstrates how to use the ObjectDataSource as a report's data source adapter for the Entity Framework DbContext.

    ASP.NET Core application with Entity Framework provides data to a report as DbContext object that operates in the scope of an HTTP request whose lifetime is different from the report's lifetime. A report is created in the HTTP request context and starts a background thread to get data and create a document. A report needs data after the initial HTTP request is completed. This means a report cannot use the default DbContext instance that the Entity Framework creates in the scope of HTTP request.

    This example demonstrates the approach that addresses the issues described above. The approach has the following requirements:

    The application needs a repository that provides data to a report.

    The repository's lifetime exceeds the lifetime of the HTTP request that creates the repository.

    A repository requests the ScopedDbContextProvider instance to create DbContext on demand.

    The HTTP request contains information used to filter data. For example, when you use the user ID to restrict access to reports. A repository, instantiated within the HTTP request's scope, stores the user ID so it is available in the filter criteria.

    The repository reads and saves values available in the HTTP request context. The values are stored for later use, so the repository saves the current user ID instead of the context-dependent IUserService object.

    The repository reads and saves the current user ID in its constructor. The constructor is invoked in the context of the HTTP request and has access to context-dependent data.*

  • User Avatar
    0
    maliming created
    Support Team Fullstack Developer

    ok, I will check.

  • User Avatar
    0
    maliming created
    Support Team Fullstack Developer

    hi

    I have solved the same problem before, but I can't remember the solution.

    https://support.abp.io/QA/Questions/1265

    I asked alkaabi, but he has not replied to me.

    Can you provide a minimal project to reproduce the problem? I think I can solve it again.

  • User Avatar
    0
    maliming created
    Support Team Fullstack Developer

    hi I found the code:

    
    public class CustomObjectDataSourceWizardTypeProvider : IObjectDataSourceWizardTypeProvider
    {
        public IEnumerable<Type> GetAvailableTypes(string context)
        {
            return new[] {
                typeof(ReportingDataSourceServiceDecorator)
            };
        }
    }
    
    
    [ExposeServices(typeof(ReportingDataSourceServiceDecorator))]
    public class ReportingDataSourceServiceDecorator : IReportingDataSourceService, ITransientDependency
    {
        private readonly IScopedServiceProvider<ReportingDataSourceService> _scopedServiceProvider;
    
        public ReportingDataSourceServiceDecorator(IScopedServiceProvider<ReportingDataSourceService> scopedServiceProvider)
        {
            _scopedServiceProvider = scopedServiceProvider;
        }
    
        public IList<BookDto> GetAllBooks()
        {
            using (var scopeServer = _scopedServiceProvider.GetService())
            {
                return scopeServer.Service.GetAllBooks();
            }
        }
    }
    
    public class ReportingDataSourceService : ApplicationService, IReportingDataSourceService
    {
        private readonly IBookCustomRepository _bookRepo;
    
        public ReportingDataSourceService()
        {
            // We use this parameterless constructor in the Data Source Wizard only, and not for the actual instantiation of the repository object.
            throw new NotSupportedException();
        }
        public ReportingDataSourceService(IBookCustomRepository bookRepo)
        {
            _bookRepo = bookRepo;
        }
    
        [UnitOfWork]
        public virtual IList<BookDto> GetAllBooks()
        {
            var query = _bookRepo.GetAllBooksAsync();
            return ObjectMapper.Map<IList<Book>, IList<BookDto>>(
                query.Result.ToList()
            );
        }
    }
    
    
    public interface IScopedServiceProvider<TService>
    {
        ServiceScopeScope<TService> GetService();
    }
    
    public class ScopedServiceProvider<TService> : IScopedServiceProvider<TService>
    {
        private readonly IServiceProvider _provider;
    
        public ScopedServiceProvider(IServiceProvider provider)
        {
            this._provider = provider ?? throw new ArgumentNullException(nameof(provider));
        }
    
        public ServiceScopeScope<TService> GetService()
        {
            var scope = _provider.CreateScope();
            return new ServiceScopeScope<TService>(scope);
        }
    }
    
    public class ServiceScopeScope<TService> : IDisposable
    {
        private readonly IServiceScope _scope;
    
        public ServiceScopeScope(IServiceScope scope)
        {
            _scope = scope ?? throw new ArgumentNullException(nameof(scope));
    
            Service = scope.ServiceProvider.GetRequiredService<TService>();
        }
    
        public TService Service { get; }
    
        public void Dispose()
        {
            _scope.Dispose();
        }
    }
    
  • User Avatar
    0
    murat.yuceer created

    Hi maliming great solution i will try. I shared with them if no problem https://supportcenter.devexpress.com/ticket/details/t1021441/objectdatasource-dependecy-injection

  • User Avatar
    0
    maliming created
    Support Team Fullstack Developer

    🙂

  • User Avatar
    0
    murat.yuceer created

    🙂

    Hi, i tried now but i got error, what is lifetime IScopedServiceProvider, Scoped or Transient

  • User Avatar
    0
    murat.yuceer created

    I sent the test project as an e-mail.

  • User Avatar
    0
    maliming created
    Support Team Fullstack Developer

    context.Services.TryAddSingleton(typeof(IScopedServiceProvider<ReportingDataSourceService>), typeof(ScopedServiceProvider<ReportingDataSourceService>));

  • User Avatar
    0
    murat.yuceer created

    context.Services.TryAddSingleton(typeof(IScopedServiceProvider<ReportingDataSourceService>), typeof(ScopedServiceProvider<ReportingDataSourceService>));

    Then that was the problem, i was inject it scoped

  • User Avatar
    0
    maliming created
    Support Team Fullstack Developer

    hi

    Is the problem resolved?

  • User Avatar
    0
    murat.yuceer created

    Yes, thank you

  • User Avatar
    0
    murat.yuceer created

    Hi maliming, today i tried with join its throw error

    System.InvalidOperationException: 'Cannot use multiple context instances within a single query execution. Ensure the query uses a single context instance.'

  • User Avatar
    0
    maliming created
    Support Team Fullstack Developer

    Cannot use multiple context instances within a single query execution. Ensure the query uses a single context instance.

    Are you using multiple context ? This is a EF Core error.

  • User Avatar
    0
    murat.yuceer created

    I dont use multiple context, this linq working in application service. Can you send the project you solved before?

  • User Avatar
    0
    maliming created
    Support Team Fullstack Developer

    hi

    I only implemented the ScopedServiceProvider. no other linq querys.

    Can you try to query in an uow?

    https://docs.abp.io/en/abp/latest/Unit-Of-Work#begin-a-new-unit-of-work

  • User Avatar
    0
    murat.yuceer created

    hi

    I only implemented the ScopedServiceProvider. no other linq querys.

    Can you try to query in an uow?

    https://docs.abp.io/en/abp/latest/Unit-Of-Work#begin-a-new-unit-of-work

    Thank you,

    I have already add UnitOfWork attribute but it didn't work.. After making sure that the example you gave is working, I found that it doesn't work because I didn't add virtual to method

            [HighlightedMember]
            [UnitOfWork(isTransactional: false)]
            public **virtual** IEnumerable<ToWhomItMayConcernReportModel> Get(Guid id, string lang = "tr")
            {
                    var result = _employeeRepository.AsQueryable().ToList(); 
                    
                    ...
            }
    
Made with ❤️ on ABP v9.1.0-preview. Updated on November 18, 2024, 05:54