Abp: How to add navigation property to an entity of a dependen module

Created on 1 May 2020  Â·  19Comments  Â·  Source: abpframework/abp

Version:
2.6.2
Entity:

public class Test : FullAuditedEntityWithUser<Guid, IdentityUser>
    {
        public string Name { get; set; }
    }

MigrationDbContext:

      protected override void OnModelCreating(ModelBuilder builder)
        {
            base.OnModelCreating(builder);
           builder.ConfigurePermissionManagement();
            builder.ConfigureSettingManagement();
            builder.ConfigureBackgroundJobs();
            builder.ConfigureAuditLogging();
            builder.ConfigureIdentity();
            builder.ConfigureIdentityServer();
            builder.ConfigureFeatureManagement();
            builder.ConfigureTenantManagement();

            /* Configure your own tables/entities inside the ConfigureWms method */

            builder.ConfigureWms();
        }

DbContext:

 protected override void OnModelCreating(ModelBuilder builder)
        {

            base.OnModelCreating(builder);

            /* Configure the shared tables (with included modules) here */

            builder.Entity<AppUser>(b =>
            {
                b.ToTable(AbpIdentityDbProperties.DbTablePrefix + "Users"); //Sharing the same table "AbpUsers" with the IdentityUser
                b.ConfigureByConvention();
                b.ConfigureAbpUser();

                /* Configure mappings for your additional properties
                 * Also see the WmsEfCoreEntityExtensionMappings class
                 */
            });

            builder.ConfigureWms();
        }

ConfigureWms:

public static void ConfigureWms(this ModelBuilder builder)
        {
            Check.NotNull(builder, nameof(builder));
            builder.Entity<Test>(
                b =>
                {
                    b.ToTable("tests");
                    b.ConfigureFullAudited();
                    b.Property(x => x.Name).IsRequired().HasMaxLength(200);

                });

        }

Exception Information:

The entity type 'IdentityUserLogin' requires a primary key to be defined. If you intended to use a keyless entity type call 'HasNoKey()'.
System.InvalidOperationException: The entity type 'IdentityUserLogin' requires a primary key to be defined. If you intended to use a keyless entity type call 'HasNoKey()'.
question

Most helpful comment

After my testing, the solution is not available, I made some changes:

DemoDbContext

````csharp
protected override void OnModelCreating(ModelBuilder builder)
{
base.OnModelCreating(builder);

builder.Entity<AppUser>(b =>
{
    b.ToTable(AbpIdentityDbProperties.DbTablePrefix + "Users"); //Sharing the same table "AbpUsers" with the IdentityUser

    b.ConfigureByConvention();
    b.ConfigureAbpUser();
});

builder.ConfigureDemo(); //Shared code between db contexts

builder.Entity<Test>(b =>
{
    //SET RELATIONS FOR THE PROJECT DBCONTEXT

    b.HasOne(x => x.Creator).WithMany().HasForeignKey("CreatorId").IsRequired(false);
    b.HasOne(x => x.LastModifier).WithMany().HasForeignKey("LastModifierUserId").IsRequired(false);
    b.HasOne(x => x.Deleter).WithMany().HasForeignKey("DeleterUserId").IsRequired(false);
});

}
````

DemoDbContextModelCreatingExtensions

public static class DemoDbContextModelCreatingExtensions
{
    public static void ConfigureDemo(this ModelBuilder builder)
    {
        Check.NotNull(builder, nameof(builder));

        builder.Entity<Test>(b =>
        {
            b.ToTable("Tests");

            b.ConfigureByConvention();

            b.Property(x => x.Name).IsRequired(false).HasMaxLength(200);
        });

        /* Configure your own tables/entities inside here */

        //builder.Entity<YourEntity>(b =>
        //{
        //    b.ToTable(DemoConsts.DbTablePrefix + "YourEntities", DemoConsts.DbSchema);

        //    //...
        //});
    }
}

DemoMigrationsDbContext

