Skip to content

Host Application

Open existing GridLab.PSSX.ModuleName.abpsln solution in order to add host application:

Select MVC UI for hosting user interface and application module.

  • Configure .csproj according to project needs.

    <Project Sdk="Microsoft.NET.Sdk.Web">
    
      <!-- Import common properties for projects -->
      <Import Project="..\..\common.props" />
    
      <PropertyGroup>
        <TargetFramework>net9.0</TargetFramework>
        <Nullable>enable</Nullable>
        <AspNetCoreHostingModel>InProcess</AspNetCoreHostingModel>
        <RootNamespace>GridLab.PSSX.ModuleName</RootNamespace>
        <AssetTargetFallback>$(AssetTargetFallback);portable-net45+win8+wp8+wpa81;</AssetTargetFallback>
        <AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
        <GenerateBindingRedirectsOutputType>true</GenerateBindingRedirectsOutputType>
        <GenerateRuntimeConfigurationFiles>true</GenerateRuntimeConfigurationFiles>
        <MvcRazorExcludeRefAssembliesFromPublish>false</MvcRazorExcludeRefAssembliesFromPublish>
        <PreserveCompilationReferences>true</PreserveCompilationReferences>
      </PropertyGroup>
    
      <!-- Add Logging support -->
      <ItemGroup>
         <PackageReference Include="Serilog.AspNetCore" Version="4.0.0" />
         <PackageReference Include="Serilog.Sinks.Async" Version="1.5.0" />
      </ItemGroup>
    
      <ItemGroup>
        <PackageReference Include="Volo.Abp.AspNetCore.Mvc" Version="9.0.5" />
        <PackageReference Include="Volo.Abp.Autofac" Version="9.0.5" />
        <PackageReference Include="Volo.Abp.AspNetCore.Serilog" Version="9.0.5" />
        <PackageReference Include="Volo.Abp.Swashbuckle" Version="9.0.5" />
        <PackageReference Include="Volo.Abp.EntityFrameworkCore.SqlServer" VersionOverride="9.0.5" />
        <!-- Database migration related tools must available -->
        <PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="9.0.4">
          <PrivateAssets>all</PrivateAssets>
          <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
        </PackageReference>
      </ItemGroup>
    
      <ItemGroup>
        <ProjectReference Include="..\..\src\GridLab.PSSX.ModuleName.Application\GridLab.PSSX.ModuleName.Application.csproj" />
        <ProjectReference Include="..\..\src\GridLab.PSSX.ModuleName.HttpApi\GridLab.PSSX.ModuleName.HttpApi.csproj" />
        <ProjectReference Include="..\..\src\GridLab.PSSX.ModuleName.Web\GridLab.PSSX.ModuleName.Web.csproj" />
        <ProjectReference Include="..\..\src\GridLab.PSSX.ModuleName.EntityFrameworkCore\GridLab.PSSX.ModuleName.EntityFrameworkCore.csproj" />
      </ItemGroup>
    
      <ItemGroup>
        <!-- Add project related framework packages -->
        <!-- .... -->
        <PackageReference Include="Volo.Abp.Account.Application" VersionOverride="9.0.5" />
        <PackageReference Include="Volo.Abp.Account.HttpApi" VersionOverride="9.0.5" />
        <PackageReference Include="Volo.Abp.Account.Web.OpenIddict" VersionOverride="9.0.5" />
        <PackageReference Include="Volo.Abp.OpenIddict.EntityFrameworkCore" VersionOverride="9.0.5" />
        <PackageReference Include="Volo.Abp.TenantManagement.HttpApi" VersionOverride="9.0.5" />
        <PackageReference Include="Volo.Abp.TenantManagement.Web" VersionOverride="9.0.5" />
        <PackageReference Include="Volo.Abp.TenantManagement.Application" VersionOverride="9.0.5" />
        <PackageReference Include="Volo.Abp.TenantManagement.EntityFrameworkCore" VersionOverride="9.0.5" />
        <PackageReference Include="Volo.Abp.SettingManagement.Application" VersionOverride="9.0.5" />
        <PackageReference Include="Volo.Abp.SettingManagement.EntityFrameworkCore" VersionOverride="9.0.5" />
        <PackageReference Include="Volo.Abp.AuditLogging.EntityFrameworkCore" VersionOverride="9.0.5" />
        <PackageReference Include="Volo.Abp.PermissionManagement.Domain.Identity" VersionOverride="9.0.5" />
        <PackageReference Include="Volo.Abp.PermissionManagement.Application" VersionOverride="9.0.5" />
        <PackageReference Include="Volo.Abp.PermissionManagement.EntityFrameworkCore" VersionOverride="9.0.5" />
        <PackageReference Include="Volo.Abp.FeatureManagement.HttpApi" VersionOverride="9.0.5" />
        <PackageReference Include="Volo.Abp.FeatureManagement.Web" VersionOverride="9.0.5" />
        <PackageReference Include="Volo.Abp.FeatureManagement.Application" VersionOverride="9.0.5" />
        <PackageReference Include="Volo.Abp.FeatureManagement.EntityFrameworkCore" VersionOverride="9.0.5" />
        <PackageReference Include="Volo.Abp.BlobStoring.Database.EntityFrameworkCore" VersionOverride="9.0.5" />
        <PackageReference Include="Volo.Abp.BackgroundJobs.EntityFrameworkCore" VersionOverride="9.0.5" />
      </ItemGroup>
    
      <!-- Add MVC UI Theme -->
      <ItemGroup>
        <PackageReference Include="Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic" VersionOverride="9.0.5" />
      </ItemGroup>
    
      <ItemGroup>
        <Compile Remove="Logs\**" />
        <Content Remove="Logs\**" />
        <EmbeddedResource Remove="Logs\**" />
        <None Remove="Logs\**" />
      </ItemGroup>
    
      <ItemGroup>
        <Folder Include="Migrations\" />
      </ItemGroup>
    
      <!-- Resolve symbols files for debugging automatically -->
      <Target Name="_ResolveCopyLocalNuGetPackagePdbs" Condition="$(CopyLocalLockFileAssemblies) == true" AfterTargets="ResolveReferences">
        <ItemGroup>
          <ReferenceCopyLocalPaths Include="@(ReferenceCopyLocalPaths->'%(RootDir)%(Directory)%(Filename).pdb')" Condition="'%(ReferenceCopyLocalPaths.NuGetPackageId)' != '' and Exists('%(RootDir)%(Directory)%(Filename).pdb')" />
        </ItemGroup>
      </Target>
    
    </Project>
    

