Hi everyone!
In project 'Core.DomainModels' add this file:
_IEntity.cs_:
public interface IEntity<T>
{
T Id { get; set; }
}
public interface IEntity : IEntity<Guid>
{
}
And in project 'Core.DomainModels.Identity' add this classes:
Abstract & interfaces:
_IUser.cs_
public interface IUser<T> : IEntity<T>
{
DateTime RegisterDate { get; set; }
}
public interface IUser : IUser<Guid>
{
}
_IRole.cs_
public interface IRole<T> : IEntity<T>
{
string Name { get; set; }
}
public interface IRole : IRole<Guid>
{
}
_IUserRole.cs_
public interface IUserRole<T>// : IEntity<T>
{
T UserId { get; set; }
T RoleId { get; set; }
IUser User { get; set; }
IRole Role { get; set; }
}
public interface IUserRole : IUserRole<Guid>
{
}
Real models:
_User.cs_
public class User : IdentityUser<Guid>, IUser<Guid>
{
public static User CreateEmptyInstance()
{
return new User();
}
#region Implementation of IUser<Guid>
public DateTime RegisterDate { get; set; }
#endregion
[NotMapped]
public bool IsAuthenticated { get; set; }
}
_Role.cs_
public class Role : IdentityRole<Guid>, IRole<Guid>
{
}
_UserRole.cs_
public class UserRole : IdentityUserRole<Guid>, IUserRole
{
#region Implementation of IEntity<Guid>
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.None)]
public Guid Id { get; set; }
#endregion
#region Implementation of IUserRole<Guid>
[NotMapped]
public IUser User { get; set; }
[NotMapped]
public IRole Role { get; set; }
#endregion
}
Add-Migration InitDev -Context EfDbMigrations -Environment Development
System.InvalidOperationException: The derived type 'UserRole' cannot have KeyAttribute on property 'Id' since primary key can only be declared on the root type. at Microsoft.EntityFrameworkCore.Metadata.Conventions.Internal.KeyAttributeConvention.Apply(InternalModelBuilder modelBuilder)
at Microsoft.EntityFrameworkCore.Metadata.Conventions.Internal.ConventionDispatcher.OnModelBuilt(InternalModelBuilder modelBuilder)
at Microsoft.EntityFrameworkCore.Infrastructure.ModelSource.CreateModel(DbContext context, IConventionSetBuilder conventionSetBuilder, IModelValidator validator)
at System.Collections.Concurrent.ConcurrentDictionary`2.GetOrAdd(TKey key, Func`2 valueFactory)
at Microsoft.EntityFrameworkCore.Internal.DbContextServices.CreateModel()
at Microsoft.EntityFrameworkCore.Internal.LazyRef`1.get_Value()
at Microsoft.Extensions.DependencyInjection.ServiceProvider.ScopedCallSite.Invoke(ServiceProvider provider)
at Microsoft.Extensions.DependencyInjection.ServiceProviderServiceExtensions.GetService[T](IServiceProvider provider)
at Microsoft.Extensions.DependencyInjection.ServiceProvider.TransientCallSite.Invoke(ServiceProvider provider)
at Microsoft.Extensions.DependencyInjection.ServiceLookup.ConstructorCallSite.Invoke(ServiceProvider provider)
at Microsoft.Extensions.DependencyInjection.ServiceProvider.TransientCallSite.Invoke(ServiceProvider provider)
at Microsoft.Extensions.DependencyInjection.ServiceProviderServiceExtensions.GetRequiredService(IServiceProvider provider, Type serviceType)
at Microsoft.Extensions.DependencyInjection.ServiceProviderServiceExtensions.GetRequiredService[T](IServiceProvider provider)
at Microsoft.EntityFrameworkCore.Design.MigrationsOperations.AddMigration(String name, String outputDir, String contextType)
at Microsoft.EntityFrameworkCore.Tools.Cli.MigrationsAddCommand.Execute(CommonOptions commonOptions, String name, String outputDir, String context, String environment, Action`1 reporter)
at Microsoft.EntityFrameworkCore.Tools.Cli.MigrationsAddCommand.<>c__DisplayClass0_0.<Configure>b__0()
at Microsoft.Extensions.CommandLineUtils.CommandLineApplication.Execute(String[] args)
at Microsoft.EntityFrameworkCore.Tools.Cli.Program.Main(String[] args)
The derived type 'UserRole' cannot have KeyAttribute on property 'Id' since primary key can only be declared on the root type.
My project.json in Core.DAL.Real.Database project:
{
"version": "1.0.0-*",
"dependencies": {
"NETStandard.Library": "1.6.0",
"Microsoft.EntityFrameworkCore": "1.0.1",
"Microsoft.EntityFrameworkCore.Tools": {
"version": "1.0.0-preview2-final",
"type": "build"
},
"Npgsql.EntityFrameworkCore.PostgreSQL": "1.0.1",
"System.Linq": "4.1.0",
"Core.DomainModels.Identity": "1.0.0-*",
"Core.DomainModels": "1.0.0-*"
},
"frameworks": {
"netcoreapp1.0": {
"imports": "dnxcore50"
}
},
"tools": {
"Microsoft.EntityFrameworkCore.Tools": "1.0.0-preview2-final"
}
}
The KeyAttribute on property _Id_ in my _UserRole_ class must not throw this error, right? Or I do something wrong?
I found the same issue #5898 but that issue is closed by @AndriySvyryd and appropriate changes (by commit https://github.com/aspnet/EntityFramework/commit/5ce5b86d37859a1410d9e343489f8b2d3fbb9527) are included in 1.0.1 version of EF Core which I used. I'm confused because of this error. And because of this, I cannot create my first (init) migration...
Please help solve this problem...
EF Core version: 1.0.1
Operating system: Windows 10
Visual Studio version: VS 2015 Update 3
@Valeriy1991 In this case the exception is by design.
UserRole derives from IdentityUserRole<Guid> that is already included in the model, so the key needs to be configured on it, as it's the base class. By default IdentityDbContext configures its key as {UserId, RoleId}.
Since UserRole represents a many-to-many relationship there's no single property that could be used as the implementation of IEntity.Id. Could you redesign the model so UserRole doesn't need to implement IEntity?
Closing as there was no response. Feel free to reopen providing more information if you are still experiencing this issue.
@AndriySvyryd hi! Now, unfortunately, I have no way to reproduce the problem (I'm busy on another project). If a problem appears, I will write about it here (I think in a month). Thanks!
I also have the same/similar problem. I tried to implement a Global Query filter. All my Db Models inherit from Abstract class and when I tried to implement the Global Query Filter, this error started happening. I don't know why exactly I used Abstract class instead of interface, but this should not even be happening, because I don't even have an Id on base class...
@Cubelaster - Query filter cannot produce that issue. Whatever changes you made must have brought your abstract type as entity type in model and then you run into all above issues. Most likely it would be a navigation pointing to abstract type on some entity type. If you cannot resolve, please file a new issue with full repro code for us to look into.
@abakumov-v I have the same problem and using this approach :
modelBuilder.Entity<UserRole>()
.ToTable("UserRoles")
.HasKey(ur => ur.Id);
I just want to say that this happened again :D
A bit differently now.
I am upgrading one of the projects from 2.2 to 3.1 and all my models are using the same base class.
Now EF says that all my models that have a [Key] on them can not have it, since they have a base class.
Now, I know this is not usually true, since I always use this approach.
So, why is it saying anything then?
I just want to say that perhaps the reason for it is the fact that EF 3.1 didn't know how to handle a relationship that existed before so I actually used the base class in "onModelCreating" for determining relations.
I am guessing this is what triggers the error, since EF suddenly becomes aware of the base class?
Has this any sense?
@Cubelaster Avoid using the base type in OnModelCreating if you don't want it included in the model.
Figured as much, however, there are a LOT of use cases where I see benefits of having a single NOT MAPPED model that contains the same logic for a family of tables.
For instance Audit metadata orJson fields.
Both of those require manual table per table configuration while setting it up on single Base class would be the desired way.
I'll try to formulate an example.
Example
Ok, so I want to use a single base class to define a Json field that EVERY table has.
Next is not supported for (I presume) the same reason as this one.
And then I have to implement it for EVERY table in solution.
Maybe I'm doing something wrong, but this seems logical to me.
``` foreach (var entityType in builder.Model.GetEntityTypes())
{
foreach (var property in entityType.GetNavigations())
{
if (property.ClrType == typeof(Translations))
{
builder.Entity(entityType.Name)
.Property
.HasConversion(v => JsonConvert.SerializeObject(v),
v => JsonConvert.DeserializeObject
.HasColumnType("json");
}
}
}
@Cubelaster This will be handled by https://github.com/dotnet/efcore/issues/6787. For now the best you can do is to extract the common configuration to a method.
@Cubelaster This will be handled by #6787. For now the best you can do is to extract the common configuration to a method.
Ok, sounds nice.
Would you be willing to provide an example or a link to one?
Thank you
@Cubelaster Something like this:
```C#
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
ConfigureBase(modelBuilder.Entity
}
protected EntityTypeBuilder
where T : MyBase
{
ConfigureTranslations(entityType.Property(d => d.MyTranslations));
return entityType;
}
protected PropertyBuilder
=> property.HasConversion(
v => JsonConvert.SerializeObject(v),
v => JsonConvert.DeserializeObject
.HasColumnType("json");
```C#
private class MyBase
{
public string Id { get; set; }
public Translations MyTranslations { get; set; }
}
private class MyDerived : MyBase
{
}
private class Translations
{
}
Most helpful comment
@Cubelaster Something like this:
```C#());
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
ConfigureBase(modelBuilder.Entity
}
protected EntityTypeBuilder ConfigureBase(EntityTypeBuilder entityType)
where T : MyBase
{
ConfigureTranslations(entityType.Property(d => d.MyTranslations));
return entityType;
}
protected PropertyBuilder ConfigureTranslations(PropertyBuilder property)(v))
=> property.HasConversion(
v => JsonConvert.SerializeObject(v),
v => JsonConvert.DeserializeObject
.HasColumnType("json");