- 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)
-
0
hi
Can you provide a minimum project?
-
0
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.*
-
0
ok, I will check.
-
0
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.
-
0
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(); } }
-
0
Hi maliming great solution i will try. I shared with them if no problem https://supportcenter.devexpress.com/ticket/details/t1021441/objectdatasource-dependecy-injection
-
0
🙂
-
0
🙂
Hi, i tried now but i got error, what is lifetime IScopedServiceProvider, Scoped or Transient
-
0
I sent the test project as an e-mail.
-
0
context.Services.TryAddSingleton(typeof(IScopedServiceProvider<ReportingDataSourceService>), typeof(ScopedServiceProvider<ReportingDataSourceService>));
-
0
context.Services.TryAddSingleton(typeof(IScopedServiceProvider<ReportingDataSourceService>), typeof(ScopedServiceProvider<ReportingDataSourceService>));
Then that was the problem, i was inject it scoped
-
0
hi
Is the problem resolved?
-
0
Yes, thank you
-
0
-
0
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.
-
0
I dont use multiple context, this linq working in application service. Can you send the project you solved before?
-
0
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
-
0
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(); ... }