I have an entity that has a Many-to-Many self-relationship. As an example consider this entity:
public class User
{
public int ID { get; set; }
public string UserName { get; set; }
public virtual ICollection<User> Friends { get; set; }
}
Here is how I configure the mapping:
HasMany(t => t.Friends).WithMany()
.Map(m => {
m.MapLeftKey("UserID");
m.MapRightKey("FriendID");
m.ToTable("UserFriends");
});
As this relationship is now managed by the EF, I don't really have access to the UserFriends
DbSet
in my code and cannot handle the concurrent access to it.
In order for this composition to handle concurrent access(add/remove), do I need to handle the Many-to-Many relationship myself and then add a [Timestamp]
column or is there a way to tell EF to handle this concurrently itself? Like a configuration in the model builder.
Edit: I'm using EF 6 and currently if there is a concurrent operation on the entity(e.g trying to remove a friend that currently doesn't exits on the database) I get the following error message and an DbUpdateException
:
An error occurred while saving entities that do not expose foreign key properties for their relationships. The EntityEntries property will return null because a single entity cannot be identified as the source of the exception. Handling of exceptions while saving can be made easier by exposing foreign key properties in your entity types. See the InnerException for details.
The many-to-may relationship can be achieved using HasMany and WithMany methods. The default conventions for many-to-many relationships creates a joining table with the default naming conventions. You can customize a joining table name and column names using Fluent API.
If you do want to implement this approach to concurrency, you have to mark all non-primary-key properties in the entity you want to track concurrency for by adding the ConcurrencyCheck attribute to them. That change enables the Entity Framework to include all columns in the SQL WHERE clause of UPDATE statements.
Custom resolution of optimistic concurrency exceptions SaveChanges(); } catch (DbUpdateConcurrencyException ex) { saveFailed = true; // Get the current entity values and the values in the database var entry = ex. Entries. Single(); var currentValues = entry. CurrentValues; var databaseValues = entry.
Optimistic concurrency does not apply here.
A junction table is never updated. Its records are either added or deleted. This means that there are no CRUD operations that need a rowversion.
So in fact, concurrency is fairly easy:
So it boils down to handling exceptions and translating them to comprehensible user feedback. All these situations must also be dealt with in situations where updates (and optimistic concurrency) do play a role.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With