I am trying to establish a good practice for working with secondary keys when using self tracking entities and entity framework 4, but there seems to be a lot of pitfalls.
Lets say I have an Alarm entity, that can have ConfirmingUser navigational property to a User. The User has an Id as primary key and a "Login" as secondary key. At some point in my system I assign a new User entity to the ConfirmingUser of an Alarm. At that point I only know the login (secondary key) of the user and lets say it's an n-tier architecture, where it's impractical to look up the Id at that point. The Alarm is later transported to a persistence layer that will attempt to store the Alarm. Now comes the tricky part. I need to determine if the ConfirmingUser already exists in the database and take appropriate action.
This is how I do it now:
using (var context = new PantoTestEntities())
{
if (alarm.ConfirmingUser != null && alarm.ConfirmingUser.ChangeTracker.State == ObjectState.Added)
{
var userIdQuery = from user in context.Users
where user.Login == alarm.ConfirmingUser.Login
select user.Id;
if (userIdQuery.Any())
{
// Disable cache or ApplyChanges will throw an exception, because the user already exists.
context.Alarms.MergeOption = MergeOption.NoTracking;
alarm.ConfirmingUser.Id = userIdQuery.First();
alarm.ConfirmingUser.MarkAsModified();
}
}
context.Alarms.ApplyChanges(alarm);
context.SaveChanges();
// Accept changes in the full entity graph
alarm.AcceptAllChanges();
}
This pattern looks rather ugly and inefficient to me. Are there better ways to handle entities with "external" keys?
An interesting pitfall with the above code is that it is not possible to replace the ConfirmingUser with a new User with the same Login. I.e. when the client application wants to assign a ConfirmingUser to an Alarm it has to check if ConfirmingUser has already been assigned to a User with the same Login property. The reason for that is that ApplyChanges of the self tracking entities will try to copy both the original user and the new user into the EF context, but EF doesn't allow that both the original and the new value are the same entity (why it can't handle that doesn't make clear sense to me).