【问题标题】:An error occurred while saving entities that do not expose foreign key properties for their relationships保存不为其关系公开外键属性的实体时发生错误
【发布时间】:2011-12-17 19:29:26
【问题描述】:

我有一个简单的代码在Entity Framework 4.1 代码优先:

PasmISOContext db = new PasmISOContext();
var user = new User();
user.CreationDate = DateTime.Now;
user.LastActivityDate = DateTime.Now;
user.LastLoginDate = DateTime.Now;
db.Users.Add(user);

db.SaveChanges();
user.Avatar = new Avatar() { Link = new Uri("http://myUrl/%2E%2E/%2E%2E") };
db.SaveChanges();


db.Users.Add(new User() { Avatar = new Avatar() { Link = new Uri("http://myUrl/%2E%2E/%2E%2E") } });
db.SaveChanges();

问题是我得到一个错误

保存不公开外键的实体时出错 他们关系的属性。 EntityEntries 属性将 返回 null,因为无法将单个实体标识为源 的例外。可以进行保存时的异常处理 通过在实体类型中公开外键属性更容易。看 InnerException 了解详情。

db.Users.Add(new User() { Avatar = new Avatar() { Link = new Uri("http://myUrl/%2E%2E/%2E%2E") } });
db.SaveChanges();

我不明白为什么类似的操作会起作用。我的模型有问题,还是 ef-code-first 有问题?

public class Avatar
{
    [Key]
    public int Id { get; set; }

    [Required]
    public string LinkInString { get; set; }

    [NotMapped]
    public Uri Link
    {
        get { return new Uri(LinkInString); }
        set { LinkInString = value.AbsoluteUri; }
    }
}

public class User
{
    [Key]
    public int Id { get; set; }
    public string UserName { get; set; }
    public string Email { get; set; }
    public string Password { get; set; }
    public Avatar Avatar { get; set; }
    public virtual ICollection<Question> Questions { get; set; }
    public virtual ICollection<Achievement> Achievements { get; set; }

    public DateTime CreationDate { get; set; }
    public DateTime LastLoginDate { get; set; }
    public DateTime LastActivityDate { get; set; }
}

【问题讨论】:

  • 我无法重现该错误。你能检查第一个代码sn-p吗?这正是你正在做的吗? (至少一个db.Users.Add(user) 似乎丢失了,否则2 个SaveChanges 没有意义。)你有任何额外的Fluent API 映射吗?
  • 谢谢斯劳马。我已经编辑了我的代码。我没有任何 Fluent API。第一个和第二个 SaveChanges 将行添加到 Avatars 和 Users 表。我不知道这是否重要,但在表中我有 Avatar_Id 列。
  • Avatar_Id in User table 是 Avatar 导航属性的外键列,没关系。您是手动创建数据库表还是让 EF 创建数据库?目前我不知道你为什么会收到这个错误。您可以尝试遵循异常中给出的建议并将外键属性添加到您的 User 类:public int? AvatarId { get; set; } 并看看会发生什么。也许至少这个例外会揭示问题的更多细节。
  • 我让 EF 创建数据库。正如你所说,我已经添加了这个属性,现在它工作正常 - 现在我有 AvatarId 而不是 Avatar_Id 并且它保持正确的头像。这很奇怪,但感谢您帮助我。
  • 您可以将其发布为 FK 属性已解决问题的答案,然后接受您的答案,以完成此问题。我不明白为什么它解决了你的问题,对我来说它在没有 FK 属性的情况下也可以工作。

标签: c# entity-framework ef-code-first entity-framework-4.1


【解决方案1】:

对于那些在正确定义所有键时仍会出现此错误的人,请查看您的实体并确保您没有留下一个空值的日期时间字段。

【讨论】:

  • 如果您查看内部异常,则会出现“System.Data.SqlClient.SqlException”类型的异常,并显示消息:“将 datetime2 数据类型转换为 datetime 数据类型导致超出范围的值。”这是因为将空 DateTime 值插入到不允许空值的列中。见stackoverflow.com/questions/1331779/…
  • 我的内部异常没有与 datetime2 转换相关的任何内容,但我将 datetime 设置为 null。 DB 列允许空值,所以我认为这不是问题,但事实证明它是。即使您认为这不是您的问题,也可以试试!
  • 你好@SarangK你能告诉你如何解决这个问题
  • 谢谢你,它帮了我很多时间!
  • @MohamedElgarnaoui 我检查了所有要在 'context.SaveChanges();' 之前初始化的 DateTime 字段