````csharp
protected override void OnModelCreating(ModelBuilder builder)
{
base.OnModelCreating(builder);

/* Include modules to your migration db context */

builder.ConfigurePermissionManagement();
builder.ConfigureSettingManagement();
builder.ConfigureBackgroundJobs();
builder.ConfigureAuditLogging();
builder.ConfigureIdentity();
builder.ConfigureIdentityServer();
builder.ConfigureFeatureManagement();
builder.ConfigureTenantManagement();

//IGNORE AppUser related properties

builder.Entity<Test>(b =>
{
      b.Ignore(x => x.Creator);
      b.Ignore(x => x.LastModifier);
      b.Ignore(x => x.Deleter);

       b.HasOne<IdentityUser>().WithMany().HasForeignKey("CreatorId").IsRequired(false);
       b.HasOne<IdentityUser>().WithMany().HasForeignKey("LastModifierUserId").IsRequired(false);
       b.HasOne<IdentityUser>().WithMany().HasForeignKey("DeleterUserId").IsRequired(false);
 });

builder.ConfigureDemo(); //Shared code between db contexts

}
````

Test entity

csharp public class Test : FullAuditedEntityWithUser<Guid, AppUser> { public string Name { get; set; } public string Code { get; set; } }

image

All 19 comments

Can you use cli to create a project to reproduce the problem?

@maliming
Demo.zip

The main problem is that I use the FullAuditedEntityWithUser class. If I use IdentityUser, it can be migrated, but it will report an error when running, if I use Appuser, it will run normally, but it will be abnormal during migration.

What are the steps?

Add Test entity in Demo.Domain
public class Test : FullAuditedEntityWithUser<Guid, IdentityUser> { public string Name { get; set; } }
I used the FullAuditedEntityWithUser base class

Then I wrote the ConfigureDemo method in OnModelCreating

public static void ConfigureDemo(this ModelBuilder builder) { Check.NotNull(builder, nameof(builder)); builder.Entity<Test>(b => { b.ToTable("Tests"); b.Property(nameof(Test.Name)).IsRequired(false).HasMaxLength(200); b.ConfigureFullAudited(); }); } }
Then perform the migration, everything is normal
Then execute the GetListAsync method in the TestApplicationService in the Demo.Application project, you will get an error message

The entity type 'IdentityUserLogin' requires a primary key to be defined. If you intended to use a keyless entity type call 'HasNoKey()'.
System.InvalidOperationException: The entity type 'IdentityUserLogin' requires a primary key to be defined. If you intended to use a keyless entity type call 'HasNoKey()'.

@maliming
If I change FullAuditedEntityWithUser to FullAuditedEntityWithUser , it works fine, but adding the migration again after changing the entity properties will be abnormal

Then perform the migration, everything is normal
Then execute the GetListAsync method in the TestApplicationService in the Demo.Application project, you will get an error message

I can't reproduce it.

@maliming
You can open the demo project that I uploaded. It has been written and reproduced. You can update-databse directly, and then run GetListAsync in the Test service

Hi @maliming , I can reproduce this issue.
AppUser

