Why can't EF handle two properties with same f

2019-09-14 21:42发布

Apparently, EF6 doesn't like objects that have multiple foreign key properties that use the same key value, but do not share the same reference. For example:

var user1 = new AppUser { Id = 1 };
var user2 = new AppUser { Id = 1 };

var address = new Address
{
    CreatedBy = user1, //different reference
    ModifiedBy = user2 //different reference
};

When I attempt to insert this record, EF throws this exception:

Saving or accepting changes failed because more than one entity of type
'AppUser' have the same primary key value. [blah blah blah]

I've discovered that doing this resolves the issue:

var user1 = new AppUser { Id = 1 };
var user2 = user1; //same reference

I could write some helper code to normalize the references, but I'd rather EF just know they're the same object based on the ID alone.

As for why EF does this, one explanation could be that its trying to avoid doing multipe CRUD operations on the same object since separate instances of the same entity could contain different data. I'd like to be able to tell EF not to worry about that.

Update

So it's as I suspected per my last paragraph above. In absense of a means to tell EF not to do CRUD on either instance, I will just do this for now:

if (address.ModifiedBy.Id == address.CreatedBy.Id)
{
    address.ModifiedBy = address.CreatedBy;
}

Works well enough so long as I am not trying to do CRUD on either.

Update2

I've previously resorted to doing this to prevent EF from validating otherwise-required null properties when all I need is the child entity's ID. However, it doesn't keep EF from going into a tizzy over separate instances with the same ID. If it's not going to do CRUD on either AppUser object, why does it care if the instances are different?

foreach (var o in new object[] { address.ModifiedBy, address.CreatedBy })
{
    db.Entry(o).State = EntityState.Unchanged;
}

3条回答
狗以群分
2楼-- · 2019-09-14 22:19

If you get AppUser from context, then you will not need to do anything, because Entity Framework will track entities:

var user1 = context.AppUsers.Find(1);
var user2 = context.AppUsers.Find(1);

var address = new Address
{
    CreatedBy = user1, //different reference
    ModifiedBy = user2 //different reference
};

Now, they both will point to same objects and will not cause to conflict.

查看更多
爱情/是我丢掉的垃圾
3楼-- · 2019-09-14 22:21

You can add two extra properties to have the Id for the main objects which is the AppUser, then you can use only one AppUser object and reference it for both the created and modified by properties.

CreatedById = user1.Id, 
ModifiedById = user1.Id 

Otherwise, your code will end up by saving two instances of AppUser with the same primary key.

Another approach is to set both the foreign key properties to only one AppUserobject

查看更多
走好不送
4楼-- · 2019-09-14 22:23

The explanation is that EF's change tracker is an identity map. I.e. a record in the database is mapped to one, and only one, CLR object.

This can be demonstrated easily by trying to attach two objects with the same key:

context.AppUsers.Attach(new AppUser { Id = 1 });
context.AppUsers.Attach(new AppUser { Id = 1 });

The second line will throw an exception:

Attaching an entity of type 'AppUser' failed because another entity of the same type already has the same primary key value.

This also happens if you assign

CreatedBy = user1, //different reference
ModifiedBy = user2 //different reference

Somewhere in the process, user1 and user2 must be attached to the context, giving rise to the exception you get.

Apparently, you have a function that receives two Id values that can be different or identical. Admittedly, it would be very convenient if you could simply create two AppUser instances from these Ids, not having to worry about identical keys. Unfortunately, your solution ...

if (address.ModifiedBy.Id == address.CreatedBy.Id)

... is necessary. Solid enough, though.

查看更多
登录 后发表回答