【问题标题】:Entity Framework adding object to collection results in unexpected behaviour实体框架将对象添加到集合会导致意外行为
【发布时间】:2016-03-02 11:50:36
【问题描述】:

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

现在我只是尝试通过curAcc.Addition.Contacts.Add(correspondingAccount) 向 AccountAdditions 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 提供的解决方案没有按预期工作。 我尝试从 Notification 和 Contact 集合中删除 virtual 关键字。 截图现在是这样的:

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 方法之后,context.Accounts 中的帐户对象指向了错误的 AccountAddition。当我想将账户f2(AdditionId 2)添加到账户f1(AdditionId 1)时,f2的additionId指向1。

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

{"保存不为其关系公开外键属性的实体时发生错误。EntityEntries 属性将返回 null,因为无法将单个实体标识为异常源。可以在保存时处理异常通过在实体类型中公开外键属性更容易。有关详细信息,请参阅 InnerException。"}

内部异常是

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

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

这令人沮丧:(

【问题讨论】:

  • 你有全局 EF 上下文吗?
  • 您好 Lance,我的上下文是在基本控制器类中给出的。公共抽象类 BaseController : Controller { protected MyDbContext context = new MyDbContext();

标签: c# asp.net-mvc entity-framework icollection


【解决方案1】:

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

要解决您的问题,您应该先检查帐户是否已存在于 acc.Contacts 中,然后再使用 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();
}

【讨论】:

  • 这会引发 OptimisticConcurrencyException。内部:{“存储更新、插入或删除语句影响了意外数量的行 (0)。实体可能已在加载实体后被修改或删除。有关理解和处理乐观并发异常的信息,请参阅 go.microsoft.com/fwlink/?LinkId=472540。”} .我会检查一下,但我很感激更多的帮助。 :) 到目前为止,谢谢,现在我必须了解为什么 EF 会这样做
  • 该异常可能是因为 curAcc 的 ID 属性。因此,您的插入应该是没有 ID 属性的 curAcc 副本。我已经更新了答案以反映这一点
  • 另外,如果没有 virtual 关键字,EF 无法对相关属性执行延迟加载,所以我认为您需要将它们添加回来。为了使延迟加载工作,EF 子类化您的类并实现这些虚拟方法。
  • 当我使用它时,会在 Accounts 表中创建一个全新的条目,并且所有 3 个帐户都指向同一个 AdditionID...
  • 那是因为它们之间的关系是一对多的。这意味着一个帐户只能属于一个 AccountAddition。您可能需要在 AccountAddition 和 Accounts 之间引入一个“联系人”表。像 public class Contact { public int ContactID { get;放; } 公共 int AccountID { 获取;放; } 公共 int AccountAdditionID { 获取;放; } }
猜你喜欢
  • 2015-12-25
  • 1970-01-01
  • 1970-01-01
  • 2012-11-17
  • 1970-01-01
  • 2012-10-28
  • 2020-04-22
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多