gius
gius

Reputation: 9437

Marking navigation property as modified in Entity Framework 7

I have an EF7 DbContext with disabled change tracking because I want to mark all changes explicitly:

var entry = context.Entry(changedEntity);
entry.Property(propertyName).IsModified = true;

This works exactly as I want it to.

However, this does not work when a reference (navigation property) has been updated.

For example, let's say my entity has two properties - ParentId and Parent where ParentId is a foreign key and Parent is the reference to the parent entity.

Calling

entry.Property("Parent").IsModified = true;

does not work and throws ModelItemNotFoundException because Parent is not a property of the entity in terms of EF (it is a navigation instead).

In EF6, this could be done as follows:

var reference = context.Entry(changedEntity).Reference("Parent");
reference.CurrentValue = reference.CurrentValue;

IN EF7, there is no such function. I can get to the INavigation object with

entry.Metadata.GetNavigation("Parent")

but cannot see any way to mark it as modified.

So, how should I do it?

Note: I know that setting ParentId would work, but this is not suitable for me because the referenced entity does not have ID yet as it has just been created and will get its ID from database when saved. Thus, I need to set it through the reference Parent.

Edit:

The note above was true for EF6 but is no longer valid for EF7 which I was not aware of. Thus, the solution is just as described in the note and answer below.

Upvotes: 2

Views: 1576

Answers (2)

Lentyai
Lentyai

Reputation: 986

In my case (EF6) marking the owned properties (nested ones) as modified succeeded in the following manner:

var reference = Context.Entry(record).Reference(o => o.ParentProperty);
if (reference.CurrentValue != null)
{
    reference.TargetEntry.Property(o => o.ChildProperty1).IsModified = true;
    reference.TargetEntry.Property(o => o.ChildProperty2).IsModified = true;
    reference.TargetEntry.Property(o => o.ChildProperty3).IsModified = true;
}

The configuration was done as follows:

builder.OwnsOne(l => l.ParentProperty, b =>
{
    b.Property(u => u.ChildProperty1).HasConversion(
        v => Cryptography.Encrypt(v),
        v => Cryptography.Decrypt(v)
    );

    b.Property(u => u.ChildProperty2).HasConversion(
        v => Cryptography.Encrypt(v),
        v => Cryptography.Decrypt(v)
    );

    b.Property(u => u.ChildProperty3).HasConversion(
        v => Cryptography.Encrypt(v),
        v => Cryptography.Decrypt(v)
    );
});

Cryptography is my internal class for making encryption/decryption.

Upvotes: -1

gius
gius

Reputation: 9437

Wow, it seems that when a new entity is attached to the DbContext, it actually gets ID (-1 in my case). So, I can set ParentId even when the target entity is new and has not been added to the database yet.

I wonder whether there are any checks that the new ID is unique (there could already be an entry with ID -1 in database).

Upvotes: 0

Related Questions