Open Closed

Cross Module Entity Reference in ABP Suite #10812


User avatar
0
johnsonlim created

Hi All, I have 2 modules in ABP Suites: Master Data Module and Transaction Module . I have all the master data entity and its CRUD page created in Master Data Module. Now i want to create transaction entity, which its attribute requiring reference to the data entity in Master Data Module. i find out that the data entity created in Master Data Module cannot be found in Transaction Module as per the picture.Can you advise what to do? This is the company entity created in Master Data Module.

This is the the page where i want to create data entity and its reference page in Transaction module. i cannot see company entity at all.

Markdown supported.
Copy, paste, or drag & drop images and files (max 100 MB per file, 100 MB total per post)

24 Answer(s)
  • User Avatar
    0
    AI-Bot created
    Support Team ABP Agent
    • In ABP Suite, the “Entity” dropdown in Define a navigation property is populated by scanning the compiled assemblies of the currently selected module/solution. It does not automatically list entities that belong to a different module (like your “Master Data” module) unless that module is referenced and its assemblies are discoverable to Suite in the current solution context.
    • ABP Suite only has an explicit checkbox for built-in ABP modules (Include entities from ABP modules)—it doesn’t have a similar “include entities from other custom modules” option in that dialog.

    What to do (recommended, DDD-friendly approach):

    1. In the Transaction module, store only the FK as a value (e.g., CompanyId) and do not add an EF Core navigation to the Company aggregate from Master Data.
      • Example:
    public class Transaction : AggregateRoot<Guid>
         {
             public Guid CompanyId { get; private set; }  // Reference by Id only
             // ...
         }
    
    1. For UI selection, use a lookup endpoint from the Master Data module (an application service returning Id + display fields) and consume it from the Transaction UI (Suite can generate lookups when the relationship is within the same module; for cross-module you typically wire the lookup manually).

    If you still want a real navigation property (same DbContext scenario):

    1. Ensure Transaction solution/module references the Master Data module projects (Domain + EFCore as needed) so types are available at compile time.
    2. Build the solution successfully (Suite’s entity scanning requires a successful build).
    3. Then try defining the navigation again. If Suite still doesn’t list it, define the relationship manually in code (EF Core mapping + DTO/UI yourself). Suite’s navigation-property UI is primarily designed for entities inside the same module/solution plus built-in ABP modules.

    Sources:

    • https://abp.io/docs/latest/suite/generating-crud-page
    • https://abp.io/community/articles/furp75ex

    This response was generated by AI. If it didn’t solve your problem, feel free to post a new answer — our support team will jump in to help you soon.

    Markdown supported.
    Copy, paste, or drag & drop images and files (max 100 MB per file, 100 MB total per post)
  • User Avatar
    0
    johnsonlim created

    Yes, i want real navigation property. Please tell me how to do so

    Markdown supported.
    Copy, paste, or drag & drop images and files (max 100 MB per file, 100 MB total per post)
  • User Avatar
    0
    AI-Bot created
    Support Team ABP Agent
    • To create a real EF Core navigation property from an entity in your Transaction module to an entity in your Master Data module using ABP Suite, you must make ABP Suite see the Master Data module’s entity types. ABP Suite can do this by scanning extra module assemblies via /.suite/module-entity-extension.json.

    1) Make the Transaction solution depend on the Master Data module (compile-time)

    In Visual Studio (or your IDE), add project references so the Transaction side can compile against Master Data types:

    • Transaction.Domain → reference MasterData.Domain
    • Transaction.EntityFrameworkCore → reference MasterData.EntityFrameworkCore (and/or MasterData.Domain as needed)
    • Also ensure your Transaction module has the proper [DependsOn(...)] module dependency on the Master Data module (Domain/EfCore module) so runtime registration is correct.

    Then build the whole solution successfully.

    2) Tell ABP Suite to include your custom module entities

    In the root of the solution you open with ABP Suite, create/edit this file:

    /.suite/module-entity-extension.json

    Put the Master Data domain assembly name there (the DLL name ABP Suite should scan):

    {
      "Modules": [
        {
          "DomainProjectDllFileName": "MyCompany.MyProject.MasterData.Domain.dll"
        }
      ]
    }
    

    Notes:

    • Use the exact DLL file name produced by the Master Data Domain project.
    • After editing, rebuild the solution, then restart ABP Suite and reopen the solution.

    3) Add the navigation in ABP Suite (Transaction entity)

    Now go back to the Transaction entity → Navigation properties tab → Add navigation property. At this point, the Company entity from Master Data should appear in the Entity dropdown (because Suite can now scan that module’s domain DLL).

    4) Generate and migrate

    Click Save and generate, then:

    • Add a migration in your DbMigrator / EF Core project
    • Run DbMigrator to apply the FK and relationship changes.

    Sources

    • https://abp.io/docs/latest/suite/generating-crud-page (see “Extending with Custom Module Entities” / module-entity-extension.json)
    • https://abp.io/docs/10.4/suite/generating-crud-page (same section, versioned)

    This response was generated by AI. If it didn’t solve your problem, feel free to post a new answer — our support team will jump in to help you soon.

    Markdown supported.
    Copy, paste, or drag & drop images and files (max 100 MB per file, 100 MB total per post)
  • User Avatar
    0
    maliming created
    Support Team Fullstack Developer

    Hi,

    The Entity dropdown is empty because Company lives in a different module (Master Data). By default Suite only lists entities from your Transaction solution itself, so entities from your other custom modules won't show up until you tell Suite to scan them.

    In your Transaction module solution:

    1. Reference the Master Data module (its *.Domain project/package) and build the solution, so the Master Data Domain DLL ends up in the build output under src.

    2. Add a .suite/module-entity-extension.json file at the solution root:

    {
      "Modules": [
        {
          "DomainProjectDllFileName": "YourCompany.MasterData.Domain.dll"
        }
      ]
    }
    
    1. Open the navigation property dialog and check Include entities from ABP modules (Suite builds the solution first if it isn't already built). It'll scan that DLL and list Company in the Entity dropdown.

    A note on what Suite generates here: on the transaction entity it adds a CompanyId foreign key rather than a Company navigation property on the aggregate itself, which follows ABP's guidance of referencing other aggregate roots by id. It still wires up a real EF Core relationship for that key (HasOne<Company>().WithMany().HasForeignKey(x => x.CompanyId)) and generates a separate ...WithNavigationProperties type that joins to Master Data to show the company fields on the list/detail pages.

    Because that relationship is real, both entities have to live in the same database and the Transaction side's DbContext has to map Company (i.e. the Transaction app references the Master Data module). So this fits a modular-monolith setup, not two independently deployed modules with separate databases.

    Docs: https://abp.io/docs/latest/suite/generating-crud-page (see the "Extending with Custom Module Entities" section).

    Thanks

    Markdown supported.
    Copy, paste, or drag & drop images and files (max 100 MB per file, 100 MB total per post)
  • User Avatar
    0
    johnsonlim created

    thanks maliming :)

    Markdown supported.
    Copy, paste, or drag & drop images and files (max 100 MB per file, 100 MB total per post)
  • User Avatar
    0
    johnsonlim created

    Hi, I successfully create the linkage and able to choose the entity from ABP and my master data module. But then i am not able to click ok button to proceed.Please advise

    Markdown supported.
    Copy, paste, or drag & drop images and files (max 100 MB per file, 100 MB total per post)
  • User Avatar
    0
    maliming created
    Support Team Fullstack Developer

    Hi,

    The OK button is disabled because the dialog needs a Display property and there's none to pick for Company. That's the property Suite uses as the visible text in the generated dropdown, and it's required — without it the button stays off.

    The catch is that Suite only offers string properties declared directly on the entity itself as the display property. Inherited ones (like the base ConcurrencyStamp) don't count, which is why Company still shows up in the entity list but the Display property field is empty. Your Company entity doesn't have its own string property, so there's nothing to select.

    To fix it, give Company a string property (e.g. Name) in the Master Data module, rebuild that module so the DLL your Transaction solution references is up to date, then reopen the dialog. Select Company again, pick the display property, and OK will be enabled.

    Thanks

    Markdown supported.
    Copy, paste, or drag & drop images and files (max 100 MB per file, 100 MB total per post)
  • User Avatar
    0
    johnsonlim created

    Hi Maliming, I do have the company entity read in Master Data module and the attribute actually contains company name.Can you advise?

    Markdown supported.
    Copy, paste, or drag & drop images and files (max 100 MB per file, 100 MB total per post)
  • User Avatar
    0
    maliming created
    Support Team Fullstack Developer

    Hi,

    Thanks, that screenshot helps. CompanyName is there in the entity, so the source is fine.

    The thing is, this dialog doesn't read your entity source — it loads the compiled WGBS_Connect.Domain.dll and reflects over it to build the Display property list. And Suite loads that DLL once per session and keeps it cached, so if it picked up an older build of the DLL (one from before CompanyName was added), it'll keep using that until Suite is restarted — which would leave the Display property empty and the OK button disabled, exactly like you're seeing.

    Could you try this:

    1. Rebuild the Master Data module and make sure the WGBS_Connect.Domain.dll under your Transaction solution's src folder is the fresh one that includes CompanyName (if you reference the module as a NuGet package, repack/reinstall it, not just rebuild the source).
    2. Fully close ABP Suite and reopen it, then open the navigation property dialog again and select Company.

    If CompanyName still doesn't show up as a display property after that, send me the compiled WGBS_Connect.Domain.dll (or a small repro project) at liming.ma@volosoft.com and I'll check it directly.

    Thanks

    Markdown supported.
    Copy, paste, or drag & drop images and files (max 100 MB per file, 100 MB total per post)
  • User Avatar
    0
    johnsonlim created

    Hi Maliming, Learn time both dll are of the same. I have email you^^

    Markdown supported.
    Copy, paste, or drag & drop images and files (max 100 MB per file, 100 MB total per post)
  • User Avatar
    0
    maliming created
    Support Team Fullstack Developer

    Hi,

    Your email hasn't reached me yet — could you send it through https://wetransfer.com instead (to liming.ma@volosoft.com)? Attachments on the support thread and email sometimes get stripped.

    To pin this down, the most useful thing would be the Transaction solution that reproduces it (a trimmed-down copy is fine). If you'd rather not share the whole thing, these two are enough:

    1. The compiled WGBS_Connect.Domain.dll — the exact copy under your Transaction solution's src folder, since that's the one the dialog loads.
    2. The Company entity source file(s), including any base or partial class if Suite split it (e.g. Company.cs plus a ...Base.cs).

    With those I can load the same DLL and see exactly why CompanyName isn't offered as a display property.

    Thanks

    Markdown supported.
    Copy, paste, or drag & drop images and files (max 100 MB per file, 100 MB total per post)
  • User Avatar
    0
    johnsonlim created

    Hi Maliming, I have just resend the email to you with the the links to download dll file from we transfer. Can you try?

    Markdown supported.
    Copy, paste, or drag & drop images and files (max 100 MB per file, 100 MB total per post)
  • User Avatar
    0
    maliming created
    Support Team Fullstack Developer

    Hi,

    Thanks for sending the DLL — that made it clear, and this is a bug on our side in ABP Suite.

    When you reference an entity from another module, the Basic tab builds the Display property list only from properties declared directly on the entity class. Suite generated your entity's properties on the CompanyBase class (your concrete Company inherits from it), so that list comes back empty, the Display property dropdown doesn't render, and the OK button never enables.

    You can get past this right now without changing the entity:

    1. Select Company.
    2. Switch to the Advanced tab.
    3. In the Display property field there, type CompanyName (it's a free-text input, so it doesn't depend on that scan).
    4. Click OK.

    That fills in the display property, the rest of the fields are already set, and the generated lookup will use the real CompanyName column — no extra property or database change needed.

    We'll fix the Basic tab scan so it also picks up properties from the generated ...Base class.

    Thanks

    Markdown supported.
    Copy, paste, or drag & drop images and files (max 100 MB per file, 100 MB total per post)
  • User Avatar
    0
    maliming created
    Support Team Fullstack Developer

    Hi,

    Since this turned out to be a bug on our side, I've refunded your ticket. The Advanced-tab step above will unblock you for now, and the Suite fix is on the way for an upcoming release.

    Thanks

    Markdown supported.
    Copy, paste, or drag & drop images and files (max 100 MB per file, 100 MB total per post)
  • User Avatar
    0
    johnsonlim created

    Hi Maliming, Thanks. But i have one more question .I find out that under the navigation property tab with "Import entities from ABP module checked", i only able to select entity of Master type but not child type. For my case, i cannot find Department from the list. Can you advise?

    Markdown supported.
    Copy, paste, or drag & drop images and files (max 100 MB per file, 100 MB total per post)
  • User Avatar
    0
    maliming created
    Support Team Fullstack Developer

    Hi,

    This is by design. When "Include entities from ABP modules" is enabled, Suite's module-entity picker only offers aggregate roots as navigation targets — child entities aren't listed by that picker, which is why Department doesn't show up while it's modeled as a child.

    It comes down to how Department is modeled:

    • If it's genuinely an independent concept in your domain (its own lifecycle and consistency boundary), make it an aggregate root. If it already uses a Guid key, use AggregateRoot<Guid>, AuditedAggregateRoot<Guid>, or FullAuditedAggregateRoot<Guid>, and give it a public string property like Name for the display field — then rebuild the module and restart Suite before selecting it again. (The picker reads module entity keys as Guid, so don't switch a non-Guid domain key just to make it appear.)
    • If it's really a child of another aggregate, keep it that way and work with it through its owning aggregate root (or that module's application services), instead of selecting it as a direct navigation target.

    Thanks

    Markdown supported.
    Copy, paste, or drag & drop images and files (max 100 MB per file, 100 MB total per post)
  • User Avatar
    0
    johnsonlim created

    HI Maliming, Can explain more in details how to do for 2nd options?

    Markdown supported.
    Copy, paste, or drag & drop images and files (max 100 MB per file, 100 MB total per post)
  • User Avatar
    0
    maliming created
    Support Team Fullstack Developer

    Hi,

    Quick note first: option 2 doesn't get Department into Suite's cross-module picker — that picker only lists aggregate roots. So option 2 is about how you wire it up, and the right wiring depends mostly on one thing: does a transaction need to save the selected department, or only show/pick departments?

    • If a transaction stores the chosen department (a DepartmentId on the transaction record), then Department is being referenced as an entity in its own right. In ABP that's the sign it should be modeled as an aggregate root rather than a child — decide it from the domain (does it have its own lifecycle and consistency boundary?), not just to get it into the picker. Once it's an aggregate root, Suite generates the reference the same way it did for Company, and it can still keep its owner id (e.g. CompanyId) to record which master it belongs to.
    • If departments are only shown/selected under a chosen master and never stored on the transaction, keep Department as a child. Load the list through the Master Data module — Suite already generates a service that returns a child's records by its master's id — and don't create a direct navigation to the child from the Transaction side.

    One thing that affects the answer either way: cross-module references stay clean when the Master Data module exposes what others need through its own service/contract, rather than the Transaction module reaching directly into Department's tables — this matters especially if the two modules might run as separate apps or databases.

    So I can give you concrete steps, could you tell me:

    1. Which entity owns Department (which master is it a child of)?
    2. Does a transaction need to save the selected department, or only display/pick it?
    3. Are both modules in one app sharing the same database, or separate services/databases — and is the UI MVC, Angular, or Blazor?

    Thanks

    Markdown supported.
    Copy, paste, or drag & drop images and files (max 100 MB per file, 100 MB total per post)
  • User Avatar
    0
    johnsonlim created

    Hi maliming, My reply as below.

    1. Company( Master entity) owns department (Child entity).
    2. Transaction need to save department because it is raised by company and department.
    3. Both modules using the same database and under same application. We are using blazor
    Markdown supported.
    Copy, paste, or drag & drop images and files (max 100 MB per file, 100 MB total per post)
  • User Avatar
    0
    maliming created
    Support Team Fullstack Developer

    Hi,

    Thanks — that settles it. Since a transaction has to save the department (raised per company + department), the department is referenced on its own from another module, so it should be an aggregate root rather than a child of Company. Child entities are meant to be reached only through their aggregate root, so referencing one directly from another module is what you want to avoid.

    A few practical points for the change:

    • Suite locks the entity type once an entity is saved, so you can't flip the existing Department from Child to Master in the UI. You'll recreate it as a Master entity with an aggregate-root base class (AggregateRoot<Guid>, AuditedAggregateRoot<Guid>, or FullAuditedAggregateRoot<Guid>), remove the inline department management from the Company page, and check the generated migration so you don't drop existing Department data.
    • On the new Department, add Company as a navigation property (this generates the CompanyId and the relationship) and give it a string property like Name — the cross-module picker needs a string for the display field. It'll then show up for the transaction just like Company does; the module dependency and .suite/module-entity-extension.json you set up for Company already cover it.
    • A child uses cascade delete, but a reference between two aggregate roots does not — so decide what should happen to a Department when its Company is deleted.
    • On the transaction, if you keep both CompanyId and DepartmentId, validate on the server side (in the app service / domain, not only in the Blazor UI) that the department belongs to the company — the generated code only null-checks the ids. If the transaction doesn't need the company on its own, storing just DepartmentId avoids the duplicate state.

    Thanks

    Markdown supported.
    Copy, paste, or drag & drop images and files (max 100 MB per file, 100 MB total per post)
  • User Avatar
    0
    johnsonlim created

    Hi Maliming, I guess i do not get the scenario clear enough. Let me explain again.

    1.In our master data design, we have company(master entity) owns department(child entity). With this approach, we could enjoy the crud page design of ABP Suites as per below. They are created under Master Data module Tested that we could not have set department as root entity once it is child entity

    2.We created request table under transaction module using ABP Suites. We learn that we are not able to add department as attribute to the transaction table which is why we raise help ^^

    Markdown supported.
    Copy, paste, or drag & drop images and files (max 100 MB per file, 100 MB total per post)
  • User Avatar
    0
    maliming created
    Support Team Fullstack Developer

    Hi,

    Thanks, that makes it clear. The key point: Suite's standard cross-module selector only offers aggregate roots. As long as Department stays a child of Company, the request won't get a Suite-generated reference to it through that selector — that part has to be wired by hand. So it comes down to keeping the master-detail (and wiring this one reference yourself) or making Department a first-class entity.

    Keeping the master-detail (recommended for your case):

    You can keep Department as a child and still let a request pick one. A clean shape:

    • On the request, reference Company the normal Suite way (you get the company picker and a real FK), and add a plain DepartmentId (Guid) property for the chosen department.
    • Load the department options filtered by the selected company (a dependent dropdown). Master Data already generates a paged "list by company id" service for the child — note it's paged with no search filter, so either page through it, or add a small dedicated lookup endpoint in Master Data if you want search/typeahead. A dedicated lookup also lets you give it its own permission, instead of requiring the full Departments CRUD permission just to create a request.
    • Validate on the server side (in the request's create/update) that the department belongs to the company — do that lookup/validation through Master Data's Application.Contracts (add the module/package reference), not by touching its repository or DbContext.
    • Put this custom picker/validation code in the *.Extended.cs / customizable-code files so re-generating the entity doesn't overwrite it.

    Things to keep in mind on this path:

    • Suite doesn't generate a database foreign key for a plain DepartmentId property (only a navigation property does). If you want DB-level enforcement you'd add the FK yourself via a migration.
    • Deletion: the Company → Department mapping uses cascade, but that's for physical deletes — audited entities soft-delete by default, so a deleted department is just hidden by the data filter, and an existing request's DepartmentId would then resolve to nothing. Decide how to handle it (e.g. block deleting a department that's in use, or store the department name on the request for historical display).

    Technically that DepartmentId points at an entity living inside the Company aggregate, so it's a pragmatic reference rather than a standard aggregate-root reference — Master Data stays responsible for validating and resolving it.

    Alternative — make Department an aggregate root:

    Then the request references it the normal Suite way (auto-generated picker and relationship). You'd keep the "manage departments under a company" experience with a custom grid filtered by CompanyId on the Company page — that's custom UI, not Suite's master-detail feature. More setup up front, but cleaner if Department ends up referenced from more places than just requests.

    Thanks

    Markdown supported.
    Copy, paste, or drag & drop images and files (max 100 MB per file, 100 MB total per post)
  • User Avatar
    0
    johnsonlim created

    HI Maliming, I learn that the transaction module does not support multi language also. Can you advise?

    I try to create a page and find out that it has below error. Can you advise?

    Markdown supported.
    Copy, paste, or drag & drop images and files (max 100 MB per file, 100 MB total per post)
  • User Avatar
    0
    maliming created
    Support Team Fullstack Developer

    Hi,

    These build errors are a separate topic from this cross-module question, so it's better to open a new question for them — we'll dig into it properly there.

    A quick pointer in the meantime: L is the localizer, and in a Blazor module it comes from the module's component base (something like OperationRequestComponentBase, which inherits AbpComponentBase and sets the LocalizationResource). Suite's generated pages @inherits that base to get L. A freshly created module has it and compiles fine, so those CS0103: 'L' errors point to that base being missing or not matching in your OperationRequest.Blazor project.

    When you open the new question, please include:

    • Whether OperationRequest.Blazor has a ...ComponentBase class that inherits AbpComponentBase — its class name and namespace.
    • The @inherits ... line from one of the failing generated .razor files.
    • The _Imports.razor of that project.

    With those we can pinpoint why L isn't resolving.

    Thanks

    Markdown supported.
    Copy, paste, or drag & drop images and files (max 100 MB per file, 100 MB total per post)
Boost Your Development
ABP Live Training
Packages
See Trainings
Mastering ABP Framework Book
The Official Guide
Mastering
ABP Framework
Learn More
Mastering ABP Framework Book
Made with ❤️ on ABP v10.8.0-preview. Updated on September 21, 2026, 06:18
1
ABP Assistant
🔐 You need to be logged in to use the chatbot. Please log in first.