PM> update-database
Build started...
Build succeeded.
System.InvalidOperationException: The property 'AppUser.ExtraProperties' could not be mapped, because it is of type 'Dictionary' which is not a supported primitive type or a valid entity type. Either explicitly map this property, or ignore it using the '[NotMapped]' attribute or by using 'EntityTypeBuilder.Ignore' in 'OnModelCreating'.
at Microsoft.EntityFrameworkCore.Infrastructure.ModelValidator.ValidatePropertyMapping(IModel model, IDiagnosticsLogger1 logger) at Microsoft.EntityFrameworkCore.Infrastructure.ModelValidator.Validate(IModel model, IDiagnosticsLogger1 logger)
at Microsoft.EntityFrameworkCore.Infrastructure.RelationalModelValidator.Validate(IModel model, IDiagnosticsLogger1 logger) at Microsoft.EntityFrameworkCore.SqlServer.Internal.SqlServerModelValidator.Validate(IModel model, IDiagnosticsLogger1 logger)
at Microsoft.EntityFrameworkCore.Metadata.Conventions.ValidatingConvention.ProcessModelFinalized(IConventionModelBuilder modelBuilder, IConventionContext1 context) at Microsoft.EntityFrameworkCore.Metadata.Conventions.Internal.ConventionDispatcher.ImmediateConventionScope.OnModelFinalized(IConventionModelBuilder modelBuilder) at Microsoft.EntityFrameworkCore.Metadata.Conventions.Internal.ConventionDispatcher.OnModelFinalized(IConventionModelBuilder modelBuilder) at Microsoft.EntityFrameworkCore.Metadata.Internal.Model.FinalizeModel() at Microsoft.EntityFrameworkCore.ModelBuilder.FinalizeModel() at Microsoft.EntityFrameworkCore.Infrastructure.ModelSource.CreateModel(DbContext context, IConventionSetBuilder conventionSetBuilder) at Microsoft.EntityFrameworkCore.Infrastructure.ModelSource.GetModel(DbContext context, IConventionSetBuilder conventionSetBuilder) at Microsoft.EntityFrameworkCore.Internal.DbContextServices.CreateModel() at Microsoft.EntityFrameworkCore.Internal.DbContextServices.get_Model() at Microsoft.EntityFrameworkCore.Infrastructure.EntityFrameworkServicesBuilder.<>c.<TryAddCoreServices>b__7_3(IServiceProvider p) at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitFactory(FactoryCallSite factoryCallSite, RuntimeResolverContext context) at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteVisitor2.VisitCallSiteMain(ServiceCallSite callSite, TArgument argument)
at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitCache(ServiceCallSite callSite, RuntimeResolverContext context, ServiceProviderEngineScope serviceProviderEngine, RuntimeResolverLock lockType)
at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitScopeCache(ServiceCallSite singletonCallSite, RuntimeResolverContext context)
at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteVisitor2.VisitCallSite(ServiceCallSite callSite, TArgument argument) at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitConstructor(ConstructorCallSite constructorCallSite, RuntimeResolverContext context) at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteVisitor2.VisitCallSiteMain(ServiceCallSite callSite, TArgument argument)
at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitCache(ServiceCallSite callSite, RuntimeResolverContext context, ServiceProviderEngineScope serviceProviderEngine, RuntimeResolverLock lockType)
at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitScopeCache(ServiceCallSite singletonCallSite, RuntimeResolverContext context)
at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteVisitor2.VisitCallSite(ServiceCallSite callSite, TArgument argument) at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.Resolve(ServiceCallSite callSite, ServiceProviderEngineScope scope) at Microsoft.Extensions.DependencyInjection.ServiceLookup.DynamicServiceProviderEngine.<>c__DisplayClass1_0.<RealizeService>b__0(ServiceProviderEngineScope scope) at Microsoft.Extensions.DependencyInjection.ServiceLookup.ServiceProviderEngine.GetService(Type serviceType, ServiceProviderEngineScope serviceProviderEngineScope) at Microsoft.Extensions.DependencyInjection.ServiceLookup.ServiceProviderEngineScope.GetService(Type serviceType) at Microsoft.Extensions.DependencyInjection.ServiceProviderServiceExtensions.GetRequiredService(IServiceProvider provider, Type serviceType) at Microsoft.Extensions.DependencyInjection.ServiceProviderServiceExtensions.GetRequiredService[T](IServiceProvider provider) at Microsoft.EntityFrameworkCore.DbContext.get_DbContextDependencies() at Microsoft.EntityFrameworkCore.DbContext.get_InternalServiceProvider() at Microsoft.EntityFrameworkCore.DbContext.Microsoft.EntityFrameworkCore.Infrastructure.IInfrastructure<System.IServiceProvider>.get_Instance() at Microsoft.EntityFrameworkCore.Infrastructure.Internal.InfrastructureExtensions.GetService[TService](IInfrastructure1 accessor)
at Microsoft.EntityFrameworkCore.Infrastructure.AccessorExtensions.GetServiceTService
at Microsoft.EntityFrameworkCore.Design.Internal.DbContextOperations.CreateContext(String contextType)
at Microsoft.EntityFrameworkCore.Design.Internal.MigrationsOperations.UpdateDatabase(String targetMigration, String contextType)
at Microsoft.EntityFrameworkCore.Design.OperationExecutor.UpdateDatabaseImpl(String targetMigration, String contextType)
at Microsoft.EntityFrameworkCore.Design.OperationExecutor.UpdateDatabase.<>c__DisplayClass0_0.<.ctor>b__0()
at Microsoft.EntityFrameworkCore.Design.OperationExecutor.OperationBase.Execute(Action action)
The property 'AppUser.ExtraProperties' could not be mapped, because it is of type 'Dictionary' which is not a supported primitive type or a valid entity type. Either explicitly map this property, or ignore it using the '[NotMapped]' attribute or by using 'EntityTypeBuilder.Ignore' in 'OnModelCreating'.
PM>

