Efcore: Allow changing values of alternate key (unique constraint) fields

Created on 30 Sep 2016  路  5Comments  路  Source: dotnet/efcore

Steps to reproduce

Given a user:

public class User
    {
        public Guid UserId { get; set; }
        public string FirstName { get; set; }
        public string LastName { get; set; }
        public string Email { get; set; }
    }

With a mapping that includes a unique constraint:

public static void Map(EntityTypeBuilder<User> builder)
        {
            builder.HasKey(x => x.UserId);
            builder.HasAlternateKey(x => x.Email);
            builder.Property(x => x.FirstName).HasMaxLength(25);
            builder.Property(x => x.LastName).HasMaxLength(50);
            builder.Property(x => x.Email).IsRequired().HasMaxLength(256);
        }

Attempts to change the value of user.Email result in System.InvalidOperationException: The property 'Email' on entity type 'User' is part of a key and so cannot be modified or marked as modified.

The issue

Describe what is not working as expected.
I should be able to update the value of fields that have unique constraints as long as the new value is also unique.

If you are seeing an exception, include the full exceptions details (message and stack trace).

Exception message:
 Test method DataAccess.Tests.UserRepositoryTests.Update_user_writes_to_database threw exception:
System.InvalidOperationException: The property 'Email' on entity type 'User' is part of a key and so cannot be modified or marked as modified.
Stack trace:
    at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.InternalEntityEntry.SetPropertyModified(IProperty property, Boolean changeState, Boolean isModified)
   at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.ChangeDetector.DetectChanges(InternalEntityEntry entry)
   at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.ChangeDetector.DetectChanges(IStateManager stateManager)
   at Microsoft.EntityFrameworkCore.DbContext.<SaveChangesAsync>d__30.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter`1.GetResult()
   at DataAccess.Tests.UserRepositoryTests.<Update_user_writes_to_database>d__7.MoveNext() in D:\test\DataAccess.Tests\UserRepositoryTests.cs:line 135
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)

Further technical details

EF Core version: 1.0.1
Operating system: Windows 10
Visual Studio version: n/a

Other details about my project setup:
Also using "Microsoft.EntityFrameworkCore.InMemory": "1.0.1"

closed-by-design

Most helpful comment

@nathanrobinson You probably don't want to use HasAlternateKey. You probably just want to create a unique index:

C# builder.HasIndex(x => x.Email).IsUnique();

HasAlternateKey is never needed. It is only there for people who like to be super-explicit about relationship mapping. If you remove all calls to HasAlternateKey and still get this error, then it is because Email is being used at the principal end of an FK relationship, for which changing the value is not supported.

All 5 comments

@nathanrobinson You probably don't want to use HasAlternateKey. You probably just want to create a unique index:

C# builder.HasIndex(x => x.Email).IsUnique();

HasAlternateKey is never needed. It is only there for people who like to be super-explicit about relationship mapping. If you remove all calls to HasAlternateKey and still get this error, then it is because Email is being used at the principal end of an FK relationship, for which changing the value is not supported.

You are right. I was confused because the intellisense for HasAlternateKey says

Creates a unique constraint for this entity if one does not exist over the specified properties.

Thanks!

Note for triage: we should probably update the docs.

Closing as this is a doc change (PR is out).

Actually @ajcvickers solution works just fine.
I have adopted the following one:

builder.Entity<Company>()
    .HasIndex(x => x.Name).IsUnique()
    .HasName("UI_Companies_Name");
Was this page helpful?
0 / 5 - 0 ratings