Replace version numbers with latest available.

  • Create <ModuleName>HostDbContext class to configure and manage database interactions for the application.

    • Define a new class that inherits from AbpDbContext<<ModuleName>HostDbContext>.
    • Add a constructor that accepts DbContextOptions and passes it to the base class.
    • Override the OnModelCreating method to configure the database schema using the ModelBuilder. Add any necessary configurations for features like audit logging, permission management, et

    Example:

    public class IdentityHostDbContext : AbpDbContext<IdentityHostDbContext>
    {
        public IdentityHostDbContext(DbContextOptions<IdentityHostDbContext> options)
            : base(options)
        { 
        }
    
        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            base.OnModelCreating(modelBuilder);
    
            modelBuilder.ConfigureAuditLogging();
            modelBuilder.ConfigurePermissionManagement();
            modelBuilder.ConfigureSettingManagement();
            modelBuilder.ConfigureIdentity();
            modelBuilder.ConfigureBlobStoring();
            modelBuilder.ConfigureTenantManagement();
            modelBuilder.ConfigureOpenIddict();
            modelBuilder.ConfigureBackgroundJobs();
            modelBuilder.ConfigureFeatureManagement();
        }
    }
    
  • Create <ModuleName>HostDbContextFactory to create an instance of the <ModuleName>HostDbContext at design time which is need by tools like EF Core migrations.

    • Define a new class that implements IDesignTimeDbContextFactory<HostDbContext>.
    • Implement the CreateDbContext method to configure the DbContextOptions and return an instance of HostDbContext.
    • Use the ConfigurationBuilder to load the connection string from the appsettings.json file.

    Example:

    public class IdentityHostDbContextFactory : IDesignTimeDbContextFactory<IdentityHostDbContext>
    {
        public IdentityHostDbContext CreateDbContext(string[] args)
        {
            var configuration = BuildConfiguration();
    
            var builder = new DbContextOptionsBuilder<IdentityHostDbContext>()
                .UseSqlServer(configuration.GetConnectionString("Default"));
    
            return new IdentityHostDbContext(builder.Options);
        }
    
        private static IConfigurationRoot BuildConfiguration()
        {
            var builder = new ConfigurationBuilder()
                .SetBasePath(Directory.GetCurrentDirectory())
                .AddJsonFile("appsettings.json", optional: false);
    
            return builder.Build();
        }
    }
    
  • Add a Default connection string in the appsettings.json file.

    {
      "ConnectionStrings": {
        "Default": "Server=(LocalDB)\\MSSQLLocalDB;Database=ModuleNameHost_Main;Trusted_Connection=True;TrustServerCertificate=True"
      }
    }
    
  • Create dotnet ef tools scripts for adding migration and updating database.

