简体   繁体   中英

Entity Framework Code First unique index foreign key throws DbUpdate Exception

Here's the entites and context code:

public class Family
{
    public int FamilyID { get; set; }
    public String address { get; set; }
    public virtual ICollection<Member> FamilyMembers { get; set; }
}
public class Member
{
    [Index("MemberUniqueID", IsUnique = true)]
    public int MemberID { get; set; }

    [StringLength(50)] [Index("MemberUniqueIndex", 1, IsUnique = true)]
    public String name { get; set; }
    [StringLength(50)] [Index("MemberUniqueIndex", 2, IsUnique = true)]
    public String surname { get; set; }

    public int age { get; set; }
    public int FamilyID { get; set; }
    public virtual Family Family { get; set; }
}
public class Bicycle
{
    public int BicycleID { get; set; }
    public virtual Member Owner { get; set; }
}
public class MyContext : DbContext
{
    public MyContext : base () { }
    public DbSet<Member> MemberDB { get; set; }
    public DbSet<Family> FamilyDB { get; set; }
    public DbSet<Bicycle> BicycleDB { get; set; }
}

Now, I add a few examples of each, add them and SaveChanges(); . Then I try to run this code:

    public void bicycle_set_FK(int IDbicycle, int IDmember)
{
       var bicycleToFind = BicycleDB.Find(IDbicycle);
       var memberToSetAsFK = MemberDB.Find(IDmember);
       bicycleToFind.Owner = memberToSetAsFK;
       SaveChanges();
}

And as a result, I get this error:

An unhandled exception of type 'System.Data.Entity.Infrastructure.DbUpdateException' occurred in EntityFramework.dll Cannot insert duplicate key row in object 'dbo.Members' with unique index 'MemberUniqueIndex'. The duplicate key value is (John, Smith). The statement has been terminated.

The weird thing is, if I run that method simultaneously while adding the examples, the method works fine and sets the FK as expected. But if I first add the examples with the first run of the code, and I try to set FKs on the second run of the code, it throws that exception.

EDIT:

Okay, so I've done as recommended and after reading the artice you linked I have this:

[ForeignKey("MemberID")]
public int? OwnerID { get; set; }
public virtual Member MemberID { get; set; }

And the method looks like this:

public void bicycle_set_FK(int IDbicycle, int IDmember)
{
   var bicycleToFind = BicycleDB.Find(IDbicycle);
   bicycleToFind.MemberID = null;
   bicycleToFind.OwnerID = IDmember;
   SaveChanges();
}

But this generates a new problem:

"Violation of PRIMARY KEY constraint 'PK_dbo.Members'. Cannot insert duplicate key in object 'dbo.Members'. The duplicate key value is (0).\\r\\nThe statement has been terminated."

The values I use are 1 and 3 ( bicycle_set_FK(1, 3); ). Members contain 3 rows with IDs: 1, 2 and 3. So where does it find that zero, since it's not in the database nor do I enter it at any point in the code?

And one more thing: the article doesn't mention how to solve that problem in a one-to-many relationship. How do I add FK between Family and Member? Do I add some property such as public List<Member> FamilyMembersFK ?

Entity framework is re-adding your Owner(Member). There are a couple of ways around this, but since you have the FK try this:

public class Bicycle
{
    public int BicycleID { get; set; }

    public int MemberID { get; set; }  // You can call this ownerId, but then you need to setup the relationship with annotation or fluent
    public virtual Member Owner { get; set; }
}

public void bicycle_set_FK(int IDbicycle, int IDmember)
{
   var bicycleToFind = BicycleDB.Find(IDbicycle);
   // var memberToSetAsFK = MemberDB.Find(IDmember); ** Don't need to do this since you have FK **
   bicycleToFind.MemberId = IDmember;
   SaveChanges();
}

See https://msdn.microsoft.com/en-us/magazine/dn166926.aspx?f=255&MSPPError=-2147217396

EDIT: First, I highly recommend you follow some convention when naming your navigation classes and keys. You are calling your navigation class "MemberId" - that is very confusing. If you want the FK to be OwnerId, go with something like this:

public int? OwnerID { get; set; }

[ForeignKey("OwnerID")]
public virtual Member Owner { get; set; }

Second, you don't need this annotation:

//[Index("MemberUniqueID", IsUnique = true)]  **MemberID will be a identity key by default so it will have a unique index created.
public int MemberID { get; set; }

Now you can insert an existing owner like this:

public void bicycle_set_FK(int IDbicycle, int IDmember)
{
   var bicycleToFind = BicycleDB.Find(IDbicycle);
   bicycleToFind.OwnerID = IDmember;  // Don't worry about the nav class, just set the FK
   SaveChanges();
}

Regarding the one to many, you already have that configured with:

public virtual ICollection<Member> FamilyMembers { get; set; }

So to add a new member:

var newMember = new Member {
    Name = "Joe",
    Surname = "Smith",
    Age = 30,
    FamilyId = familyId
};

context.Members.Add(newMember);
SaveChanges();

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM