There is a problem with async executing query to web api using entity framework.
General view such that a request is sent to API and ActionFilter catch request to function of controller, send to client status ok with response key, execute request async and after send data by SignalR.
ActionFilter starts async executing like this:
HostingEnvironment.QueueBackgroundWorkItem(async (ct) =>
{
var response = await actionContext.ActionDescriptor.ExecuteAsync(actionContext.ControllerContext,
actionContext.ActionArguments, ct);
var data = new JavaScriptSerializer().Serialize(response);
await connectionContext.Connection.Send(connectionId, $"{requestKey};{data}");
});
Controller:
[HttpPost]
[Route("")]
public ICollection<TradeAccountModel> GetAll()
{
using (var ls = _lifetimeScope.BeginLifetimeScope())
{
return _tradeAccountService.GetAll();
}
}
Service:
public ICollection<TradeAccountModel> GetAll()
{
using (_tradeAccountRepository.BeginTransaction())
{
return _tradeAccountRepository.Get().Select(acc => acc.ToModel());
}
}
Respository uses UOW pattern.
And when repository try to get data from DB there is error: System.InvalidOperationException: The operation cannot be completed because the DbContext has been disposed.
TDataRepository containts common operations and extends BaseDataRespository, such as GetById and ect
public interface ITradeRepository: ITDataRepository<TradeAccount>
{
}
internal class TradeRepository : T1DataRepository<TradeAccount>,
ITradeRepository
{
}
IEnumerable<TEntity> ITDataRepository<TEntity>.Get()
{
return base.Get<TEntity>();
}
BaseDataRespository has BeginTransaction method
public IDisposable BeginTransaction()
{
if (_scope == null)
{
_scope = new TransactionScope(
TransactionScopeOption.Required,
new TransactionOptions()
{
IsolationLevel = IsolationLevel.ReadCommitted,
Timeout = TimeSpan.FromSeconds(300)
},
TransactionScopeAsyncFlowOption.Enabled);
}
return _scope;
}
Context creates by BaseDataRespository
private TransactionScope _scope;
private readonly Lazy<DataContext> _contextFactory;
private DataContext Context => _contextFactory.Value;
public BaseDataRepository()
{
_contextFactory = new Lazy<DataContext>(()=>
{
var ctx = CreateContext();
ctx.FireBuild += Build;
return ctx;
});
}
If your DbContext is registered as scoped service / HTTP request scoped service, you should refrain from passing DbContext from your request pipeline to your background task.
This is because as per the docs:
Schedules a task which can run in the background, independent of any request.
Any scoped services that also implement IDisposable will be automatically disposed after the request completes.
You should activate new DbContext using an independent scope in your controller action / request pipeline, and pass it to your background task.
This docs is for .Net Core, but it may give you ideas how scoped services could be used in a background task.
Related
I was working with SignalR, and created timer that will execute following code where I need to get the number of unread messages from database
[Route("api/[controller]")]
[ApiController]
public class PorukeHubController : ControllerBase
{
private readonly IHubContext<PorukeHub> _hub;
private readonly TimerManager _timer;
private DnevnikContext _context { get; set; }
public PorukeHubController(IHubContext<PorukeHub> hub,
TimerManager timer, DnevnikContext context)
{
_hub = hub;
_timer = timer;
_context = context;
}
[HttpGet]
public IActionResult Get()
{
var currentUserId = 1;
if (!_timer.IsTimerStarted)
_timer.PrepareTimer(() =>
{
var unreadMessages = _context.Messages
.Where(p => p.RecieverID == currentUserId && p.isRead == false)
.Count();
_hub.Clients.All.SendAsync("checkForMessages", unreadMessages);
});
return Ok(new { Message = "Request Completed" });
}
Unfortunately, I get the following error when trying to access _context:
System.ObjectDisposedException: 'Cannot access a disposed context
instance. A common cause of this error is disposing a context instance
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 instance, or
wrapping it in a using statement. If you are using dependency
injection, you should let the dependency injection container take care
of disposing context instances. ObjectDisposed_ObjectName_Name'
I'm very confused on what steps should I take to solve this, I'm not yet familiar that much with DI too
Any help would be appreciated
You should turn your method into an async method and await the database call.
public async Task<IActionResult> Get()
{
...
var unreadMessages = await _context.Messages.Where(p => p.RecieverID == currentUserId && p.isRead == false).Count();
...
}
A DbContext only lives for a short period of time and its lifetime ends at the end of the web request for which it is created.
The delegate, however, that you use to initialize the TimerManager stores a reference to that DbContext likely for the duration of the application (assuming that TimerManager is a Singleton). But since that DbContext is disposed of soon after you initialized the TimerManager it becomes unusable.
In general, you should prevent moving injected dependencies from thread to thread (except in case those threads are part of a sequentially executed asynchronous operation), because the consuming code (i.e. your controller) doesn't know whether or not it is safe to do so.
This means that, instead of reusing the same DbContext, the timer should get its own instance and, preferable, get a fresh DbContext instance every time the timer is triggered. You should see each timer tick as a new request, just like every call to a controller is a new (web) request. And the general rule of thumb is that each new request gets its own container scope.
What this means is that, instead of reusing the same DbContext instance, you should wrap each tick in a container scope / IServiceScope and resolve the DbContext from that scope.
For instance:
private readonly TimerManager _timer;
private readonly IServiceProvider _provider;
public PorukeHubController(TimerManager timer, IServiceProvider provider)
{
_timer = timer;
_provider = provider;
}
[HttpGet]
public IActionResult Get()
{
var currentUserId = 1;
if (!_timer.IsTimerStarted)
_timer.PrepareTimer(() =>
{
using (var scope = this.provider.CreateScope())
{
var sp = scope.ServiceProvider;
var context = sp.GetRequiredService<DnevnikContext>();
var hub = sp.GetRequiredService<IHubContext<PorukeHub>>();
var unreadMessages = context.Messages
.Where(p => p.RecieverID == currentUserId && p.isRead == false)
.Count();
hub.Clients.All.SendAsync("checkForMessages", unreadMessages);
}
});
return Ok(new { Message = "Request Completed" });
}
Although the code above fixes the initial problem of letting the DbContext go out of scope, it poses a new problem, which is more fundamental in nature, which is that application components shouldn't depend on the (IServiceProvider) DI Container. This is called the Service Locator anti-pattern.
This means that you shouldn't do this type of initialization inside your controllers, but instead move this to the application startup path; a.k.a. the Composition Root.
This can be done, for instance, by introducing a new abstraction:
private readonly IMessageInitializer _initializer;
public PorukeHubController(IMessageInitializer initializer)
{
__initializer = _initializer;
}
[HttpGet]
public IActionResult Get()
{
_messageInitializer.Initialize();
return Ok(new { Message = "Request Completed" });
}
In this code example the new IMessageInitializer hides the complexity of initialization of the timer, the querying of the database and the calling of the hub from the controller.
Inside your Composition Root you can now define an implementation with the original code:
public class ScheduledMessageInitializer : IMessageInitializer
{
private readonly TimerManager _timer;
private readonly IServiceProvider _provider;
public ScheduledMessageInitializer(
TimerManager timer, IServiceProvider provider)
{
_timer = timer;
_provider = provider;
}
public void Initialize()
{
var currentUserId = 1;
if (!_timer.IsTimerStarted)
_timer.PrepareTimer(() => {
using (var scope = this.provider.CreateScope())
{
var sp = scope.ServiceProvider;
var context = sp.GetRequiredService<DnevnikContext>();
var hub = sp.GetRequiredService<IHubContext<PorukeHub>>();
var unreadMessages = context.Messages
.Where(p => p.RecieverID == currentUserId && p.isRead == false)
.Count();
hub.Clients.All.SendAsync("checkForMessages", unreadMessages);
}
});
}
}
This class can be registered as follows:
// This assumes that TimerManager is a singleton as well.
services.AddSingleton<IMessageInitializer, ScheduledMessageInitializer>();
This still poses a (smaller) design issue, which is that with DI you should strive to keep the application's business logic out of the Composition Root. It should only contain the required infrastructural code for the application to execute. The querying of the database and sending it to the hub can be considered business logic; not infrastructure.
That would mean that one last refactoring is in place: you should extract that logic out of the ScheduledMessageInitializer and place it in a new application component:
public class UnreadMessageChecker
{
private readonly DnevnikContext _context;
private readonly IHubContext<PorukeHub> _hub;
public UnreadMessageChecker(DnevnikContext context, IHubContext<PorukeHub> hub)
{
_context = context;
_hub = hub;
}
public async Task CheckAsync()
{
var unreadMessages = context.Messages
.Where(p => p.RecieverID == currentUserId && p.isRead == false)
.Count();
// I noticed how you called SendAsync. You should await it, otherwise
// you'll get into the same trouble as where you started out with:
// with an ObjectDisposedExcetion.
await hub.Clients.All.SendAsync("checkForMessages", unreadMessages);
}
}
services.AddTransient<UnreadMessageChecker>();
This new component can be resolved from the ScheduledMessageInitializer, which reduces it to the following:
public class ScheduledMessageInitializer : IMessageInitializer
{
private readonly TimerManager _timer;
private readonly IServiceProvider _provider;
public ScheduledMessageInitializer(
TimerManager timer, IServiceProvider provider)
{
_timer = timer;
_provider = provider;
}
public void Initialize()
{
var currentUserId = 1;
if (!_timer.IsTimerStarted)
_timer.PrepareTimer(async () =>
{
using (var scope = this.provider.CreateScope())
{
var checker = scope.ServiceProvider
.GetRequiredService<UnreadMessageChecker>();
await checker.CheckAsync();
}
});
}
}
There might still be other issues with your code. For instance, it seems weird to me that you have a currentUserId (which is runtime data, changing on each request), while using it to initialize the timer with; unless that timer isn't a Singleton. But if the timer isn't a singleton, that would mean that would be initializing an endless number of timers, which likely isn't a good idea as well.
Another issue is that, if the TimerManager is indeed singleton, there might be a race condition while initializing. Is the TimerManager thread-safe? What would happen when it gets initialized twice simultaneously? Would that cause problems. I, unfortunately, can't answer this.
I am using ASP.NET Core, and I am adding some users to a collection via SingalR hub endpoint:
public class MatchMakingHub : Hub
{
//....
// called by client
public async Task EnlistMatchMaking(int timeControlMs)
{
Guid currentId = Guid.Parse(this.Context.User.GetSubjectId());
GetPlayerByIdQuery getPlayerByIdQuery = new GetPlayerByIdQuery(currentId);
Player currentPlayer = await requestSender.Send<Player>(getPlayerByIdQuery);
var waitingPlayer = new WaitingPlayer(currentPlayer, timeControlMs);
this.matchMakePool.Add(waitingPlayer);
}
}
matchMakePool being a singleton collection.
Later, I have an ASP.NET Core background service fetch the users from the collection, and notify them about being fetched:
public class MatchMakingBackgroundService : BackgroundService
{
private readonly MatchMakePoolSingleton matchMakePoolSingleton;
private readonly IServiceProvider serviceProvider;
private const int RefreshTimeMs = 1000;
public MatchMakingBackgroundService(MatchMakePoolSingleton matchMakePoolSingleton, IServiceProvider serviceProvider)
{
this.matchMakePoolSingleton = matchMakePoolSingleton;
this.serviceProvider = serviceProvider;
}
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
while(!stoppingToken.IsCancellationRequested)
{
var result = matchMakePoolSingleton.RefreshMatches();
var tasks = new List<Task>();
foreach(var match in result)
{
tasks.Add(StartGameAsync(match));
}
await Task.WhenAll(tasks);
await Task.Delay(RefreshTimeMs, stoppingToken);
}
}
private async Task StartGameAsync(MatchMakeResult match)
{
using var scope = serviceProvider.CreateScope();
var sender = scope.ServiceProvider.GetRequiredService<ISender>();
var hubContext = serviceProvider.GetRequiredService<IHubContext<MatchMakingHub>>();
CreateNewGameCommand newGameCommand = new CreateNewGameCommand(match.WhitePlayer.Id, match.BlackPlayer.Id, TimeSpan.FromMilliseconds(match.TimeControlMs));
Guid gameGuid = await sender.Send(newGameCommand);
await hubContext.Clients.User(match.WhitePlayer.Id.ToString()).SendAsync("NotifyGameFound", gameGuid);
await hubContext.Clients.User(match.BlackPlayer.Id.ToString()).SendAsync("NotifyGameFound", gameGuid);
}
}
My problem is that NotifyGameFound is not being called in the client side. When I notified them straight from the hub itself it was received, but for some reason it doesn't when I call it through the provided IHubContext<MatchMakingHub>. I suspect that this is because it runs on another thread.
Here is the client code:
// in blazor
protected override async Task OnInitializedAsync()
{
var tokenResult = await TokenProvider.RequestAccessToken();
if(tokenResult.TryGetToken(out var token))
{
hubConnection
= new HubConnectionBuilder().WithUrl(NavigationManager.ToAbsoluteUri("/hubs/MatchMaker"), options =>
{
options.AccessTokenProvider = () => Task.FromResult(token.Value);
}).Build();
await hubConnection.StartAsync();
hubConnection.On<Guid>("NotifyGameFound", id =>
{
//do stuff
});
await MatchMakeRequast();
}
}
async Task MatchMakeRequast() =>
await hubConnection.SendAsync("EnlistMatchMaking", Secs * 1000);
I use injection to achieve this.
In my servers Startup.cs ConfigureServices mothod I have:
services.AddScoped<INotificationsBroker, NotificationsBroker>();
In your case I am assuming you are injecting MatchMakingBackgroundService
Something like:
services.AddScoped<MatchMakingBackgroundService>();
In my NotificationsBroker constructor I inject the context:
private readonly IHubContext<NotificationsHub> hub;
public NotificationsBroker(IHubContext<NotificationsHub> hub)
=> this.hub = hub;
I then inject the broker into any service I require it and the service can call the hubs methods I expose via the interface.
You don't have to go the extra step, I do this for testing, you could inject the context directly into your MatchMakingBackgroundService.
I'm trying to write some test with XUnit, specifically I'd like to have a test that ensures that when a certain exception is thrown it gets remapped into a meaningful error code.
I already set up the Global error handling middleware and it works correctly.
Here there is some example code of how my solution works:
My controller with a post endpoint that can return 200 or 404
//Controller
[HttpPost]
[ProducesResponseType(200)]
[ProducesResponseType(404)]
public async Task<StatusCodeResult> Create([FromBody] Request request) {
//Process request
handler.Handle(request);
return Ok();
}
The Middleware for the Global error handling that remaps exceptions into Error codes
//StartUp Middleware
app.UseExceptionHandler(builder => {
builder.Run(handler: async context => {
IExceptionHandlerFeature error = context.Features.Get<IExceptionHandlerFeature>();
if (error != null) {
int statusCode = (int)GetStatusCodeForException(error.Error);
context.Response.StatusCode = statusCode;
context.Response.ContentType = "application/json";
await context.Response.WriteAsync(new ErrorDetails { StatusCode = statusCode, Message = error.Error.Message }.ToString());
}
});
});
And then my test in where I arrange some mocks, instantiate the controller and call the Create method
//UnitTest
[Fact]
public async Task Test()
{
//Arrange
var mockHandler = new Mock<IHandler>();
mockHandler.Setup(handler => handler.Handle(It.IsAny<Request>())).Throws(new CustomException(It.IsAny<string>()));
MyController myController = new MyController();
//Act
var statusCodeResult = await myController.Create(request);
//Assert
StatusCodeResult result = Assert.IsType<NotFoundResult>(statusCodeResult);
}
Here I want to ensure that the CustomException is remapped into a 404 status code. How do I do it? Any help is appreciated.
In your test the middleware is not available. You need to spin up a hosting environment to do that, the package Microsoft.AspNetCore.TestHost provides you with one that you can use for testing:
[Fact]
public async Task Test1()
{
using var host = new TestServer(Program.CreateHostBuilder(null));
var client = host.CreateClient();
var requestMessage = new HttpRequestMessage(HttpMethod.Get, "/api/controller");
var result = await client.SendAsync(requestMessage);
var status = result.StatusCode;
// TODO: assertions
}
Now when you call your API in a way an exception is thrown, the middleware should be executed and covered.
You can use the WebApplicationFactory class from the Microsoft.AspNetCore.Mvc.Testing nuget package. This bootstraps your application in-memory to allow for end to end functional tests. Rather than calling individual action methods you can make calls via HttpClient to ensure all middleware etc is called.
You use the Startup class that you have already defined in your main entry project and can add mock/fake objects to the IoC container as required. This allows you to verify/setup any dependencies.
You can then inject this as an IClassFixture. Once injected calling .CreateClient() on the instance will return an HttpClient, through which you can make requests.
Here is an example implementation:
// app factory instance
public class TestAppFactory : WebApplicationFactory<Startup>
{
// mock for setup/verify
public Mock<IHandler> MockHandler { get; } = new Mock<IHandler>();
protected override void ConfigureWebHost(IWebHostBuilder builder)
{
builder.ConfigureTestServices(services =>
{
services.AddSingleton(MockHandler);
});
}
}
public class MyTestClass : IClassFixture<TestAppFactory>
{
private readonly TestAppFactory _factory;
private readonly HttpClient _client;
private readonly Mock<IHandler> _mockHandler;
public MyTestClass(TestAppFactory factory)
{
_factory = factory;
_client = factory.CreateClient();
_mockHandler = factory.MockHandler;
}
[Fact]
public async Task Test()
{
// make calls via _client
}
}
In my test I have to have access to database context (real database, it's not in memory). Because after post I want to ensure that record has been saved in database.
This is what I have, it works perfectly but I don't have enough experiance to be sure that this is correct approach for getting context.
public class ValuesControllerTests : TestHostFixture
{
[Theory]
[InlineData("/values/sample")]
public async Task Sample_WhenCreatingSampleData_ThenIsAddedToDatabase(string url)
{
// given
var command = new AddSampleCommand { Name = "TestRecord" };
// when
var httpResponse = await Client.PostAsJsonAsync(url, command);
// then
httpResponse.EnsureSuccessStatusCode();
using (var dbContext = GetContext())
{
dbContext.Samples.FirstOrDefault(x => x.Name == "TestRecord").ShouldNotBeNull();
}
}
}
public abstract class TestHostFixture : IClassFixture<CustomWebApplicationFactory<Startup>>
{
protected readonly HttpClient Client;
private readonly CustomWebApplicationFactory<Startup> _factory;
protected TestHostFixture()
{
_factory = new CustomWebApplicationFactory<Startup>();
Client = _factory.CreateClient();
}
protected MyContext GetContext()
{
return _factory.Server.Host.Services.CreateScope().ServiceProvider.GetService<MyContext>();
}
}
So just to sum up - in test I'm getting Context by:
using (var dbContext = GetContext())
{
dbContext.Samples.FirstOrDefault(x => x.Name == "TestRecord").ShouldNotBeNull();
}
And GetContext method:
protected MyContext GetContext()
{
return _factory.Server.Host.Services.CreateScope().ServiceProvider.GetService<MyContext>();
}
Please let me know if this is fine, or maybe I should refactor it somehow because of some potential issue in future.
Generally, this is fine: You definitely should make use of the host’s DI container to retrieve the database context. Since the database context is scoped, it is also correct to create a new service scope to retrieve the context.
However, since the DI container is what usually manages the lifetime of the objects it creates, you should leave the disposal of the database context up to the DI container, and instead dispose of the service scope.
While it will probably not matter much in a unit test, which will be cleaned up quickly anyway (and since you are disposing the context you also won’t leak database connections), it’s still a better style and safer in the long run.
So dispose the service scope instead:
// …
httpResponse.EnsureSuccessStatusCode();
using (var scope = Host.Services.CreateScope())
{
var dbContext = scope.ServiceProvider.GetService<MyContext>();
var item = dbContext.Samples.FirstOrDefault(x => x.Name == "TestRecord");
item.ShouldNotBeNull();
}
Improving on the implementation provided by poke, you could consider creating a delegate to handle the proper disposal of the created scope.
For example
protected void GetContext(Action<MyContext> test) {
using(var scope = _factory.Server.Host.Services.CreateScope()) {
var context = scope.ServiceProvider.GetRequiredService<MyContext>();
test(context);
}
}
When exercising your test simple call the delegate
[Theory]
[InlineData("/values/sample")]
public async Task Sample_WhenCreatingSampleData_ThenIsAddedToDatabase(string url) {
// given
var command = new AddSampleCommand { Name = "TestRecord" };
// when
var httpResponse = await Client.PostAsJsonAsync(url, command);
// then
httpResponse.EnsureSuccessStatusCode();
GetContext(dbContext => {
var item = dbContext.Samples.FirstOrDefault(x => x.Name == "TestRecord");
item.ShouldNotBeNull();
});
}
I have a WebApi controller that has services injected by AutoFac in the OWIN Startup class
builder.Register(c => new MyEntities()).InstancePerRequest();
I have also tried
builder.Register(c => new MyEntities()).InstancePerLifetimeScope();
In a controller action I call a service method to create a new record, pass the id created to an external api through HttpClient to get some more data, then update the new record with some return data.
[HttpPost, Route("")]
public async Task<IHttpActionResult> MyControllerAction(MyModel model)
{
var id = await _MyService.CreateNewThing(model.SomeId);
var externalData = await CallExternalApiThroughHttpClient(id);
await _MyService.UpdateNewThing(id, externalData);
return Ok();
}
service code
public class MyService : IMyService
{
private MyEntities _context;
public MyService(MyEntities context)
{
_context = context;
}
public async Task<int> CreateNewThing(int someId)
{
var thing = new Thing
{
SomeId = someId
};
_context.Things.Add(thing);
await _context.SaveChangesAsync();
return thing.Id;
}
public async Task UpdateNewThing(int id, string externalDataField)
{
var thing = await _context.Things.SingleOrDefaultAsync(o => o.Id == id);
if (thing == null)
{
throw new ServiceNotFoundException("Thing " + transactionId + " not found");
}
thing.ExternalDataField= externalDataField;
await _context.SaveChangesAsync();
}
}
But I get an InvalidOperationException in UpdateNewThing var thing = await _context.Things.SingleOrDefaultAsync(o => o.Id == id);
System.InvalidOperationException: The connection was not closed. The connection's current state is connecting.
It seems like I have to give up either injecting the context, async/await or use something like a contextfactory; unless anyone can spot something simple I have missed that would let me continue with this design.
Your code looks fine in a single-threaded context. However, DbContext is not thread safe, and I suspect what is happening is you're executing CreateNewThing() on one thread, and the task scheduler is in this case executing UpdateNewThing() on a different thread.
Either way, a better metaphor is to use a context factory, which you inject into your IMyService in this case, and then for every IMyService method you create a new MyEntities context in a using() block.
DbContext's are cheap to create and this is how they are intended to be used; long-lived contexts are almost always incorrect usage.
Edit 1 - example context factory as requested. I tend to implement a generic factory that can create multiple contexts, but that's probably moving outside the scope of this question.
public interface IMyEntitiesFactory
{
MyEntities Create();
}
public class MyEntitiesFactory : IMyEntitiesFactory
{
MyEntities IMyEntitiesFactory.Create()
{
return new MyEntities();
}
}
// For use with unit tests; e.g. pass a mock object to the constructor.
public class TestMyEntitiesFactory : IMyEntitiesFactory
{
private readonly MyEntities _value;
public TestMyEntitiesFactory(MyEntities value)
{
_value = value;
}
MyEntities IMyEntitiesFactory.Create()
{
return _value;
}
}