The property 'AppUser.ExtraProperties' could not be mapped, because it is of type 'Dictionary' which is not a supported primitive type or a valid entity type. Either explicitly map this property, or ignore it using the '[NotMapped]' attribute or by using 'EntityTypeBuilder.Ignore' in 'OnModelCreating'.

According to the current design, you cannot use AppUser in EntityFrameworkCore.DbMigrations.

AppUserand IdentityUserare shared tables, you can use IdentityUser(FullAuditedEntityWithUser<Guid, IdentityUser>).

The property 'AppUser.ExtraProperties' could not be mapped, because it is of type 'Dictionary' which is not a supported primitive type or a valid entity type. Either explicitly map this property, or ignore it using the '[NotMapped]' attribute or by using 'EntityTypeBuilder.Ignore' in 'OnModelCreating'.

According to the current design, you cannot use AppUser in EntityFrameworkCore.DbMigrations.

AppUserand IdentityUserare shared tables, you can use IdentityUser(FullAuditedEntityWithUser<Guid, IdentityUser>).

Yes, I see, there are also some issues using IdentityUser, I'll try to reproduce to you.

Hi @maliming , I have reproduced the issue, please down the demo and do below steps:

  1. Extract the zip file
  2. Run "Update-Database"
  3. Debug Demo.HttpApi.Host project
  4. Execute ​/api​/app​/test
  5. Debug function public override Task> GetListAsync(bool includeDetails = false, CancellationToken cancellationToken = new CancellationToken()) in TestRepository.cs file

IdentityUser
image

DemoIdentityUser_2.zip

@GerryGe That's the problem

@XuJin186 @GerryGe

It is not recommended that you use Entity in other modules as the navigation property, because the module entity may exist in another database. Of course, you can use the Id of the entity.

Even if all modules exist in a database, EF Core will still throw an exception because of different DbContext, such as the above exception information. (IdentityUserLogin 'requires a primary key to be defined.). The configuration of the entity is associated with DbContext.

Maybe classes like FullAuditedEntityWithUsershould be removed from the framework.

In short, don't use FullAuditedEntityWithUser but use the Id of User entity to query User's information.

@maliming Do you have any idea or background of why define these *WithUser class?

Maybe classes like FullAuditedEntityWithUser should be removed from the framework.

@hikalkan what do you think?

I implemented it and sending the modified source code:

demo-modified.zip

As a general principle; MigrationDbContext should not know the AppUser and ProjectDbContext should not know the IdentityUser. You must ignore related properties where needed.

DemoDbContext

````csharp
protected override void OnModelCreating(ModelBuilder builder)
{
base.OnModelCreating(builder);

builder.Entity<AppUser>(b =>
{
    b.ToTable("User", AbpIdentityDbProperties.DbTablePrefix + "Users"); //Sharing the same table "AbpUsers" with the IdentityUser

    b.ConfigureByConvention();
    b.ConfigureAbpUser();
});

builder.ConfigureDemo(); //Shared code between db contexts

builder.Entity<Test>(b =>
{
    //SET RELATIONS FOR THE PROJECT DBCONTEXT

    b.HasOne(x => x.Creator).WithMany().IsRequired(false);
    b.HasOne(x => x.LastModifier).WithMany().IsRequired(false);
    b.HasOne(x => x.Deleter).WithMany().IsRequired(false);
});

}
````

DemoMigrationsDbContext

````csharp
protected override void OnModelCreating(ModelBuilder builder)
{
base.OnModelCreating(builder);

/* Include modules to your migration db context */

builder.ConfigurePermissionManagement();
builder.ConfigureSettingManagement();
builder.ConfigureBackgroundJobs();
builder.ConfigureAuditLogging();
builder.ConfigureIdentity();
builder.ConfigureIdentityServer();
builder.ConfigureFeatureManagement();
builder.ConfigureTenantManagement();

//IGNORE AppUser related properties

builder.Entity<Test>(b =>
{
    b.Ignore(x => x.Creator);
    b.Ignore(x => x.LastModifier);
    b.Ignore(x => x.Deleter);
});

builder.ConfigureDemo(); //Shared code between db contexts

}
````

Test entity

csharp public class Test : FullAuditedEntityWithUser<Guid, AppUser> { public string Name { get; set; } public string Code { get; set; } }

This is how we can add navigation properties to an entity defined in a module.

@maliming, removing FullAuditedEntityWithUser doesn't solve problems, because people will then add navigation properties themselves. But I also don't like this class much to be honest :)