.NET CLI manifest should available for tools that are installed as local. Check project root for .config folder.

Add Migration:

```powershell
dotnet tool restore

$migrationName = "Initial"
Write-Host "Adding migration '$migrationName'"
dotnet ef migrations add $migrationName
"Added migration."

Write-Host "Press enter to update the database or CTRL-C to exit ..."
$x = $host.UI.RawUI.ReadKey("NoEcho,IncludeKeyDown")

dotnet ef database update
"Database update completed."

pause
```

Update Database:

```powershell
dotnet tool restore

"Updating the database..."
dotnet ef database update
"The database update completed."
Pause
```
  • The versions of npm packages defined in package.json should be checked, where extra npm packages to be used in mvc ui should be defined.
{
  "version": "1.0.0",
  "name": "my-app",
  "private": true,
  "dependencies": {
    "@abp/aspnetcore.mvc.ui.theme.basic": "^9.0.5"
  }
}

Add extra packages if necessary

{
  "version": "0.1.0",
  "name": "my-app-authserver",
  "private": true,
  "dependencies": {
    "@abp/aspnetcore.mvc.ui.theme.basic": "~9.0.5",
    "ace-builds": "^1.4.12",
    "bootstrap-markdown-editor-4": "^3.0.0",
    "jquery-datetimepicker": "^2.5.21",
    "moment": "^2.29.1"
  }
}

Replace version numbers with latest available.

In order to run abp install-libs automation scripts, a definition must be made in abp.resourcemapping.js.

module.exports = {
    aliases: {
        "@node_modules": "./node_modules",
        "@libs": "./wwwroot/libs"
    },
    clean: [
        "@libs",
        "!@libs/**/foo.txt"
    ],
    mappings: {
        "@node_modules/bootstrap-markdown-editor-4/dist/css/bootstrap-markdown-editor.min.css": "@libs/bootstrap-markdown-editor-4/css/",
        "@node_modules/bootstrap-markdown-editor-4/dist/js/bootstrap-markdown-editor.min.js": "@libs/bootstrap-markdown-editor-4/js/",
        "@node_modules/ace-builds/src-noconflict/ace.js": "@libs/ace/",
        "@node_modules/ace-builds/src-noconflict/theme-tomorrow.js": "@libs/ace/",
        "@node_modules/ace-builds/src-noconflict/mode-markdown.js": "@libs/ace/",
        "@node_modules/ace-builds/src-noconflict/ext-language_tools.js": "@libs/ace/",
        "@node_modules/moment/moment.js": "@libs/moment/",
        "@node_modules/jquery-datetimepicker/jquery.datetimepicker.js": "@libs/jquery-datetimepicker/",
        "@node_modules/jquery-datetimepicker/jquery.datetimepicker.css": "@libs/jquery-datetimepicker/",
    }
};

Clean wwwroot/libs and node_modules folders for clean installation. This is required action for abp install-libs script.

ABP CLI's abp install-libs command copies resources from node_modules to wwwroot/libs folder. Each standard package (see the @ABP NPM Packages section) defines the mapping for its own files. So, most of the time, you only configure dependencies.

Configure Services

ConfigureServices method is used to register services and configure the application's behavior. Meanwhile PreConfigureServices method is executed before the ConfigureServices method and is used to set up configurations that need to be applied early in the application's lifecycle.

  • Configure OpenIdDict library.