【解决方案2】:

可以出于任何原因抛出此错误消息。 'InnerException' 属性(或其 InnerException 或该 InnerException 等)包含问题的实际主要原因。

了解问题发生的位置当然很有用——工作单元中的哪个对象导致了问题?异常消息通常会在“EntityEntries”属性中告诉您,但在这种情况下,由于某种原因,无法做到这一点。这种诊断复杂性 - 'EntityEntries' 属性为空 - 显然是因为某些实体'不为它们的关系公开外键属性。'

即使 OP 因未能为 User 的第二个实例初始化 DateTimes 而收到错误,他们也会得到诊断复杂性 - 'EntityEntries' 为空,以及令人困惑的顶级消息......因为他们的实体之一不会“公开外键属性”。要解决这个问题,Avatar 应该定义一个 public virtual ICollection&lt;User&gt; Users { get; set; } 属性。

【讨论】:

    【解决方案3】:

    通过添加 FK 属性解决了该问题。

    【讨论】:

    • 我偶然发现了这个答案。我在数据库初始化(dropcreatedb)期间收到此错误。我已经有一个外键,但让它可以为空解决了我的问题
    【解决方案4】:

    在我的情况下,以下情况给了我同样的例外:

    想象一个代码优先的 EF 模型,其中您有一个 Garage 实体,其中包含一组 Car 实体。我需要从车库里移走一辆车,所以我最终得到了如下代码:

    garageEntity.Cars.Remove(carEntity);
    

    相反,它应该是这样的:

    context.Cars.Remove(carEntity);
    

    【讨论】:

    • 假设汽车必须有车库,是的。因为否则您会尝试在 Car 对象中为 GarageId 传递 null。
    【解决方案5】:

    仅适用于可能有类似问题的其他人。我有同样的错误,但出于不同的原因。在其中一个子对象中,我将 [Key] 定义为不同保存的相同值。我犯了一个愚蠢的错误,但错误消息不会立即将您引向问题。

    【讨论】:

      【解决方案6】:

      在我的情况下,抛出异常是因为 EF 错误地创建了迁移。 它错过了在第二张桌子上设置 identity: true。所以进入创建相关表的迁移并检查它是否错过了添加身份。

      CreateTable(
          "dbo.LogEmailAddressStats",
          c => new
              {
                  Id = c.Int(nullable: false, identity: true),
                  EmailAddress = c.String(),
              })
          .PrimaryKey(t => t.Id);
      
      CreateTable(
          "dbo.LogEmailAddressStatsFails",
          c => new
              {
                  Id = c.Int(nullable: false), // EF missed to set identity: true!!
                  Timestamp = c.DateTime(nullable: false),
              })
          .PrimaryKey(t => t.Id)
          .ForeignKey("dbo.LogEmailAddressStats", t => t.Id)
          .Index(t => t.Id);
      

      Id 列应该具有标识(即自动递增!)所以这一定是 EF 错误。

      您可以使用 SQL 手动将身份直接添加到数据库中,但我更喜欢使用实体框架。

      如果您遇到同样的问题,我会看到两个简单的解决方案

      替代 1

      使用

      来反转错误创建的迁移
      update-database -target:{insert the name of the previous migration}
      

      然后将identity: true手动添加到迁移代码中,然后再次update-database

      替代 2

      您创建了一个添加身份的新迁移。如果您没有更改模型并运行

      add-migration identity_fix
      

      它将创建一个空迁移。然后添加这个

          public partial class identity_fix : DbMigration
          {
              public override void Up()
              {
                  AlterColumn("dbo.LogEmailAddressStatsFails", "Id", c => c.Int(nullable: false, identity: true));
              }
      
              public override void Down()
              {
                  AlterColumn("dbo.LogEmailAddressStatsFails", "Id", c => c.Int(nullable: false));
              }
          }
      

      【讨论】:

      • 这个表重命名了我。谢谢。
      【解决方案7】:

      这个问题也可能是由反向键声明引起的。如果您使用fluent来配置关系,请确保左右键映射到正确的实体。

      【讨论】:

        【解决方案8】:

        我有同样的问题。就我而言,这是由于 datetime 字段具有空值。我必须将一个值传递给 datetime 并且一切都很好

        【讨论】:

          【解决方案9】:

          另一个答案:

          我用过这个:

          public List<EdiSegment> EdiSegments { get; set; }
          

          而不是这个:

          public virtual ICollection<EdiSegment> EdiSegments { get; set; }
          

          并收到上述错误消息。

          【讨论】:

            【解决方案10】:

            我遇到了同样的错误,就我而言,问题是我添加了一个已加载“AsNoTracking”的关系对象。我不得不重新加载关系属性。

            顺便说一句,有些人建议对数据库中已经存在的关系使用“附加”,不过我没有尝试过这个选项。

            【讨论】:

              【解决方案11】:

              在我的情况下,问题是我不正确地重命名了一个列,因此迁移生成了两列,一列称为“TeamId”,另一列称为“TeamID”。 C# 关心,SQL 不关心。

              【讨论】:

                【解决方案12】:

                这里还有另一个不同的情况。 查询被强制转换为列表,在执行此操作时,它通过其构造函数创建实体,以便在 ToList() 之后的 linq 表达式中进行比较。这会创建在 linq 表达式完成后进入删除状态的实体。
                然而!有一个小的调整,在构造函数中创建了另一个实体,因此这个新实体链接到一个标记为已删除的实体。

                一些代码来说明:

                query.Except(_context.MyEntitySetSet()
                                .Include(b => b.SomeEntity)
                                .Where(p => Condition)
                                .ToList() // This right here calls the constructor for the remaining entities after the where
                                .Where(p => p.Collection.First(b => Condition).Value == 0)
                                .ToList();
                

                MyEntity 的构造函数:

                public partial class MyEntity
                {
                    protected MyEntity()
                    {
                        // This makes the entities connected though, this instance of MyEntity will be deleted afterwards, the instance of MyEntityResult will not.
                        MyEntityResult = new MyEntityResult(this);
                    }
                }
                

                我的解决方案是确保整个表达式在 IQueryable 内完成,这样就不会创建任何对象。

                【讨论】:

                  【解决方案13】:

                  我不完全确定它会对您的情况有所帮助,因为我正在使用 Fluent API 设置我的表,但是,据我所知,无论是否使用数据注释设置架构,都会出现问题(属性)或 Fluent API(配置)。

                  EF (v. 6.1.3) 中似乎存在一个错误,因为它在将数据库更新到下一次迁移时忽略了对架构的某些更改。最快的方法是(在开发阶段)从数据库中删除所有表并再次从初始化阶段迁移。

                  如果您已经投入生产,我发现最快的解决方案是手动更改数据库中的架构,或者,如果您想对更改进行版本控制,请手动操作方法 Up() Down() 在您的迁移中。

                  【讨论】:

                    【解决方案14】:

                    今天我遇到了这个问题并尝试了上面发布的可能解决方案,但没有一个对我有帮助。我实现了UnitOfWork 模式,系统在添加所有记录后最后提交数据。

                    在我的案例中,系统是结合这两个模型并查询数据库

                    对象名称“dbo.RoleModelUserModel”无效。

                    这实际上是两个不同的模型。

                    我通过重新排序插入语句并首先添加父实体来解决此问题。在这种情况下,首先添加用户并解决问题。

                    【讨论】:

                      【解决方案15】:

                      经过一番调查,我发现 .Net 在 SQL Server Compact Edition中支持 01/01/0001 00:00:00 的最小日期 (DateTime.MinValue) 和 31/12/9999 23:59:59 的最大值 (DateTime.MaxValue) > 最短日期为01/01/1753 00:00:00。 当我输入一个大于01/01/1753 00:00:00 的日期时,这个错误就消失了。

                      【讨论】:

                        猜你喜欢
                        • 1970-01-01
                        • 1970-01-01
                        • 1970-01-01
                        • 1970-01-01
                        • 1970-01-01
                        • 1970-01-01
                        • 2012-01-17
                        • 2015-08-01
                        • 2019-08-23
                        相关资源
                        最近更新 更多