Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does Entity Framework try to insert existing entity?

I am using Entity Framework (with a code-first approach) and the database is created successfully with the expected foreign keys and unique key constraints.

I have those two model classes:

public class Foo 
{
    [Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    public Guid Id { get; set; }

    public Bar Bar { get; set; } 
}

public class Bar
{
    [Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    public Guid Id { get; set; }

    [Index(IsUnique = true), StringLength(512)]
    public string Url { get; set; }
}

And this application code:

var foo = GetData();

using (DatabaseContext db = new DatabaseContext())
{
    db.Entry(foo).State = EntityState.Added;

    if (foo.Bar != null)
    {
        var bar = await db.Bar.FirstOrDefaultAsync(x => x.Url == foo.Bar.Url);

        if (bar != null)
        {
            // When I assign an existing entity ...
            foo.Bar = bar;
        }
    }

    // ... following exception will be thrown.
    await db.SaveChangesAsync();
}

SqlException: Cannot insert duplicate key row in object 'dbo.Bar' with unique index 'IX_Url'. The duplicate key value is (https://api.example.com/rest/v1.0/bar/FD6FAB72-DCE2-47DB-885A-424F3D4A70B6). The statement has been terminated.

I don't understand why Entity Framework is trying to add the navigation property Bar, even after obtaining and assigning it from the same DbContext. Similar StackOverflow questions haven't provided any working solution yet.

Please tell me if I need to provide additional information.

Did I forget to set any EF related configuration or something like that? Thank you in advance!

like image 673
ˈvɔlə Avatar asked Mar 06 '23 12:03

ˈvɔlə


1 Answers

It's because

db.Entry(foo).State = EntityState.Added;

marks the foo.Bar (and any referenced entity not tracked by the context) as Added as well.

You should resolve the referenced entities before adding the Foo entity:

var foo = GetData();
using (DatabaseContext db = new DatabaseContext())
{
    // Resolve references
    if (foo.Bar != null)
    {
        var bar = await db.Bar.FirstOrDefaultAsync(x => x.Url == foo.Bar.Url);
        if (bar != null)
            foo.Bar = bar;
    }
    // Then add the entity
    db.Entry(foo).State = EntityState.Added;

    await db.SaveChangesAsync();
}
like image 187
Ivan Stoev Avatar answered Mar 14 '23 22:03

Ivan Stoev