Open Closed

Unable To Define Many-To-Many IdentityUser Relationship #2954


0
riley.trevillion created
  • ABP Framework version: v5.2.0
  • UI type: Angular
  • DB provider: EF Core
  • Tiered (MVC) or Identity Server Separated (Angular): no

Hi

I'm attempting to add a many-to-many relationship between my aggregate root and the built in IdentityUser class using the EF Core Fluent API. I'm am having trouble defining this relationship.

Once the relationship is defined it is my intention to be able to pull a user out of the database and '.Include()' the navigation property defined on the IdentityUser to get a list of ActivityInstances they are part of (and vice versa).

Could you please advise on what I am doing wrong and whether I need to approach this differently to have a many-to-many with IdentityUser or whether this is a bug and that I should be able to reference the navigation property defined via the EntityExtensionMapping in the Fluent API?

ActivityInstance (my aggregate root)

  • AssociatedBookings is the navigation property

SchedulerEfCoreEntityExtensionMappings

  • This is to add the navigation property to the IdentityUser
  • The const string value is "AssociatedInstances"

SchedulerDbContextModelCreatingExtensions

  • The configuration block gives me an error (see screenshot) if I attempt to configure the relationship with a string value

  • I am unable to select the property defined in the SchedulerEfCoreEntityExtensionMappings file if I attempt to use the typical lambda method of selecting the property instead

My understanding is that EF Core will automatically produce the joining table with the appropriate foreign keys configured. This works as intended with other many-to-many relationships I have defined between my other entities where I have direct control over the navigation properties defined in those classes as shown here

I'm aware that the property definition in the SchedulerEfCoreEntityExtensionMappings file might interfer with how EF Core automatically configures the joining table with the appropriate foreign keys but I'm not sure.

Thankyou


8 Answer(s)
  • 0
    riley.trevillion created

    Hello

    Just checking in to see if there are any updates on this one?

    Thankyou

  • 0
    albert created
    Support Team

    hi Riley, sorry for the delay this is waiting in our test phase. I'll let you know once we determine the issue.

  • 0
    riley.trevillion created

    The service bot automatically closed this issue. Bumping to reopen it.

    Has there been any further progress on this one since last week?

    Thankyou

  • 0
    albert created
    Support Team

    did you try to use ABP Suite the latest version. ABP Suite supports many to many relationships and you can see how it's generating the source code with best practises.

  • 0
    riley.trevillion created

    Hi @albert

    I've generated a sample TestEntity -> IdentityUser many to many relationship using the latest version of ABP Suite (5.2) and observed the outputed code. It appears to be setting up the relationship using the more traditional method of manually defining the joining entity, joining properties, database configuration etc.

    EFCore 6 allows us to define many to many relationships between 2 entities directly in Fluent API without needing to manually define all the boilerplate code that the ABP Suite is creating to faciliate a many to many relationship. For reference please see Microsoft documentation here

    • https://docs.microsoft.com/en-us/ef/core/modeling/relationships?tabs=fluent-api%2Cfluent-api-simple-key%2Csimple-key#many-to-many

    In my situation the ABP Suite generated code in the screenshot above would simply be a 'HasMany()' -> 'WithMany()' definition i.e 2-3 lines of code without needing any joining entity defined. I'm trying to utilise this by defining the joins like you can see in this Microsoft documentation link and in the screenshots in my comment when first opening this issue.

    • https://docs.microsoft.com/en-us/ef/core/modeling/relationships?tabs=fluent-api%2Cfluent-api-simple-key%2Csimple-key#join-entity-type-configuration

    The error when defining the join property using the EfCoreEntityExtensionMappings feature on the IdentityUser entity is what appears to be causing my issue.

    I'd like to not have to manually define the join entities and all the boilerplate that goes with that when there's a supported feature of EFCore 6 that lets us do many to many in a very simple and clean way.

    Additionally (and quite importantly for our requirements), using the ABP Suite to generate the code doesn't seem to allow me to pull a list of users out the database and access the associated entities on the join property (TestEntities) as it doesn't exist in this implementation. The way I'm trying to do it (theoretically) should allow me to do this.

    Should EfCoreEntityExtensionMappings support the ability to define a navigation property on IdentityUser (i.e my issue is a bug), or is this an enhancement that could be made to support this?

    Thankyou

  • 0
    albert created
    Support Team

  • 1
    hikalkan created
    Support Team

    Hi,

    IdentityUser class is not in your application, it comes from the Identity module as you know. So, you can not directly change its source code to add a navigation collection (unless you include Identity modules's source code into your solution).

    So, when you don't add ICollection<T> to the IdentityUser, there is no way to define many-to-many relationship as you want. Not possible to do it with fluent api and defining collection only in one side. EF Core doesn't allow to it. You can see these links for more info:

    • https://stackoverflow.com/questions/66790470/can-many-to-many-relationships-in-ef-core-5-0-be-configured-keeping-just-one-nav
    • https://github.com/dotnet/efcore/issues/3864

    The best solution I can suggest is to manually create the relation entity. I tried with the following entities:

    public class Book : AggregateRoot<int>
    {
        public string Name { get; set; }
    
        public Collection<UserBook> UserBooks { get; set; }
    }
    
    public class UserBook : BasicAggregateRoot<int>
    {
        public IdentityUser User { get; set; }
        public Book Book { get; set; }
    }
    

    My purpose is to establish a many-to-many relation between IdentityUser and Book entities. So, I defined UserBook as a relation entity. Then mapped it like that:

    builder.Entity<UserBook>(b =>
    {
        b.ToTable("UserBooks");
        b.ConfigureByConvention();
    
        b.HasOne(x => x.User).WithMany().HasForeignKey("UserId");
        b.HasOne(x => x.Book).WithMany(x => x.UserBooks).HasForeignKey("BookId");
    });
    

    The relation table is created when I add a new migration. So, now you can include Users while querying books. However, you can not do vice verse. You should separately query books of a user or perform a LINQ JOIN query yourself since Include won't work (no navigation collection on the IdentityUser entity).

  • 0
    riley.trevillion created

    @albert @hikalkan

    I see. Thankyou for clarifying what needs to be done. It is a shame that I can't define a navigation property on the IdentityUser entity given how much it would simplify things. Perhaps it could be a future feature or enhancement? Nevertheless, I have manually created the joining entity based on the code provided to solve my problem.

    Thankyou again