John Mitchell John Mitchell - 10 months ago 74
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

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

I would really love to do this without enabling the MARS flag in
. Given that the controllers cannot use
or no tracking and the 'inner'
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;

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();

(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>();


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 Source

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)

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.