Why DbMigrator didn’t create the audit DB
Fix step-by-step
[DependsOn(
typeof(Volo.Abp.EntityFrameworkCore.SqlServer.AbpEntityFrameworkCoreSqlServerModule),
typeof(Volo.Abp.AuditLogging.EntityFrameworkCore.AbpAuditLoggingEntityFrameworkCoreModule)
)]
public class YourAppEntityFrameworkCoreModule : AbpModule
{
public override void ConfigureServices(ServiceConfigurationContext context)
{
Configure<AbpDbContextOptions>(options =>
{
// Global default or per-context config
options.UseSqlServer();
options.Configure<Volo.Abp.AuditLogging.EntityFrameworkCore.AbpAuditLoggingDbContext>(c =>
{
c.UseSqlServer();
});
});
}
}
{
"ConnectionStrings": {
"Default": "Server=.;Database=MainDb;Trusted_Connection=True;TrustServerCertificate=true",
"AbpAuditLogging": "Server=.;Database=AuditDb;Trusted_Connection=True;TrustServerCertificate=true"
}
}
Add the AbpAuditLogging.EntityFrameworkCore package reference to the project where you keep migrations (commonly the EFCore or DbMigrator project references it).
Make sure the project builds and can resolve the context at design-time. Typically you either:
Example design-time factory (only for migration generation):
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Design;
using Microsoft.Extensions.Configuration;
using System.IO;
using Volo.Abp.AuditLogging.EntityFrameworkCore;
public class AbpAuditLoggingDbContextFactory : IDesignTimeDbContextFactory<AbpAuditLoggingDbContext>
{
public AbpAuditLoggingDbContext CreateDbContext(string[] args)
{
var configuration = new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("appsettings.json", optional: false)
.AddEnvironmentVariables()
.Build();
var builder = new DbContextOptionsBuilder<AbpAuditLoggingDbContext>()
.UseSqlServer(configuration.GetConnectionString("AbpAuditLogging"));
return new AbpAuditLoggingDbContext(builder.Options);
}
}
dotnet ef migrations add Init_Audit_Logging --context Volo.Abp.AuditLogging.EntityFrameworkCore.AbpAuditLoggingDbContext
Notes:
dotnet run --project src/YourApp.DbMigrator
Configure<AbpAuditingOptions>(options =>
{
options.IsEnabled = true;
options.EntityHistorySelectors.AddAllEntities();
});
public override void OnApplicationInitialization(ApplicationInitializationContext context)
{
var app = context.GetApplicationBuilder();
app.UseAuditing();
}
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.
<abp-modal
[(visible)]="isOpen"
[options]="{ windowClass: 'my-wide-modal', size: 'xl' }"
>
<ng-template #abpHeader>My Wide Editor</ng-template>
<ng-template #abpBody> ... complex content ... </ng-template>
<ng-template #abpFooter>
<abp-button (click)="isOpen = false">Close</abp-button>
</ng-template>
</abp-modal>
Notes:
ngb-modal-window.my-wide-modal .modal-dialog {
max-width: 90vw !important; /* or a fixed px width */
width: 90vw; /* ensure it expands */
margin: 1.5rem auto; /* optional: adjust centering/margins */
}
/* Optionally control body height/scrolling if needed */
ngb-modal-window.my-wide-modal .modal-content {
height: calc(100vh - 3rem);
}
Why global? NgbModal renders outside the component tree, so ViewEncapsulation styles won’t reach it. A unique windowClass ensures the rule applies only to that single modal, not globally.
About dialogClass/modalClass inputs
Is passing custom NgbModal options supported?
If you prefer a CSS-only approach without options
Recommended alternative for very complex editors
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.
What happened
Immediate fix (so you can continue working)
Make the failure visible next time
CI/CD and production guidance
Feature request acknowledgement
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.
The AbpAuditLoggingDbContext uses a predefined connection string name: "AbpAuditLogging". If you don’t define it, it falls back to the "Default" connection string.
To store audit logs in a separate database, add a dedicated connection string named AbpAuditLogging in your configuration. No extra wiring to the DbContext is needed beyond using the EF Core provider for that context.
Steps:
{
"ConnectionStrings": {
"Default": "Server=.;Database=MainDb;Trusted_Connection=True;TrustServerCertificate=true",
"AbpAuditLogging": "Server=.;Database=AuditDb;Trusted_Connection=True;TrustServerCertificate=true"
}
}
[DependsOn(
typeof(Volo.Abp.EntityFrameworkCore.SqlServer.AbpEntityFrameworkCoreSqlServerModule),
typeof(Volo.Abp.AuditLogging.EntityFrameworkCore.AbpAuditLoggingEntityFrameworkCoreModule)
)]
public class YourAppEntityFrameworkCoreModule : AbpModule
{
public override void ConfigureServices(ServiceConfigurationContext context)
{
Configure<AbpDbContextOptions>(options =>
{
// Global provider or per-context
options.UseSqlServer();
options.Configure<Volo.Abp.AuditLogging.EntityFrameworkCore.AbpAuditLoggingDbContext>(c =>
{
c.UseSqlServer();
});
});
}
}
Why this works:
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.
You’re right: AbpDbConnectionOptions.Databases.Configure(...).MappedConnections.Add(...) expects connection name strings, not DbContext types. Use the module’s connection name for Audit Logging.
Correct configuration for storing Audit Logs in a separate database:
{
"ConnectionStrings": {
"Default": "Server=.;Database=MainDb;Trusted_Connection=True;TrustServerCertificate=true",
"AuditLogging": "Server=.;Database=AuditDb;Trusted_Connection=True;TrustServerCertificate=true"
}
}
Configure<AbpDbConnectionOptions>(options =>
{
options.Databases.Configure("AuditLogging", db =>
{
// Map module connection names (strings), not types
db.MappedConnections.Add("AbpAuditLogging");
});
});
Configure<AbpDbContextOptions>(options =>
{
options.UseSqlServer();
options.Configure<Volo.Abp.AuditLogging.EntityFrameworkCore.AbpAuditLoggingDbContext>(opt =>
{
opt.UseSqlServer();
});
});
[DependsOn(
typeof(Volo.Abp.EntityFrameworkCore.SqlServer.AbpEntityFrameworkCoreSqlServerModule),
typeof(Volo.Abp.AuditLogging.EntityFrameworkCore.AbpAuditLoggingEntityFrameworkCoreModule)
)]
public class YourAppEntityFrameworkCoreModule : AbpModule
{
}
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.
Use a single DbMigrator to migrate both databases (your main “Default” DB and the separate Audit Logging DB). ABP’s DbMigrator already supports multiple DbContexts and multiple databases; you only need to map the AbpAuditLoggingDbContext to its own connection and ensure its migrations are included.
Steps:
{
"ConnectionStrings": {
"Default": "Server=.;Database=MainDb;Trusted_Connection=True;TrustServerCertificate=true",
"AuditLogging": "Server=.;Database=AuditDb;Trusted_Connection=True;TrustServerCertificate=true"
}
}
[DependsOn(
typeof(AbpEntityFrameworkCoreSqlServerModule),
typeof(AbpAuditLoggingEntityFrameworkCoreModule)
)]
public class YourAppEntityFrameworkCoreModule : AbpModule
{
public override void ConfigureServices(ServiceConfigurationContext context)
{
Configure<AbpDbContextOptions>(options =>
{
options.UseSqlServer();
options.Configure<AbpAuditLoggingDbContext>(c => c.UseSqlServer());
});
Configure<AbpDbConnectionOptions>(options =>
{
options.Databases.Configure("AuditLogging", db =>
{
db.MappedConnections.Add(typeof(AbpAuditLoggingDbContext));
});
});
}
}
dotnet ef migrations add "Init_Audit_Logging" --context AbpAuditLoggingDbContext
Ensure the design-time factory (IDesignTimeDbContextFactory) for AbpAuditLoggingDbContext reads the “AuditLogging” connection.
cd your-solution/src/YourApp.DbMigrator
dotnet run
It should apply migrations to MainDb and AuditDb respectively.
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.
Cause
Fix/Workarounds
Cause
Checklist to fix
Project: MeinTestDemoWerk.ProcessService.Domain
Folder: Processes
File example: using Volo.Abp.Domain.Entities.Auditing;
namespace MeinTestDemoWerk.ProcessService.Processes { public class Process : FullAuditedAggregateRoot<Guid> { public string Name { get; set; } // other props... } }
Notes
If issues persist
Sources
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.
Key steps:
Keep API contracts identical
Provide two hosts with the same API surface
[DependsOn(
typeof(MyProjectApplicationModule),
typeof(MyProjectHttpApiModule),
typeof(AbpAspNetCoreMvcModule)
)]
public class MyProjectLocalHostModule : AbpModule
{
public override void ConfigureServices(ServiceConfigurationContext context)
{
// Configure EF Core to SQLite for local store
Configure<AbpDbConnectionOptions>(options =>
{
options.ConnectionStrings.Default = "Data Source=local.db";
});
}
}
public override void PreConfigureServices(ServiceConfigurationContext context)
{
PreConfigure<AbpRemoteServiceOptions>(options =>
{
var isOffline = /* your connectivity check */;
options.RemoteServices.Default = new RemoteServiceConfiguration(
baseUrl: isOffline ? "https://localhost:5005" : "https://central-host.example.com"
);
});
}
Optionally, add Polly retry logic to smooth transitions.
Use a local EF Core provider
Authentication considerations
Data synchronization (when back online)
Caching and queues
Blazor hosting choice
Minimal local host example
public class Program
{
public static async Task Main(string[] args)
{
var builder = WebApplication.CreateBuilder(args);
// SQLite connection for local mode
builder.Services.Configure<AbpDbConnectionOptions>(options =>
{
options.ConnectionStrings.Default = builder.Configuration.GetConnectionString("Default");
});
await builder.AddApplicationAsync<MyProjectLocalHostModule>();
var app = builder.Build();
await app.InitializeApplicationAsync();
app.MapControllers();
await app.RunAsync();
}
}
appsettings.json (local): { "ConnectionStrings": { "Default": "Data Source=local.db" }, "App": { "SelfUrl": "https://localhost:5005" } }
What ABP gives you vs what you must implement
This approach aligns with common ABP conventions, but specific official documentation for an “offline local middleware with identical API + sync back to central SQL Server” is not available.
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.
A) Backend (HttpApi.Host) – Resolve tenant from subdomain and allow wildcard OpenIddict/redirects
// using Volo.Abp.OpenIddict.WildcardDomains;
PreConfigure<AbpOpenIddictWildcardDomainOptions>(options =>
{
options.EnableWildcardDomainSupport = true;
// Add all authority formats that will be used by the auth server (API host if combined)
// Example for environments:
options.WildcardDomainsFormat.Add("https://{0}.app-qa.example.com");
options.WildcardDomainsFormat.Add("https://{0}.app-pre.example.com");
options.WildcardDomainsFormat.Add("https://{0}.app.example.com");
// If your API host is also subdomained (like {0}.api.app-qa.example.com), add those too.
});
// using Volo.Abp.MultiTenancy;
Configure<AbpTenantResolveOptions>(options =>
{
// Adjust formats to your real hostnames used for API endpoints receiving requests.
options.AddDomainTenantResolver("{0}.app-qa.example.com");
options.AddDomainTenantResolver("{0}.app-pre.example.com");
options.AddDomainTenantResolver("{0}.app.example.com");
// If your API host has a different subdomain pattern, add its formats as well.
});
// using Owl.TokenWildcardIssuerValidator;
context.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(options =>
{
options.Authority = configuration["AuthServer:Authority"]; // Your base authority
options.RequireHttpsMetadata = true;
// Allow wildcard issuers for subdomain tenants
options.TokenValidationParameters.IssuerValidator = TokenWildcardIssuerValidator.IssuerValidator;
options.TokenValidationParameters.ValidIssuers = new[]
{
"https://{0}.app-qa.example.com/",
"https://{0}.app-pre.example.com/",
"https://{0}.app.example.com/"
};
});
context.Services.AddAbpStrictRedirectUriValidator();
context.Services.AddAbpClientConfigurationValidator();
context.Services.AddAbpWildcardSubdomainCorsPolicyService();
"IdentityServer": {
"Clients": {
"My_Blazor_Wasm": {
"ClientId": "My_Blazor_Wasm",
"ClientSecret": "1q2w3e*",
"RootUrl": "https://{0}.app-qa.example.com"
}
}
}
Repeat entries for each environment if you keep per-env DBs; otherwise include each environment’s RootUrl pattern if you use one central auth DB for all envs. Re-run DbMigrator so allowed redirect URIs/CORS origins are updated.
B) Blazor Interactive (WebAssembly-only) – Propagate the current tenant’s subdomain to Authority and BaseUrl
For Blazor WebAssembly, there is no server-side tenant resolver in the UI process. You must dynamically shape the Authority and BaseUrl at startup using the current browser URL. This is documented for Angular and demonstrated for Blazor in community samples. Implement the pattern below:
Example utility methods:
private static readonly string[] ProtocolPrefixes = { "http://", "https://" };
private static string ConvertToTenantSubDomain(WebAssemblyHostBuilder builder, string configPath)
{
var baseUrl = builder.HostEnvironment.BaseAddress; // e.g., https://tenant1.app-qa.example.com/
var configUrl = builder.Configuration[configPath]; // e.g., "https://{0}.api.app-qa.example.com"
return configUrl.Replace("{0}.", GetTenantNamePrefix(baseUrl));
}
// Returns "tenant1." or "" when no tenant
private static string GetTenantNamePrefix(string baseUrl)
{
var hostName = baseUrl.RemovePreFix(ProtocolPrefixes); // tenant1.app-qa.example.com/
var host = hostName.TrimEnd('/').Split('/')[0]; // tenant1.app-qa.example.com
var parts = host.Split('.');
// Your rule: "tenant-env.example.com" => first label is tenant, second is env-app
// E.g., tenant1.app-qa.example.com => tenant is parts[0], rest identifies environment/app
return parts.Length > 2 ? $"{parts[0]}." : string.Empty;
}
builder.Services.AddOidcAuthentication(options =>
{
builder.Configuration.Bind("AuthServer", options.ProviderOptions);
options.UserOptions.RoleClaim = "role";
options.ProviderOptions.Authority = ConvertToTenantSubDomain(builder, "AuthServer:Authority");
});
Configure<AbpRemoteServiceOptions>(options =>
{
options.RemoteServices.Default = new RemoteServiceConfiguration(
ConvertToTenantSubDomain(builder, "RemoteServices:Default:BaseUrl")
);
});
{
"App": {
"SelfUrl": "https://{0}.app-qa.example.com"
},
"AuthServer": {
"Authority": "https://{0}.api.app-qa.example.com",
"ClientId": "My_Blazor_Wasm",
"ResponseType": "code"
},
"RemoteServices": {
"Default": {
"BaseUrl": "https://{0}.api.app-qa.example.com"
}
}
}
Repeat per environment or compute environment from the current host if you prefer a single file with multiple candidates.
C) Cookie/Authentication isolation across tenants in WASM If you observe “login to one tenant logs me into all,” your auth cookie is likely scoped to the parent domain. Always let the auth cookie domain be the exact host (no leading dot, Domain = null). OpenIddict server defaults typically align with this; avoid setting a shared cookie domain. Clear browser cookies after changes.
D) Resolver order and middleware ABP’s default resolver order starts with CurrentUserTenantResolveContributor, then QueryString, Route, Header, Cookie. Adding the domain resolver (as shown) integrates correctly. Ensure app.UseAbpRequestLocalization(), app.UseRouting(), app.UseAuthentication(), app.UseAbpMultiTenancy(), app.UseAuthorization() are in the standard order generated by templates.
E) Environment-based subdomains (app-qa/app-pre/app)
F) Known limitations and notes for Blazor WASM
G) Minimal verification steps
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.
Swagger succeeds because it automatically sends the required headers (Authorization, X-Requested-With, RequestVerificationToken, etc.). Your browser/web client likely does not. That often results in a 404 Not Found (ABP hides some auth-related responses to avoid information leakage) unless you send the expected headers/tokens.
Checklist to fix the Not Found when calling from Web:
Ensure you send Authorization like Swagger does
Include the ABP Ajax header to avoid anti-forgery validation (GET is safe, but ABP expects the Ajax header in many UI scenarios)
If you enabled Anti-Forgery (default for MVC/Razor), either:
Confirm your frontend is calling the exact same URL and method
If your app is multi-tenant and the request needs tenant context
Ensure route precedence is not overridden
Test quickly with curl or Postman using the same headers Swagger shows
Example: minimal safe controller annotations if you don’t want anti-forgery for GET
[Authorize(KSVRPermissions.VehicleOwners.Default)]
[Route("api/ksvr/vehicle-owners")]
public class VehicleOwnersAppService : ApplicationService
{
[IgnoreAntiforgeryToken] // optional if you face CSRF checks in your UI pipeline
[HttpGet("lookup/autocomplete")]
public virtual async Task<ListResultDto<VehicleAutocompleteDto>> AutocompleteAsync([FromQuery] string keyword)
{
...
}
}
And sample JS fetch with the required headers:
const res = await fetch('http://localhost:44303/api/ksvr/vehicle-owners/lookup/autocomplete?keyword=29', {
method: 'GET',
headers: {
'Authorization': `Bearer ${accessToken}`,
'X-Requested-With': 'XMLHttpRequest'
// 'RequestVerificationToken': csrfToken // if needed in your setup
// 'Abp-Tenant': tenantName // if multi-tenant by name
},
credentials: 'include'
});
Why your other API works while autocomplete doesn’t
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.