After my testing, the solution is not available, I made some changes:

DemoDbContext

````csharp
protected override void OnModelCreating(ModelBuilder builder)
{
base.OnModelCreating(builder);

builder.Entity<AppUser>(b =>
{
    b.ToTable(AbpIdentityDbProperties.DbTablePrefix + "Users"); //Sharing the same table "AbpUsers" with the IdentityUser

    b.ConfigureByConvention();
    b.ConfigureAbpUser();
});

builder.ConfigureDemo(); //Shared code between db contexts

builder.Entity<Test>(b =>
{
    //SET RELATIONS FOR THE PROJECT DBCONTEXT

    b.HasOne(x => x.Creator).WithMany().HasForeignKey("CreatorId").IsRequired(false);
    b.HasOne(x => x.LastModifier).WithMany().HasForeignKey("LastModifierUserId").IsRequired(false);
    b.HasOne(x => x.Deleter).WithMany().HasForeignKey("DeleterUserId").IsRequired(false);
});

}
````

DemoDbContextModelCreatingExtensions

public static class DemoDbContextModelCreatingExtensions
{
    public static void ConfigureDemo(this ModelBuilder builder)
    {
        Check.NotNull(builder, nameof(builder));

        builder.Entity<Test>(b =>
        {
            b.ToTable("Tests");

            b.ConfigureByConvention();

            b.Property(x => x.Name).IsRequired(false).HasMaxLength(200);
        });

        /* Configure your own tables/entities inside here */

        //builder.Entity<YourEntity>(b =>
        //{
        //    b.ToTable(DemoConsts.DbTablePrefix + "YourEntities", DemoConsts.DbSchema);

        //    //...
        //});
    }
}

DemoMigrationsDbContext

````csharp
protected override void OnModelCreating(ModelBuilder builder)
{
base.OnModelCreating(builder);

/* Include modules to your migration db context */

builder.ConfigurePermissionManagement();
builder.ConfigureSettingManagement();
builder.ConfigureBackgroundJobs();
builder.ConfigureAuditLogging();
builder.ConfigureIdentity();
builder.ConfigureIdentityServer();
builder.ConfigureFeatureManagement();
builder.ConfigureTenantManagement();

//IGNORE AppUser related properties

builder.Entity<Test>(b =>
{
      b.Ignore(x => x.Creator);
      b.Ignore(x => x.LastModifier);
      b.Ignore(x => x.Deleter);

       b.HasOne<IdentityUser>().WithMany().HasForeignKey("CreatorId").IsRequired(false);
       b.HasOne<IdentityUser>().WithMany().HasForeignKey("LastModifierUserId").IsRequired(false);
       b.HasOne<IdentityUser>().WithMany().HasForeignKey("DeleterUserId").IsRequired(false);
 });

builder.ConfigureDemo(); //Shared code between db contexts

}
````

Test entity

csharp public class Test : FullAuditedEntityWithUser<Guid, AppUser> { public string Name { get; set; } public string Code { get; set; } }

image

hi @albutta

Please create a new issue.

if it's related to abp commercial please get support via the support website.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

hikalkan picture hikalkan  Â·  3Comments

zsanhong picture zsanhong  Â·  3Comments

hikalkan picture hikalkan  Â·  3Comments

vfabregat picture vfabregat  Â·  3Comments

wakuflair picture wakuflair  Â·  3Comments