Net core repository pattern. I have below piece of code
public class MapFileUploadBusinessLogic : IMapFileUploadBusinessLogic
{
public MapFileUploadBusinessLogic(IServiceScopeFactory scopeFactory, IOptions<AuthenticationConfig> authenticationConfig, IOptions<ADFClient> AdfClient, IUnitOfWork unitOfWork, IConfiguration configuration, IMapper mapper, ILogger<MapFileUploadBusinessLogic> logger)
{
UnitOfWork = unitOfWork ?? throw new ArgumentNullException(nameof(unitOfWork));
Configuration = configuration ?? throw new ArgumentNullException(nameof(configuration));
CoordinateReferenceSystemRepository = UnitOfWork.GetRepository<CoordinateReferenceSystem>();
this.Logger = logger ?? throw new ArgumentNullException(nameof(logger));
MapUploadRepository = UnitOfWork.GetRepository<MapUpload>();
azureDataFactoryRepository = unitOfWork.GetAzureRepository();
this._authenticationConfig = authenticationConfig.Value;
this._ADFClient = AdfClient.Value;
this.scopeFactory = scopeFactory;
}
public AuthenticationConfig _authenticationConfig { get; set; }
public ADFClient _ADFClient { get; set; }
public IConfiguration Configuration { get; }
private IUnitOfWork UnitOfWork { get; }
private IDbRepository<MapUpload> MapUploadRepository { get; set; }
public async Task UploadMapFile(List<MapFileUploadEntity> mapFileUploadEntities)
{
Dictionary<string, object> parameters = new Dictionary<string, object>
{
{"Parameters", mapFileUploadEntities }
};
DataFactoryManagementClient dataFactoryManagementClient = azureDataFactoryRepository.InitiateConnection(_authenticationConfig);
var result = await azureDataFactoryRepository.RunADFPipeline(dataFactoryManagementClient, parameters, _ADFClient, ApplicationConstants.SciHubPipeline);
await Task.Delay(15000);
ADFPipeLineStatus aDFPipeLineStatus = await azureDataFactoryRepository.GetPipelineInfoAsync(dataFactoryManagementClient, _ADFClient, result);
if(aDFPipeLineStatus.Status == "Succeeded")
{
var mapUploadData = await this.MapUploadRepository.GetAsync(x => mapFileUploadEntities.Any(m => m.MapName == x.MapName)).ConfigureAwait(false);
//push notification
}
else if(aDFPipeLineStatus.Status == "Failed")
{
MapUpload mapUpload = await MapUploadRepository.GetFirstOrDefaultAsync(x => x.MapId == 9);
var mapUploadData = await this.MapUploadRepository.GetAsync(x => mapFileUploadEntities.Any(m => m.MapName == x.MapName)).ConfigureAwait(false);
//push notification
}
}
In the above code when I call await this.MapUploadRepository.GetAsync(x => mapFileUploadEntities.Any(m => m.MapName == x.MapName)).ConfigureAwait(false); it throws
Cannot access a disposed object. A common cause of this error is
disposing a context that was resolved from dependency injection and
then later trying to use the same context instance elsewhere in your
application. This may occur if you are calling Dispose() on the
context, or wrapping the context in a using statement. If you are
using dependency injection, you should let the dependency injection
container take care of disposing context instances.\r\nObject name:
'MyContext'."
Is the Task.Delay making this problem? I have below registration in my startup.cs
services.AddDbContext<MyContext>(options =>
options.UseSqlServer(this.Configuration["AzureSQLConnectionString"]));
services.AddScoped<IUnitOfWork, UnitOfWork>();
Can someone help me to understand what I am doing wrong here? Any help would be appreciated. Thanks
Related
I am having an issue where while looping through a data set and looking in the db to see if it already exists, it works the first run but the second item causes an error.
The following method builds up a List from a raw data file
private async Task<List<Vehicle>> CreateListOfVehiclesFromAuctionDataFile(IEnumerable<string> rows)
{
var cars = new List<Vehicle>();
var vinList = new List<string>();
foreach (var dataRow in rows)
{
var data = dataRow.Split(",");
var dto = GetCarModelInfoFromAuctionData(dataRow);
if (vinList.Contains(data[14]))
{
continue;
}
vinList.Add(data[14]);
var car = new Vehicle
{
Vin = data[14],
InteriorColor = data[15],
ExteriorColor = data[16],
VehicleImageUrl = data[17],
Notes = data[18],
ModelId = await GetModelIdFromCarModelDto(dto)
};
cars.Add(car);
}
return cars;
}
That method calls this method within it
private async Task<int> GetModelIdFromCarModelDto(CarModelDto dto)
{
var modelId =0;
try
{
modelId = await _context.Models.Where(u => u.ModelYear == dto.ModelYear)
.Where(u => u.ModelType == dto.ModelType)
.Where(u => u.BodyStyle.Contains(dto.BodyStyle))
.Select(u => u.ModelId)
.FirstOrDefaultAsync();
}
catch (Exception ex)
{
//log error here
var errorMessage = $"Model id not found: {ex.Message}- Exception: {ex.InnerException}";
}
return modelId;
}
Those private method calls come from this public method
public async Task<int> AddVehicleDataFromAuctionFileAsync()
{
var currentRecords = _context.AuctionDatum.Count();
var data = await AzureDataRetrieval.GetDataAsStreamAsync(AzureService.AzureContainer,
AzureFilePathsFromMain.VehicleAuctionData);
var rows = ConvertAuctionDataStreamToDataArray(data);
**var cars = await CreateListOfVehiclesFromAuctionDataFile(rows);**
var datum = CreateListOfVehicleAuctionData(rows);
await _context.AuctionDatum.AddRangeAsync(datum);
await _context.Vehicles.AddRangeAsync(cars);
await _context.SaveChangesAsync();
return await _context.AuctionDatum.CountAsync() - currentRecords;
}
The main method call comes from the controller
public VehiclesController(CarCollectionDataContext context, IConfiguration configuration)
{
_context = context;
_configuration = configuration;
}
public async Task<IActionResult> Test()
{
var vm = new VehicleDataViewModel(VehicleView.Models);
var mgr = new VehicleDataManager(_context, _configuration);
**var datum = mgr.AddVehicleDataFromAuctionFileAsync();**
vm.Models = await mgr.GetModelListAsync();
return View(vm);
}
The first time through this method it works fine and retrieve's the ModelId. The second time through it fails with the following error message. The where clause parameters, when checked manually, show there is an item that should be retrieved.
Model id not found: Cannot access a disposed object. A common cause of this error is disposing a context that was resolved from dependency injection
and then later trying to use the same context instance elsewhere in your application. This may occur if you are calling Dispose() on the context,
or wrapping the context in a using statement. If you are using dependency injection, you should let the dependency injection container take care of
disposing context instances.
Object name: 'CarCollectionDataContext'.- Exception:
I am not disposing the context anywhere in the code. The context IS brought in via DI
private readonly CarCollectionDataContext _context;
private readonly IConfiguration _configuration;
public VehicleDataManager(CarCollectionDataContext context, IConfiguration configuration)
{
_context = context;
_configuration = configuration;
AzureService = new AzureService.AzureService(_configuration);
}
I am at the end of my knowledge base on how to resolve this, any help appreciated.
Currently I'm able to handle IServiceCollection to inject mocks for particular services in the following manner.
public class TestClass
{
private IMediator _mediatr;
private void SetupProvider(IUnitOfWork unitOfWork, ILogger logger)
{
configuration = new ConfigurationBuilder().Build();
_services = new ServiceCollection();
_services.AddSingleton(configuration);
_services.AddScoped(x => unitOfWork);
_services.AddSingleton(logger);
_services.AddMediatR(Assembly.Load("Application"));
_services.AddScoped(typeof(IPipelineBehavior<,>), typeof(LoggerBehaviour<,>));
_mediator = _services.BuildServiceProvider().GetService<IMediator>();
}
[Fact]
public async void UnitTest_Success()
{
var unitOfWork = new Mock<IUnitOfWork>();
var logger = new Mock<ILogger>();
SetupProvider(unitOfWork.Object, logger.Object);
var fixture = new Fixture();
var command = fixture.Create<MediatorCommand>();
unitOfWork.Setup(x => x.Repository.FindAll(It.IsAny<IList<long>>(), It.IsAny<bool?>()))
.ReturnsAsync(new List<Domain.Model>());
var response = await _mediatr.Send(command);
using (new AssertionScope())
{
response.Should().NotBeNull();
response.IsSuccess.Should().BeTrue();
}
}
}
For the following subject under test
public class MediatorCommand : IRequest<CommandResponse>
{
public string Name { get; set ;}
public string Address { get; set; }
}
public class MediatorCommandHandler : IRequestHandler<MediatorCommand, CommandResponse>
{
private readonly ILogger _logger;
private readonly IUnitOfWork _unitOfWork;
public MediatorCommandHandler(IUnitOfWork unitOfWork, ILogger logger)
{
_logger = logger;
_unitOfWork = unitOfWork;
}
public async Task<CommandResponse> Handle(MediatorCommand command, CancellationToken cancellationToken)
{
var result = new CommandResponse { IsSuccess = false };
try
{
var entity = GetEntityFromCommand(command);
await _unitOfWork.Save(entity);
result.IsSuccess = true;
}
catch(Exception ex)
{
_logger.LogError(ex, ex.Message);
}
return result;
}
}
This test runs fine and the unitOfWork and logger mocks are used in the command handlers.
I'm try to move this so that the IServiceCollection construction happens per class instead of each test using the following:
public class SetupFixture : IDisposable
{
public IServiceCollection _services;
public IMediator Mediator { get; private set; }
public Mock<IUnitOfWork> UnitOfWork { get; private set; }
public SetupFixtureBase()
{
UnitOfWork = new Mock<IUnitOfWork>();
configuration = new ConfigurationBuilder().Build();
_services = new ServiceCollection();
_services.AddSingleton(configuration);
_services.AddScoped(x => UnitOfWork);
_services.AddSingleton(new Mock<ILogger>().Object);
_services.AddMediatR(Assembly.Load("Application"));
_services.AddScoped(typeof(IPipelineBehavior<,>), typeof(LoggerBehaviour<,>));
Mediator = _services.BuildServiceProvider().GetService<IMediator>();
}
public void Dispose()
{
Mediator = null;
_services.Clear();
_services = null;
}
}
public class TestClass : IClassFixture<SetupFixture>
{
protected readonly SetupFixture _setupFixture;
public UnitTestBase(SetupFixture setupFixture)
{
_setupFixture = setupFixture;
}
[Fact]
public async void UnitTest_Success()
{
var fixture = new Fixture();
var command = fixture.Create<MediatorCommand>();
_setupFixture.UnitOfWork.Setup(x => x.Repository.FindAll(It.IsAny<IList<long>>(), It.IsAny<bool?>()))
.ReturnsAsync(new List<Domain.Model>());
var response = await _mediatr.Send(command);
using (new AssertionScope())
{
response.Should().NotBeNull();
response.IsSuccess.Should().BeTrue();
}
}
}
Unfortunately with this method my mocks do not get injected on the command handler. Is there a way to get this to work?
Thank you,
I found the issue and it is not related to moving to IClassFixuture<>. The issue was that I was initializing Mediator on a base class an then adding the mock UnitOfWork on a derived class.
This cause the Mediator initialization to fail because one of the beheviours expected the UnitOfWork which at the time was not yet on the container.
Moving the initialization of Mediator after all the services have been added helped me resolve the issue and now all works as expected.
If you try the same thing, please make sure to include all the services in the container before initializing any objects that require those dependencies.
Thank you all those who had input.
I'm trying to use DBContext in Hosted services but getting this error.
I tried to follow this accepted answer but somehow its not working, I'm not sure what I'm doing wrong.
I'm new to .net please guide me into right direction.
Unhandled Exception: System.InvalidOperationException: Cannot consume
scoped service 'StatusApp.Context.DBContext' from singleton
'Microsoft.AspNetCore.Hosting.Internal.HostedServiceExecutor'.
public class TokenService : IHostedService
{
public IConfiguration _Configuration { get; }
protected IMemoryCache _cache;
private Timer _timer;
public IHttpClientFactory _clientFactory;
public DBContext _DBcontext;
private readonly IServiceScopeFactory _scopeFactory;
public TokenService(IConfiguration configuration, IMemoryCache memoryCache, IHttpClientFactory clientFactory, DBContext DBcontext, IServiceScopeFactory scopeFactory)
{
_Configuration = configuration;
_cache = memoryCache;
_clientFactory = clientFactory;
_scopeFactory = scopeFactory;
_DBcontext = _scopeFactory.CreateScope().ServiceProvider.GetRequiredService<DBcontext>();
}
public Task StartAsync(CancellationToken cancellationToken)
{
_timer = new Timer(getOrg, null, 0, 1000);
return Task.CompletedTask;
}
public Task StopAsync(CancellationToken cancellationToken)
{
//Timer does not have a stop.
_timer?.Change(Timeout.Infinite, 0);
return Task.CompletedTask;
}
public async Task getOrg()
{
var request = new HttpRequestMessage(HttpMethod.Get, "organizations");
var response = await _client_NP.SendAsync(request);
var json = await response.Content.ReadAsStringAsync();
OrganizationsClass.OrgsRootObject model = JsonConvert.DeserializeObject<OrganizationsClass.OrgsRootObject>(json);
foreach (var item in model.resources)
{
var g = Guid.Parse(item.guid);
var x = _DBcontext.Organizations.FirstOrDefault(o => o.OrgGuid == g);
if (x == null)
{
_DBcontext.Organizations.Add(new Organizations
{
OrgGuid = g,
Name = item.name,
CreatedAt = item.created_at,
UpdatedAt = item.updated_at,
Timestamp = DateTime.Now,
Foundation = 3
});
}
else if (x.UpdatedAt != item.updated_at)
{
x.CreatedAt = item.created_at;
x.UpdatedAt = item.updated_at;
x.Timestamp = DateTime.Now;
}
}
await _DBcontext.SaveChangesAsync();
}
}
You're almost there, but you've left DBContext as a dependency in TokenService's constructor. Remove that and you'll no longer receive the error.
public TokenService(
IConfiguration configuration,
IMemoryCache memoryCache,
IHttpClientFactory clientFactory,
DBContext DBcontext,
IServiceScopeFactory scopeFactory)
However, you're not quite following the recommendation for dealing with DbContext's in a singleton service. Instead of creating a single instance of DBContext in the constructor and storing it as a field, create a scope and a corresponding DBContext whenever you need it. In your case, that's in the getOrg method.
Follow these steps to achieve that:
Remove the _DBcontext field from your TokenService class:
public DBContext _DBcontext;
Remove the associated assignment from the TokenService constructor:
_DBcontext = _scopeFactory.CreateScope().ServiceProvider.GetRequiredService<DBcontext>();
In getOrg, create a scope, resolve an instance of DBContext and, lastly, dispose of the scope:
public async Task getOrg()
{
var request = new HttpRequestMessage(HttpMethod.Get, "organizations");
var response = await _client_NP.SendAsync(request);
var json = await response.Content.ReadAsStringAsync();
OrganizationsClass.OrgsRootObject model = JsonConvert.DeserializeObject<OrganizationsClass.OrgsRootObject>(json);
using (var scope = _scopeFactory.CreateScope())
{
var dbContext = scope.ServiceProvider.GetRequiredService<DBcontext>();
foreach (var item in model.resources)
{
var g = Guid.Parse(item.guid);
var x = dbContext.Organizations.FirstOrDefault(o => o.OrgGuid == g);
if (x == null)
{
dbContext.Organizations.Add(new Organizations
{
OrgGuid = g,
Name = item.name,
CreatedAt = item.created_at,
UpdatedAt = item.updated_at,
Timestamp = DateTime.Now,
Foundation = 3
});
}
else if (x.UpdatedAt != item.updated_at)
{
x.CreatedAt = item.created_at;
x.UpdatedAt = item.updated_at;
x.Timestamp = DateTime.Now;
}
}
await dbContext.SaveChangesAsync();
}
}
Instead of using the _DBContext field, the code above creates a local, properly-scoped variable dbContext and uses that instead.
I get this error
System.InvalidOperationException:
'Cannot resolve scoped service 'LC.Assets.API.ApiDbFullContext' from root provider.'
Registering DbContextPool:
services.AddDbContextPool<ApiDbFullContext>(o => o.UseSqlServer(connString));
ExtensionDbHelper:
public class ExtensionDBHelper : DisposableBase, IExtensionDBHelper
{
public ExtensionDBHelper(IServiceProvider service)
{
IHttpContextAccessor http = service.GetRequiredService<IHttpContextAccessor>();
IHostingEnvironment env = service.GetRequiredService<IHostingEnvironment>();
IApiFullDbContext db = service.GetRequiredService<ApiDbFullContext>();
this.DB = db;
this.Helper = new ApiDbContextHelper(http, db, env);
this.Worker = new ApiDbUnitOfWork(this.Helper);
}
public IApiFullDbContext DB { get; }
public IApiDbContextHelper Helper { get; }
public ApiDbUnitOfWork Worker { get; }
}
UseLCCors in IApplicationBuilderExtension:
public static IApplicationBuilder UseLCCors(this IApplicationBuilder builder, Action<LCCorsOptions> option)
{
LCCorsOptions opt = new LCCorsOptions();
option.Invoke(opt);
IExtensionDBHelper helper = new ExtensionDBHelper(builder.ApplicationServices);
ICorsOriginHub hub = GenericExpressions.CorsOriginHub(helper.Worker.GetRepo<CorsOriginHub>().DbSet).FirstOrDefault(h => h.Active && h.Version.Equals(opt.Version));
if (hub == null)
{
throw new HubNotFoundException(opt.Version);
}
else if (hub.Outdated)
{
throw new IncludeHubOutdatedException(opt.Version);
}
foreach(ICorsOriginEntry entry in hub.Items.Where(itm => itm.Active))
{
builder.UseCors(options => options.WithOrigins(entry.Host).AllowAnyMethod().AllowAnyHeader());
}
return builder;
}
this solved my issue:
Changing from AddDbContextPool to plain AddDbContext
Altering the code in ExtensionDBHelper to:
IApiFullDbContext db = service.CreateScope().ServiceProvider.GetRequiredService<ApiDbFullContext>();
I need to resolve a DbContext based on tenant's owin value. But in the pipeline of method OnDisconnected of hub, the HttpContext is not accessible.
My hub class:
public class UserTrackingHub : Hub
{
private readonly UserContext _context;
public UserTrackingHub(UserContext context) { ... }
public override async Task OnConnected() { /* OK HERE...*/ }
public override async Task OnDisconnected(bool stopCalled)
{
// NEVER FIRES WITH IF I USE THE CTOR INJECTION.
var connection = await _context.Connections.FindAsync(Context.ConnectionId);
if (connection != null)
{
_context.Connections.Remove(connection);
await _context.SaveChangesAsync();
}
}
}
Here's my Autofac config:
public static IContainer Register(IAppBuilder app)
{
var builder = new ContainerBuilder();
// Other registers...
builder.Register<UserContext>(c =>
{
// Details and conditions omitted for brevity.
var context = HttpContext.Current; // NULL in OnDisconnected pipeline.
var owinContext = context.GetOwinContext();
var tenant = owinContext.Environment["app.tenant"].ToString();
var connection = GetConnectionString(tenant);
return new UserContext(connection);
});
var container = builder.Build();
var config = new HubConfiguration
{
Resolver = new AutofacDependencyResolver(container)
};
app.MapSignalR(config);
return container;
}
Can someone help me to identify the tenant OnDisconnected in this or any other way?
Thanks!
For anyone interested, I end up injecting a context factory instead the context itself:
public class UserTrackingHub : Hub
{
private readonly Func<string, UserContext> _contextFactory;
public UserTrackingHub(Func<string, UserContext> contextFactory) { ... }
public override async Task OnConnected() { ... }
public override async Task OnDisconnected(bool stopCalled)
{
var tenant = Context.Request.Cookies["app.tenant"].Value;
using (var context = _contextFactory.Invoke(tenant))
{
var connection = await context.Connections.FindAsync(Context.ConnectionId);
if (connection != null)
{
context.Connections.Remove(connection);
await context.SaveChangesAsync();
}
}
}
}
And Autofac's config:
// Resolve context based on tenant
builder.Register<Func<string, UserContext>>(c => new Func<string, UserContext>(tenant => UserContextResolver.Resolve(tenant)));