实体框架将对象添加到集合会导致意外行为

本文关键字:意外 集合 框架 对象 添加 实体 | 更新日期: 2023-09-27 18:37:10

我目前正在使用实体框架6开发一个新的 ASP.NET MVC应用程序。我有 2 个类一起工作:帐户和帐户添加。帐户表示基本用户帐户,帐户添加包含例如联系人,通知或上次登录时间的ICollect。

现在,我只是尝试通过curAcc.Addition.Contacts.Add(correspondingAccount)将新帐户添加到帐户添加ICollection Contacts属性中,其中correspondingAccount是要添加的帐户对象(两个帐户都在数据库中给出)。

问题是,当我将correspondingAccount添加到当前帐户的联系人时,引用correspondingAccount.Addition突然指向当前帐户的AccountAddition

问题部分:

...
Account curAcc = context.Accounts.FirstOrDefault(p => p.Alias == currentAlias);
        if(ModelState.IsValid)
        {
            var correspondingAccount = context.Accounts.FirstOrDefault(p => p.Alias == addAlias);
            if(correspondingAccount != null)
            {
                curAcc.Addition.Contacts.Add(correspondingAccount);
                context.SaveChanges();
                ...

代码第一类:

public class Account
{
    public Account()
    {
        IsEnabled = true;
    }
    [Key]
    public int ID { get; set; }
    public string Alias { get; set; }
    [DataType(DataType.Password)]
    public string Password { get; set; }
    public byte[] Salt { get; set; }
    [ForeignKey("Config")]
    public int ConfigID { get; set; }
    public virtual CryptoConfig Config { get; set; }
    [ForeignKey("Addition")]
    public int AdditionID { get; set; }
    public virtual AccountAddition Addition { get; set; }
    public virtual ICollection<AccountRoleLink> Roles { get; set; }
    public bool IsEnabled { get; set; }
}
public class AccountAddition
{
    public AccountAddition()
    {
        CreatedOn = DateTime.Now;
        Contacts = new List<Account>();
        Notifications = new List<Notification>();
    }
    [Key]
    public int ID { get; set; }
    public virtual ICollection<Account> Contacts { get; set; }
    public virtual ICollection<Notification> Notifications { get; set; }
    public string ContactEmail { get; set; }
    public Nullable<DateTime> LastLogin { get; set; }
    public Nullable<DateTime> LastFailedLogin { get; set; }
    public Nullable<DateTime> CreatedOn { get; set; }
}

更多例子:

Account curAcc = // Find current account (current context)
Account correspondingAcc = // Get account to add (same context)
curAcc.Addition is Addition with id 1
correspondingAcc.Addition is Addition with id 2
// correspondingAcc gets added via curAcc.Addition.Contacts.add(cor...)
// context saves changes
curAcc.Addition is Addition with id 2 afterwards
correspondingAcc.Addition is Addition with id 2 afterwards

我尝试在EF教程中进行谷歌搜索和搜索,但没有找到解决方案。怎么了?

感谢您抽出宝贵时间

更新:

好的,显然Ody提供的解决方案没有按预期工作。我尝试从通知和联系人集合中删除虚拟关键字。截图现在看起来像这样:

Account curAcc = GetCurrentAccount();
        if(ModelState.IsValid)
        {
            var correspondingAccount = context.Accounts.FirstOrDefault(p => p.Alias == addAlias);
            if(correspondingAccount != null)
            {
                curAcc.Addition.Contacts.Add(correspondingAccount);
                var existingCorrespondingAccount = curAcc.Addition
                                         .Contacts.Where(a => a.Alias == addAlias)
                                         .FirstOrDefault();
                if (existingCorrespondingAccount != null)
                {
                    context.Accounts.Add(curAcc);
                }

                context.SaveChanges();

调用 Add 方法后,上下文中的帐户对象。帐户指向错误的帐户添加。当我想将帐户 f2 (AdditionId 2) 添加到帐户 f1 (AdditionId 1) 时,f2 的 additionId 指向 1。

当我调用 SaveChanges 时,会抛出 System.Data.Entity.Infrastructure.DbUpdateException。

{"保存未公开其关系的外键属性的实体时出错。属性将返回 null,因为无法将单个实体标识为异常的来源。通过在实体类型中公开外键属性,可以更轻松地在保存时处理异常。有关详细信息,请参阅 InnerException。

内部例外是

{"存储更新、插入或删除语句影响了意外的行数 (0)。自加载实体以来,实体可能已被修改或删除。有关了解和处理乐观并发异常的信息,请参阅 http://go.microsoft.com/fwlink/?LinkId=472540。

我点击了链接并尝试使用OptimisticConcurrencyException和DbUpdateConcurrencyException捕获它,但是ex是DbUpdateException,他们没有捕获。

这是令人沮丧的:(

实体框架将对象添加到集合会导致意外行为

问题是,每当将实体添加到相关属性集合并调用SaveChanges()时,它都不会检查 id 是否已存在。无论如何,它会创建一个新的。这就是 EF 的工作方式。

要解决您的问题,您应该检查帐户中是否已存在该帐户。使用ID手动添加之前的联系。

像这样的东西

Account curAcc = context.Accounts.FirstOrDefault(p => p.Alias == currentAlias);
var correspondingAccount = context.Accounts.FirstOrDefault(p => p.Alias == addAlias);
if (correspondingAccount != null)
{
    curAcc.Addition.Contacts.Add(correspondingAccount);
    var existingCorrespondingAccount = curAcc.Addition
                                             .Contacts.Where(a => a.Alias == addAlias)
                                             .FirstOrDefault();
    if(existingCorrespondingAccount != null)
    {
          context.Accounts.Add(new Account
          {
               AdditionID = curAcc.AdditionID,
               Alias = curAcc.Alias,
               ConfigID = curAcc.ConfigID,
               IsEnabled = curAcc.IsEnabled,
               Password = curAcc.Password,
               Salt = curAcc.Salt
           });
    }
    context.SaveChanges();
}