10

I use Entity Framework 7.0.0-rc1-final with SQL 2014 LocalDB (code first). I have the model class:

public class UnitGroup {
    public int UnitGroupId { get; private set; }
    public string Name { get; set; }

    public ObservableCollection<Unit> UnitSet { get; set; }
    public UnitGroup() {
        UnitSet = new ObservableCollection<Unit>();
    }
}

I use this delegate for fluent configuration:

Action<EntityTypeBuilder<UnitGroup>> UnitGroupConfig = delegate (EntityTypeBuilder<UnitGroup> e) {
        e.Property(p => p.Name).IsVarchar(25).IsRequired();

        e.HasAlternateKey(p => p.Name);
    };

IsVarchar() is my extension method:

public static PropertyBuilder IsVarchar(this PropertyBuilder<string> propertyBuilder, int maxLength) {
        return propertyBuilder.HasColumnType($"varchar({maxLength})");
    }

Then I use it like this:

protected override void OnModelCreating(ModelBuilder modelBuilder) {    
        modelBuilder.Entity<UnitGroup>(e => UnitGroupConfig(e));
}

This is migration code for this table:

migrationBuilder.CreateTable(
            name: "UnitGroup",
            columns: table => new
            {
                UnitGroupId = table.Column<int>(nullable: false)
                    .Annotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn),
                Name = table.Column<string>(type: "varchar(25)", nullable: false)
            },
            constraints: table =>
            {
                table.PrimaryKey("PK_UnitGroup", x => x.UnitGroupId);
                table.UniqueConstraint("AK_UnitGroup_Name", x => x.Name);
            });

After migration a have table in my Db:UnitGroup table

I need to encapsulate EF in my Prism Data Module. I have a class for this:

public class DataService : IDataService {
private DataContext context;

public DataService() {
    context = new DataContext();
}

public IQueryable<T> GetAsTracking<T>(params Expression<Func<T, object>>[] includes) where T : class {
    return includes.Aggregate(context.Set<T>().AsTracking(), (source, expression) => {
        if (expression.Body is MemberExpression) {
            return source.Include(expression);
        }
        return source;
    });
}

public int SaveChanges() {
    return context.SaveChanges();
}

}

Finally, I try to run code like this:

var ds = new DataService();
var ug = ds.GetAsTracking<UnitGroup>().First();
ug.Name="new value";
ds.SaveChanges();

and get this error:

The property 'Name' on entity type 'UnitGroup' is part of a key and so cannot be modified or marked as modified.

I have found similar questions here. It was a problem in editing primary key all the time. I checked all described parts twice. Property Name is not part of primary key, it's part of the unique key. When I have used EF6, I had code in partial class of migration:

CreateIndex("UnitGroup", "Name", unique: true);

which was created the same unique key and I had the ability to edit Name. Why now it's impossible now, in EF7?

Salomon Zhang
  • 1,553
  • 3
  • 23
  • 41
cyberVoOdOo
  • 257
  • 1
  • 2
  • 11

2 Answers2

14

I solved the problem thanks to Rowan Miller. He said:

In EF Core, an alternate key is designed to be used as the target of a relationship (i.e. there will be a foreign key that points to it). Currently EF does not support changing the values.

If I want a unique index on the property, then I must use this code:

modelBuilder.Entity<UnitGroup>().HasIndex(u => u.Name).IsUnique();
cyberVoOdOo
  • 257
  • 1
  • 2
  • 11
-1

I got same Exception in EF6, let me put my solution here.

In my case it's complicated, finally I find the reason, First I have two DbSet:

var listTableA = dbContext.Set<TableA>();
var listTableB = dbContext.Set<TableB>();

Then, primary key of TableA item is modify, and then I do RemoveRange to TableB, I got this Excpetion:

var itemA = listTableA.First();
//Id property is primary key of TableA
itemA.Id = 123;

// ... omit some code in the middle ... 

var itemsToDel = listTableB.Where(t=>t.CreateTime > "20180101");
// Exception throw here 
listableB.RemoveRange(itemsToDel); 

So in my case, the problem is not really occur at listableB.RemoveRange, but is caused by itemA.Id = 123; which modify the primaryKey of TableA.

Finally I avoid modify the primary key of tableA and use other alternative way, the problem is solved.

yu yang Jian
  • 6,680
  • 7
  • 55
  • 80