John Mitchell John Mitchell - 1 month ago 25
ASP.NET (C#) Question

Secondary Db Context in ASP.NET CORE EF

I have a service that sends notifications, it requires a db connection to lookup subscriptions. I also have a controller (and may more) that does some logic, and sends notifications.

The problem with this is, because of DI it uses the same instance of the

DbContext
so I get a error thrown for re-using a
DataReader
in the same context (understandable).

I would really love to do this without enabling the MARS flag in
DbConnectionString
. Given that the controllers cannot use
.ToList()
or no tracking and the 'inner'
NotificationService
needs to lookup the database - is this even possible?

public class NotificationSystem
{
private readonly DbContext context;
public NotificationSystem(DbContext context) { this.context = context;}

public void SendNotification(string username){
var subscriptions = context.subscriptions.where(u => u.username == username);
// Do some notification stuff
}
}


And a simple controller

public class SendRemindersController : Controller
{
private readonly DbContext _context;
private readonly NotificationSystem _notificationSystem;

public SendRemindersController(DbContext context, NotificationSystem notificationSystem)
{
this._context = context;
this._notificationSystem = notificationSystem;
}

[HttpGet]
public async Task<IActionResult> Get()
{
var reminders = _context.Reminders.Where(r => r.Sent == false && r.RemindAt < DateTime.UtcNow);

foreach (var reminder in reminders)
{
await _notificationSystem.SendNotificationToUser(reminder.UserId);
reminder.Sent = true;
}

await _context.SaveChangesAsync();
return Ok();
}
}


And
startup.cs
(yes I know I haven't used an interface, that will get refactored later).

services.AddDbContext<DbContext>(options => options.UseSqlServer(connection));
services.AddTransient<NotificationSystem, NotificationSystem>();


Update



This question is flawed since i was under the false impression that .ToList/.ToArray also detached the entities from the context. In-fact these do not detach and only execute the query.

Answer

Its because you are using the same DbContext to execute multiple simultaneous transactions. If you add .ToListAsync() to this line of code like this

var reminders = await _context.Reminders
  .Where(r => r.Sent == false && r.RemindAt < DateTime.UtcNow)
  .ToListAsync();

It will retrieve all the reminders immediately and then the code inside the loop (after this statement) can use the DbContext without the DbContext throwing an exception because an active result set is still being iterated over.