public override void PreConfigureServices(ServiceConfigurationContext context)
{
    var hostingEnvironment = context.Services.GetHostingEnvironment();
    var configuration = context.Services.GetConfiguration();
    ....
    ....
    PreConfigure<OpenIddictBuilder>(builder =>
    {
        builder.AddValidation(options =>
        {
            options.AddAudiences("Identity");
            options.UseLocalServer();
            options.UseAspNetCore();
        });
    });

    PreConfigure<AbpOpenIddictAspNetCoreOptions>(options =>
    {
        options.AddDevelopmentEncryptionAndSigningCertificate = true;
    });
}

Configure related Authentication services

public override void ConfigureServices(ServiceConfigurationContext context)
{
    var hostingEnvironment = context.Services.GetHostingEnvironment();
    var configuration = context.Services.GetConfiguration();

    ConfigureAuthentication(context, configuration);
    ....
}
private void ConfigureAuthentication(ServiceConfigurationContext context, IConfiguration configuration)
{
    if (!configuration.GetValue<bool>("App:DisablePII"))
    {
        Microsoft.IdentityModel.Logging.IdentityModelEventSource.ShowPII = true; // Enables logging of Personally Identifiable Information (PII) if the App:DisablePII configuration is set to false.
        Microsoft.IdentityModel.Logging.IdentityModelEventSource.LogCompleteSecurityArtifact = true;
    }

    if (!configuration.GetValue<bool>("AuthServer:RequireHttpsMetadata"))
    {
        Configure<OpenIddictServerAspNetCoreOptions>(options =>
        {
            options.DisableTransportSecurityRequirement = true; // Disables the HTTPS metadata requirement for OpenIddict if AuthServer:RequireHttpsMetadata is set to false.
        });

        Configure<ForwardedHeadersOptions>(options =>
        {
            options.ForwardedHeaders = ForwardedHeaders.XForwardedProto; // Configures ForwardedHeadersOptions to handle X-Forwarded-Proto headers, which is useful when the app is behind a reverse proxy.
        });
    }

    context.Services.ForwardIdentityAuthenticationForBearer(OpenIddictValidationAspNetCoreDefaults.AuthenticationScheme);
    context.Services.Configure<AbpClaimsPrincipalFactoryOptions>(options => 
    {
        options.IsDynamicClaimsEnabled = true; // Configures identity authentication for bearer tokens and enables dynamic claims.
    });
}

Add a AuthServer:RequireHttpsMetadata and App:DisablePII configuration to the appsettings.json file.

{
  "App": {
    ....,
    "DisablePII": "false"
  },
  "AuthServer": {
    ....,
    "Authority": "https://localhost:44334",
    "RequireHttpsMetadata": true
  }
}
  • Configure Databases ORM provider.
public override void ConfigureServices(ServiceConfigurationContext context)
{
    var hostingEnvironment = context.Services.GetHostingEnvironment();
    var configuration = context.Services.GetConfiguration();

    ConfigureDatabaseConnection(context, configuration);
    ....
}
private void ConfigureDatabaseConnection(ServiceConfigurationContext context, IConfiguration configuration)
{
    Configure<AbpDbConnectionOptions>(options =>
    {
        options.ConnectionStrings.Default = configuration.GetConnectionString("Default");
    });

    Configure<AbpDbContextOptions>(options =>
    {
        options.UseSqlServer();
    });
}
  • Configure virtual file system for development purposes.
public override void ConfigureServices(ServiceConfigurationContext context)
{
    var hostingEnvironment = context.Services.GetHostingEnvironment();
    var configuration = context.Services.GetConfiguration();

    ConfigureVirtualFileSystem(context, configuration);
    ....
}

Example:

