I am working on an existing multi-user EF6 / SQL ASP.NET MVC 5 application (database first) that is having concurrency issues I need to address. The database contains a one-to-many relationship that is represented by a bridge table between the 'one' and 'many' tables.
[EDIT] It has been pointed out that a Bridge is not required in a 1-to-M structure. I'm aware of that. Please see the comment & response below.
One table
OneTableId (one relationship w BridgeTable)
.....
RowVersion (not yet created)
Many table
ManyTableId (many relationship w BridgeTable)
.....
RowVersion (not yet created)
Bridge table
BridgeTableRowId
OneTableId (relationship w OneTable)
ManyTableId (relationship w ManyTable)
The bridging rows are established when a One entity and an arbitrary number of Many entities are passed to a long running process which is executed using a QueueBackgroundWorkItem on a SignalR Hub. The process creates a number of PDFs and Emails them.
The creation of the bridge records is currently the only database impact during the long Running Process, which is initiated like this:
using (Entities db = new Entities())
{
var oneTableEntity = db.oneTable.SingleOrDefault(s => s.OneTableId == RequestedOneId);
var manyTableEntities = db.manyTable.Where(s => RequestedIdArray.Contains(s.ManyTableId).ToList();
}
HostingEnvironment.QueueBackgroundWorkItem(ct => _DoLongRunningProcess(OneTableEntity, ManyTableEntities).ContinueWith(x =>
{
// cleanup and SignalR notifications
}
The LRP (handled in a separate class) instantiates the separate Data Contexts and Data Streams(memory and file) it needs to complete its chores.
The system is currently not transacted but I'm adding a Transaction that is only Completed if the PDFs are successfully created and emailed.
I need to handle concurrency such that if a user selects and submits a OneTable or ManyTable entity that is already being handled in the Long Running Process, we'll get an exception to be handled.
Questions:
Could I simply place a unique constraint over the
OneTableId-ManyTableId
columns in the Bridge table and just catch that exception OR should I implement a Row Versioning system? I'm leery about just letting the DB complain based on a Unique Index Constraint, but not clear about the impact of the LRP with its separate Data Contexts.If I transact the LRP so that Bridge Table row commitment is dependent upon the emails being sent, what level of isolation should I use, given the concurrency issue?
If I use the Row Versioning approach:
- Should the assignment of BridgeTable records trigger EF6 to increment RowVersions in OneTable and ManyTable or do I need to somehow manually check entity RowVersions on entry to (inside) the LRP?
- Do I need to wrap the entire LRP in the outer Data Context (using()) in order for EF6 to handle this correctly?
Please note that the overall design for this app is already in place and I need to minimize changes while addressing the Concurrency issue.
Hope I've been clear enough, happy to answer any questions that would help :-)
Thanks for any guidance.