在使用自我跟踪实体和实体框架 4 时,我正在尝试建立使用辅助键的良好做法,但似乎有很多陷阱。
假设我有一个警报实体,它可以具有用户的 ConfirmingUser 导航属性。用户有一个 Id 作为主键,一个“登录”作为辅助键。在我的系统中的某个时刻,我将一个新的用户实体分配给警报的 ConfirmingUser。那时我只知道用户的登录名(辅助密钥),可以说它是一个 n 层架构,此时查找 Id 是不切实际的。警报随后被传输到将尝试存储警报的持久层。现在是棘手的部分。我需要确定 ConfirmingUser 是否已存在于数据库中并采取适当的措施。
这就是我现在的做法:
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();
}
这种模式对我来说看起来相当丑陋和低效。有没有更好的方法来处理带有“外部”键的实体?
上述代码的一个有趣缺陷是无法用具有相同登录名的新用户替换 ConfirmingUser。即,当客户端应用程序想要将 ConfirmingUser 分配给警报时,它必须检查 ConfirmingUser 是否已经分配给具有相同登录属性的用户。原因是自跟踪实体的 ApplyChanges 将尝试将原始用户和新用户都复制到 EF 上下文中,但 EF 不允许原始值和新值是同一个实体(为什么它无法处理这对我来说没有明确的意义)。