1

I add identity to my project.

Models :

 [Table("AspNetRoles")]
public class ApplicationRole : IdentityRole
{
    [StringLength(100)]
    public string PersianName { get; set; }

    public string? ParentId { get; set; }

    [ForeignKey("ParentId")]
    public ApplicationRole Parent { get; set; }

    public virtual ICollection<ApplicationUserRole> Users { get; set; }

    public virtual ICollection<IdentityRoleClaim<string>> Claims { get; set; }

    public ApplicationRole()
    {
        Id = Guid.NewGuid().ToString();
    }
    public ApplicationRole(string name, string persianName = "", string parentId = null)
    {
        Name = name;
        PersianName = persianName;
        ParentId = parentId;
    }

}



[Table("AspNetUsers")]
public class ApplicationUser : IdentityUser
{
    public ApplicationUser()
    {
        Id = Guid.NewGuid().ToString();
    }
    [Required(AllowEmptyStrings = false, ErrorMessageResourceType = typeof(ValidationResource), ErrorMessageResourceName = "Required")]
    [Display(Name = "Name", ResourceType = typeof(ApplicationUserResource))]
    [StringLength(50, ErrorMessageResourceType = typeof(ValidationResource), ErrorMessageResourceName = "StringLength")]
    public string Name { get; set; }

    [Required(AllowEmptyStrings = false, ErrorMessageResourceType = typeof(ValidationResource), ErrorMessageResourceName = "Required")]
    [Display(Name = "Family", ResourceType = typeof(ApplicationUserResource))]
    [StringLength(70, ErrorMessageResourceType = typeof(ValidationResource), ErrorMessageResourceName = "StringLength")]
    public string Family { get; set; }

    [Required(AllowEmptyStrings = false, ErrorMessageResourceType = typeof(ValidationResource), ErrorMessageResourceName = "Required")]
    [Display(Name = "Email", ResourceType = typeof(ApplicationUserResource))]
    [StringLength(150, ErrorMessageResourceType = typeof(ValidationResource), ErrorMessageResourceName = "StringLength")]
    public string Email { get; set; }

    [Display(Name = "Picture", ResourceType = typeof(ApplicationUserResource))]
    public string Picture { get; set; } = "";

    [Display(Name = "Region", ResourceType = typeof(ApplicationUserResource))]
    public Region Region { get; set; }

    [Display(Name = "Language", ResourceType = typeof(ApplicationUserResource))]
    public Language Language { get; set; }

    [Required(AllowEmptyStrings = false, ErrorMessageResourceType = typeof(ValidationResource), ErrorMessageResourceName = "Required")]
    [Display(Name = "Mobile", ResourceType = typeof(ApplicationUserResource))]
    [StringLength(12, ErrorMessageResourceType = typeof(ValidationResource), ErrorMessageResourceName = "StringLength")]
    public string Mobile { get; set; }

    public bool AllowDelete { get; set; }
    public bool AllowUpdate { get; set; }

    public Guid CreatedUser { get; set; } = Guid.Empty;

    public DateTime CreatedDate { get; set; } = DateTime.Now;

    public virtual ICollection<ApplicationUserRole> Roles { get; set; }
    public virtual ICollection<IdentityUserClaim<string>> Claims { get; set; }


    [NotMapped]
    public string FullName
    {
        get
        {
            var displayName = $"{Name} {Family}";
            return string.IsNullOrWhiteSpace(displayName) ? UserName : displayName;
        }
    }


}

 [Table("AspNetUserRoles")]
public class ApplicationUserRole : IdentityUserRole<string>
{
    
    public virtual ApplicationUser User { get; set; }

    public virtual ApplicationRole Role { get; set; }
}

and in dbontext

 public class ApplicationDbContext : ApplicationApiAuthorizationDbContext<ApplicationUser, ApplicationRole>, IApplicationDbContext
{
    private readonly IMediator _mediator;
    private readonly AuditableEntitySaveChangesInterceptor _auditableEntitySaveChangesInterceptor;

    public ApplicationDbContext(
        DbContextOptions<ApplicationDbContext> options,
        IOptions<OperationalStoreOptions> operationalStoreOptions,
        IMediator mediator,
        AuditableEntitySaveChangesInterceptor auditableEntitySaveChangesInterceptor)
        : base(options, operationalStoreOptions)
    {
        _mediator = mediator;
        _auditableEntitySaveChangesInterceptor = auditableEntitySaveChangesInterceptor;
    }

 protected override void OnModelCreating(ModelBuilder builder)
    {
        builder.ApplyConfigurationsFromAssembly(Assembly.GetExecutingAssembly());
        builder.Entity<ApplicationUserRole>(userRole =>
        {
            userRole.HasOne(ur => ur.Role)
                .WithMany(r => r.Users)
                .HasForeignKey(ur => ur.RoleId)
                .IsRequired();

            userRole.HasOne(ur => ur.User)
                .WithMany(r => r.Roles)
                .HasForeignKey(ur => ur.UserId)
                .IsRequired();
        });

        base.OnModelCreating(builder);
    }

but when i add-migration. created ApplicationUserRole with UserId,RoleId,UserId1,RoleId1. and get warning

The foreign key property 'ApplicationUserRole.RoleId1' was created in shadow state because a conflicting property with the simple name 'RoleId' exists in the entity type, but is either not mapped, is already used for another relationship, or is incompatible with the associated primary key type. See https://aka.ms/efcore-relationships for information on mapping relationships in EF Core.

ar.gorgin
  • 4,765
  • 12
  • 61
  • 100
  • To answer to your question, someone should write a very long article about "How to customize the ASP.NET Core Identity". This is a tiny part of it: https://stackoverflow.com/questions/45127666/asp-net-core-custom-aspnetcore-identity-implementation-not-working/45160657#45160657 or you can use this highly customized solution and stop wasting your time: https://github.com/VahidN/DNTIdentity – VahidN Sep 12 '22 at 04:29
  • Thank, i use https://stackoverflow.com/questions/51004516/net-core-2-1-identity-get-all-users-with-their-associated-roles/71321924#71321924 for custom identity and set custom service like yout article , but when add migration, it add two fields(UserId1,RoleId1). – ar.gorgin Sep 12 '22 at 06:07

1 Answers1

1

Identity already defines the relationship between those entities without navigation. You are trying to configure using navigation properties.

You are defining relationships with navigations, then calling the base method. This will override the FK property configuration which you are using, so the FK you configured will go to the relationship without navigations. Since the navigations you defined are still in the model, conventions will add another relationship for that and create shadow FK properties for them. Essentially Identity overwrote the configuration you did and your navigations caused extra relationships by convention. The best way to override Identity configuration is by calling base.OnModelCreating() method first before writing your own configuration code.

Omer
  • 8,194
  • 13
  • 74
  • 92