private void ConfigureVirtualFileSystem(IWebHostEnvironment hostingEnvironment)
{
    Configure<AbpVirtualFileSystemOptions>(options =>
    {
        options.FileSets.AddEmbedded<IdentityWebHostModule>();

        if (hostingEnvironment.IsDevelopment())
        {
            options.FileSets.ReplaceEmbeddedByPhysical<IdentityDomainSharedModule>(Path.Combine(hostingEnvironment.ContentRootPath, string.Format("..{0}..{0}src{0}GridLab.PSSX.Identity.Domain.Shared", Path.DirectorySeparatorChar)));
            options.FileSets.ReplaceEmbeddedByPhysical<IdentityDomainModule>(Path.Combine(hostingEnvironment.ContentRootPath, string.Format("..{0}..{0}src{0}GridLab.PSSX.Identity.Domain", Path.DirectorySeparatorChar)));
            options.FileSets.ReplaceEmbeddedByPhysical<IdentityWebModule>(Path.Combine(hostingEnvironment.ContentRootPath, string.Format("..{0}..{0}src{0}GridLab.PSSX.Identity.Web", Path.DirectorySeparatorChar)));
            options.FileSets.ReplaceEmbeddedByPhysical<IdentityApplicationModule>(Path.Combine(hostingEnvironment.ContentRootPath, string.Format("..{0}..{0}src{0}GridLab.PSSX.Identity.Application", Path.DirectorySeparatorChar)));
            options.FileSets.ReplaceEmbeddedByPhysical<IdentityApplicationContractsModule>(Path.Combine(hostingEnvironment.ContentRootPath, string.Format("..{0}..{0}src{0}GridLab.PSSX.Identity.Application.Contracts", Path.DirectorySeparatorChar)));
            options.FileSets.ReplaceEmbeddedByPhysical<IdentityHttpApiModule>(Path.Combine(hostingEnvironment.ContentRootPath, string.Format("..{0}..{0}src{0}GridLab.PSSX.Identity.HttpApi", Path.DirectorySeparatorChar)));
            options.FileSets.ReplaceEmbeddedByPhysical<IdentityWebHostModule>(hostingEnvironment.ContentRootPath);
        }
    });
}
  • Configure MVC root url.
public override void ConfigureServices(ServiceConfigurationContext context)
{
    var hostingEnvironment = context.Services.GetHostingEnvironment();
    var configuration = context.Services.GetConfiguration();

    ConfigureUrls(configuration);
    ....
}
private void ConfigureUrls(IConfiguration configuration)
{
    Configure<AppUrlOptions>(options =>
    {
        options.Applications["MVC"].RootUrl = configuration["App:SelfUrl"];
    });
}

Add a App:SelfUrl configuration to the appsettings.json file.

{
  "App": {
    ....,
    "SelfUrl": "https://localhost:44372", // Update port according to project needs
  }
}
  • Enable API versioning.

Refer to Api Versioning.

  • Configure Swagger by using IConfigureOptions<SwaggerGenOptions> interface, in order to do that create new folder named Swagger and then add new class ConfigureSwaggerOptions.cs at GridLab.PSSX.<ModuleName> project.

Refer to Configure Swagger section at Api Versioning.

  • Configure Localization options.
public override void ConfigureServices(ServiceConfigurationContext context)
{
    var hostingEnvironment = context.Services.GetHostingEnvironment();
    var configuration = context.Services.GetConfiguration();

    ConfigureLocalization();
    ....
}
private void ConfigureLocalization()
{
    Configure<AbpLocalizationOptions>(options =>
    {
        options.Languages.Add(new LanguageInfo("de-DE", "de-DE", "Deutsch"));
        options.Languages.Add(new LanguageInfo("en", "en", "English"));
        options.Languages.Add(new LanguageInfo("tr", "tr", "Türkçe"));
    });
}

Pages

  • Add <ModuleName>PageModel as base class.

Example:

public abstract class IdentityPageModel : AbpPageModel
{
    protected IdentityPageModel()
    {
        LocalizationResourceType = typeof(IdentityResource);
    }
}
  • Update IndexModel.

Example:

public class IndexModel : IdentityPageModel
{
    public void OnGet()
    {

    }
}
  • Update Index.cshtml

Example:

@page
@model GridLab.PSSX.Identity.Pages.IndexModel
@using Volo.Abp.Users
@inject ICurrentUser CurrentUser
<abp-card>
    <abp-card-header>Welcome to the Identity Management Application</abp-card-header>
    <abp-card-body>
        @if (!CurrentUser.IsAuthenticated)
        {
            <form method="POST">
                <input type="submit" asp-page-handler="Login" value="LOGIN" class="btn btn-primary" />
            </form>
        }
        else
        {
            <strong>Claims</strong><br />
            @Html.Raw(CurrentUser.GetAllClaims().Select(c => $"{c.Type}={c.Value}").OrderBy(x => x).JoinAsString(" <br /> "))
        }
        <hr />
        <p class="text-end"><a href="https://www.gridlabx.io" target="_blank">gridlabx.io</a></p>
    </abp-card-body>
</abp-card>