保存不公开其关系的外键属性的实体时发生错误

时间:2011-10-29 10:46:02

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

我首先在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以获取详细信息。

at

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; }
}

14 个答案:

答案 0 :(得分:155)

对于那些仍然会在正确定义所有键的情况下出现此错误的人,请查看您的实体,并确保不要将datetime字段留空值。

答案 1 :(得分:11)

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

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

即使OP由于未能为DateTime的第二个实例初始化User而得到错误,它们也会得到诊断并发症 - 'EntityEntries'为空,并且顶层令人困惑消息...因为他们的某个实体没有'公开外键属性'。要解决此问题,Avatar应定义public virtual ICollection<User> Users { get; set; }属性。

答案 2 :(得分:6)

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

答案 3 :(得分:3)

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

想象一下代码第一个EF模型,其中有一个Garage实体,其中包含Car个实体的集合。我需要从车库中取出一辆车,所以我最终得到的代码如下:

garageEntity.Cars.Remove(carEntity);

相反,它应该看起来像这样:

context.Cars.Remove(carEntity);

答案 4 :(得分:2)

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

答案 5 :(得分:2)

在我的情况下,由于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

Alt 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));
        }
    }

答案 6 :(得分:0)

另一个答案:

我用过这个:

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

而不是:

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

并收到上面提到的错误消息。

答案 7 :(得分:0)

我有同样的错误,在我的情况下,问题是我添加了一个已经加载的关系对象&#34; AsNoTracking&#34;。我不得不重新加载关系属性。

BTW,有人建议使用&#34;附加&#34;对于已经存在于db中的关系,我还没有尝试过这个选项。

答案 8 :(得分:0)

我有同样的问题。在我的情况下,它是由于datetime字段的空值。我不得不将值传递到日期时间,一切都很顺利

答案 9 :(得分:0)

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

答案 10 :(得分:0)

这里又是另一种情况。 查询被转换为列表,在执行此操作时,它通过构造函数创建实体,以便在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中完成,这样就不会创建任何对象。

答案 11 :(得分:0)

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

EF(v.6.1.3)中似乎存在一个错误,因为它在将数据库更新到下一次迁移时忽略了对模式的某些更改。围绕它的最快路线是(在开发阶段)从数据库中删除所有表并再次从init阶段进行迁移。

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

答案 12 :(得分:0)

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

在我的情况下,系统正在组合两个模型并查询数据库

  

无效的对象名称'dbo.RoleModelUserModel'。

其中这些是两个不同的模型。

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

答案 13 :(得分:0)

经过一番调查,我发现虽然.Net在 SQL Server Compact中支持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时,此错误消失了。