Exception message and full stack trace
No exception. Compile-time issue on the generated client project (details in the steps below).
Steps to reproduce the issue
Context
-------
Modular monolith (not microservices), ABP Commercial 10.6.0, .NET 10, ABP CLI 3.1.1.
Two applications, AppA and AppB, versioned and deployed independently.
AppA.HttpApi.Client is published to a private NuGet feed and consumed by AppB.
AppA.Application.Contracts is NOT published: our CI only packages HttpApi.Client.
What we want
------------
AppA exposes 2 integration services. We want AppA.HttpApi.Client to be self-contained,
so that AppB can consume those contracts without referencing AppA.Application.Contracts.
Steps
-----
1. In AppA, declare the integration services with [IntegrationService] and set
options.ExposeIntegrationServices = true in ConfigureConventionalControllers().
They live in business namespaces, e.g. MyApp.Param_Ops.Ops.Numbers.
2. Start the host and run:
abp generate-proxy -t csharp -u https://localhost:44314/ -m app -st integration
3. The 2 integration service interfaces and their client proxies are generated - correct.
But the generated DTOs are not the transitive closure of those interfaces: out of 723
exposed types, 73 are generated, matching exactly the namespaces (and sub-namespaces)
of the 2 integration service controllers, whether or not they are reachable from the
integration service signatures.
4. As a result, application DTOs that merely share the namespace are generated too (e.g.
NumberWithRioDto), and they reference types from OTHER namespaces (e.g. CountryDto in
Params.Countries) which are not generated. Building the client project standalone
fails with 34 errors:
error CS0234: The type or namespace name 'CountryDto' does not exist in the namespace
'MyApp.Param_Ops.Params.Countries'
Keeping a ProjectReference to Application.Contracts makes it compile, but every DTO
then exists twice (generated file + imported type), producing 101 CS0436 warnings, and
dotnet pack declares a NuGet dependency on a package we do not publish (NU1101 for the
consumer). --without-contracts requires that same contracts package, which is exactly
the coupling we are trying to avoid.
Question
--------
What is the recommended way to obtain a self-contained HttpApi.Client package exposing
only the integration service contracts, for applications that are packaged and deployed
independently?
We got it to work by moving the integration services and their dedicated *IntegrationDto
types into a separate namespace, but shared enums (StatusNumber, NumberType, ...) live in
AppA.Domain.Shared under the application namespaces, are used across the whole
application, and therefore are not generated. We had to duplicate them into the
integration namespace, which we would rather avoid since it creates two declarations of a
single business concept with no build-time protection against divergence.
Is that the expected approach, or is there a supported way to have the generator emit the
types a contract actually depends on, even when they belong to another namespace?
2 Answer(s)
-
0
In ABP 10.6, the C# proxy generator is not a “dependency graph / transitive closure” generator.
When you runabp generate-proxy -t csharp ... -st integration, ABP determines the “models to generate” primarily by namespace grouping (service namespace and related model namespaces), not by recursively walking every referenced DTO/enum type across other namespaces. That’s why you see:- DTOs in the same namespace getting generated even if not referenced by the integration service signatures, and
- referenced DTOs/enums from other namespaces not generated, causing compile errors in a standalone
HttpApi.Clientpackage.
Recommended/supported approach for independently deployed apps (AppA → NuGet → AppB):
- Create a dedicated Integration Contracts surface (a stable boundary) and generate/publish the client from that:
- Put integration service interfaces + integration DTOs/enums into a dedicated project/namespace (e.g.
AppA.Integration.Contracts). - Keep them separate from internal application DTOs.
- Generate the C# client proxies from integration services and package them.
- Put integration service interfaces + integration DTOs/enums into a dedicated project/namespace (e.g.
- If you want to reuse “shared enums” without duplicating, the supported way is to move those shared enums (and any shared DTOs) into a shared contracts assembly that is part of your integration boundary and is published alongside (or referenced by) the client package.
Practically:- Move
StatusNumber,NumberType, etc. into something likeAppA.Domain.Sharedbut in an integration-safe namespace/package (or a newAppA.Integration.Sharedproject). - Reference that shared package from both AppA and AppB.
- Keep the integration DTOs referencing only types from:
- ABP framework packages, and
- your published shared integration contracts packages.
- Move
- Create a dedicated Integration Contracts surface (a stable boundary) and generate/publish the client from that:
What you’re doing now (moving Integration services + IntegrationDto into a separate namespace) is essentially the intended boundary.
The missing piece is: anything used by that boundary must also live in (or be referenced by) that boundary, otherwise the generated client can’t be self-contained because the generator won’t chase types across your whole solution and emit a perfect closure.What not to rely on
--without-contractsis for the opposite scenario: it assumes the consumer already references the target service’s*.Application.Contractspackage so types are reused (and avoids duplication). It won’t help you make a single self-contained package if you don’t publish those contracts.
Sources (verified):
- https://abp.io/docs/10.6/framework/api-development/static-csharp-clients
- https://abp.io/docs/10.6/release-info/migration-guides/abp-7-0
- https://abp.io/docs/10.6/release-info/migration-guides/abp-7-4
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) -
0
Hi,
The C# proxy generator picks the DTOs by the namespace prefix of the service class, not by the types your method signatures actually use. That's why DTOs that only share the namespace get generated, and types your contracts depend on but live elsewhere don't. There is no switch to change that today.
So rather than generating the contracts, publish them - that's what our own modules do.
Volo.Abp.Identity.HttpApi.Clientreferences the publishedVolo.Abp.Identity.Application.Contractsand is generated with--without-contracts, so it carries client proxy classes only and nothing is duplicated.For you that is a small
AppA.Integration.Contractsproject with the two integration service interfaces and their*IntegrationDtotypes. It referencesAppA.Domain.Shared, soStatusNumberandNumberTypestay exactly where they are - no move, no second declaration.AppA.Application.Contractsreferences the new project, and you publishAppA.Domain.SharedandAppA.Integration.Contractsnext toAppA.HttpApi.Client.[DependsOn( typeof(AppADomainSharedModule), typeof(AbpDddApplicationContractsModule) )] public class AppAIntegrationContractsModule : AbpModule { }In
AppA.HttpApi.Client, swap theAppA.Application.Contractsproject reference forAppA.Integration.Contracts, replace that one entry inDependsOnand keep the rest, and register the proxies over the contracts assembly:[DependsOn( typeof(AppAIntegrationContractsModule), // replaces AppAApplicationContractsModule typeof(AbpHttpClientModule), typeof(AbpIdentityHttpApiClientModule), // the other module dependencies stay as they are // ... )] public class AppAHttpApiClientModule : AbpModule { public const string RemoteServiceName = "Default"; public override void ConfigureServices(ServiceConfigurationContext context) { context.Services.AddStaticHttpClientProxies( typeof(AppAIntegrationContractsModule).Assembly, RemoteServiceName ); Configure<AbpVirtualFileSystemOptions>(options => { options.FileSets.AddEmbedded<AppAHttpApiClientModule>(); }); } }Remove the previously generated files before you regenerate -
generate-proxyoverwrites and adds, it never removes, so the 73 old DTOs would stay behind and keep producing CS0436.remove-proxydeletes the wholeClientProxiesfolder, so move any partial proxy class you have customized out of the way first:abp remove-proxy -t csharp abp generate-proxy -t csharp -u https://localhost:44314/ -m app -st integration --without-contractsYou end up with the two client proxy classes and the proxy json, no DTOs. AppB references
AppA.HttpApi.Clientand gets the contracts and the enums through it, so there is a single declaration of each enum and no dependency onAppA.Application.Contracts.We'll also improve the generator to emit the types a contract actually depends on instead of relying on the namespace grouping.
Thanks
Markdown supported.Copy, paste, or drag & drop images and files (max 100 MB per file, 100 MB total per post)