When having the following scenario configured in the model:
It results in:
ef migrations update fail moving forwardTo be clear this happens not just the first time after adding, but every time we create a new migration from now on.
Please see the _Identifying the root cause_ section at the end as I think I have pinpointed the problem.
Business Scenario:
``` C#
// the table comes from a legacy system, we cannot change it in any way
public class EntityA{
public int Id { get; set; }
public int MyNavigationPropertyId { get; set; }
public EntityB MyNavigationProperty { get; set; }
}
public class EntityB{
public int Id { get; set; }
public int AlternateKeyOnEntityB { get; set; }
}
modelBuilder.Entity
{
entity.ToView("EntityA");
entity.HasOne(d => d.MyNavigationProperty)
.WithMany()
.HasPrincipalKey(d => d.AlternateKeyOnEntityB);
});
```
Identifying the root cause:
IsIgnoredByMigrations extension method is calledToView EF Core version: 3.1.3
Database provider: Microsoft.EntityFrameworkCore.SqlServer
Target framework: .NET Core 3.1
Operating system: Windows
IDE: Visual Studio 2019 16.3, but doesn't matter as migrations were crated via dotnet ef cli
After further investigation:
AlternateKeyOnEntityB on EntityB via model builderHasAlternateKey are removed when there is a relationship that would redefine them (as in our case)IsIgnoredByMigrations filters out any view (EntityA), hence the relationship that would define the alternate key implicitly is not generated eitherAs far as I can asses this could either be fixed by:
The second solution would likely also cover other edge cases outside of this issue that stem from the snapshot not containing readonly entities.
Note:
HasPrincipalKey call everything works just fine (migration wise)Notes for implementation:
Issue here is that ModelSnapshot ignores generating anything for EntityTypes which are ignored by migrations hence there is no entity type call generate for EntityA.
But our key generation code ignores generating HasAlternateKey when there is referencing FK (though it was ignored in previous step). Hence every model diff will say there is a new alternate key since previous alternate key was never stored in snapshot.
https://github.com/dotnet/efcore/blob/9f316e5b6150b4abdbd75697823eec27cef64523/src/EFCore.Design/Migrations/Design/CSharpSnapshotGenerator.cs#L651-L657
Is there any chance to see a bugfix for this in 3.x? Totally understand if not just trying to understand if this is rather short term ro timeline is release of 5.0
If it wont be available before 5.0 release do you have any suggestions for a workaround?
Here are the ones we came up with:
Can you think of any other options? We are not afraid to meddle with internal infrastructure if necessary. When a patch comes along we would then be able to remove whatever intermediate solution there might be and not have to change the business logic of our app.
In the model snapshot file, just add
C#
modelBuilder.Entity("EntityB").HasAlternateKey("AlternateKeyOnEntityB");
That will register the alternate key and future migration will stop generating operation in Up method.
For already generated migrations, just remove the operation which generates AK from Up method.
@smitpatel You are correct that when editing the snapshot the next migration will be generated correctly. However the snapshot after the migration does not contain the alternate key anymore. Meaning developers would need to remember todo it (similar to option 1 above).
Still manipulating the snapshot file is the most promising avenue. I have looked into whether these unrecognized keys could be defined in a partial class, but there is no way to hook into BuildModel method that is already defined in the generated snapshot file.
We are now looking at one of two options:
BuildModel method and adding the keys after the original method has run (this happens on every build + has build time validation)The last will be what we are focusing on. Will report back here if this worked out for others to find.
We found another workaround using compiler symbols, the general idea is
We have defined a custom compiler symbol SKIPVIEWRELATIONS, to be able to quickly identify it later in our code. The only downside is that devs need to define the alternate key as well as the relation, but that's manageable.
The benefits are that once setup correctly:
I wanne thank you for the time you invested to pinpoint this issue which allowed to to come up with this workaround.
Most helpful comment
Notes for implementation:
Issue here is that ModelSnapshot ignores generating anything for EntityTypes which are ignored by migrations hence there is no entity type call generate for
EntityA.But our key generation code ignores generating HasAlternateKey when there is referencing FK (though it was ignored in previous step). Hence every model diff will say there is a new alternate key since previous alternate key was never stored in snapshot.
https://github.com/dotnet/efcore/blob/9f316e5b6150b4abdbd75697823eec27cef64523/src/EFCore.Design/Migrations/Design/CSharpSnapshotGenerator.cs#L651-L657