Open Closed

Additional Column to Identity User Table and related issues #10653


User avatar
0
punta created

I want to add LastName field to IdentityUser table. Additionally, I want to make Surname field to appear as Middle Name just on the UI. The documentation might look enough for these, but finally, in the registration page, I want these three to appear in the order: Name, Middle Name (Surname), Last Name. More insights are welcome.

Thanks in advance!

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

5 Answer(s)
  • User Avatar
    0
    AI-Bot created
    Support Team ABP Agent
    • Add a physical LastName column to AbpUsers (IdentityUser)
      1. Define the extension property in your *.Domain.Shared project (YourProjectNameModuleExtensionConfigurator.cs), inside ConfigureExtraProperties:
    using System.ComponentModel.DataAnnotations;
         using Volo.Abp.ObjectExtending;
    
         ObjectExtensionManager.Instance.Modules().ConfigureIdentity(identity =>
         {
             identity.ConfigureUser(user =>
             {
                 user.AddOrUpdateProperty<string>(
                     "LastName",
                     options =>
                     {
                         options.Attributes.Add(new StringLengthAttribute(64));
                     }
                 );
             });
         });
    
    1. Map it to a real SQL column in your *.EntityFrameworkCore project (YourProjectNameEfCoreEntityExtensionMappings.cs):
    using Volo.Abp.Identity;
         using Volo.Abp.ObjectExtending;
    
         ObjectExtensionManager.Instance
             .MapEfCoreProperty<IdentityUser, string>(
                 "LastName",
                 (_, propertyBuilder) =>
                 {
                     propertyBuilder.HasMaxLength(64);
                     propertyBuilder.HasDefaultValue("");
                 }
             );
    
    1. Add migration + update DB (run from the migrations/EFCore project you use):
      • Add-Migration Added_LastName_To_AbpUsers
      • Run DbMigrator / Update-Database
    • Show Surname as “Middle Name” only on the UI

      • Don’t change the backend property name (Surname) if you want to keep ABP’s defaults; just change the label/display text in the registration UI.
      • In MVC/Razor Pages, the “label text” is typically localized; you can override the localization value for the displayed field name (so it renders as Middle Name while still posting to Surname).
    • Make the registration page order: Name, Middle Name (Surname), Last Name

      • In MVC, the reliable way is to override/customize the Register page UI and reorder the inputs in the .cshtml.
      • Keep binding as:
        • Name → first input
        • Surname → second input (but label it “Middle Name”)
        • ExtraProperties["LastName"] (or your strongly typed extension) → third input
    • If you want LastName to be saved during registration

      • Add the input on the register form and send it as an extension property.
      • In tiered solutions, ensure the Account endpoints can carry your extension property by applying entity configuration to the Account/Profile/Register DTOs (same mechanism used for profile update). The known working approach is to call ModuleExtensionConfigurationHelper.ApplyEntityConfigurationToApi(...) in the host where the Account endpoints run (AuthServer/HttpApi.Host), including the relevant DTO types.

    Less obvious references:

    • https://abp.io/community/articles/rixchoha
    • https://abp.io/docs/latest/framework/architecture/modularity/extending/module-entity-extensions
    • https://abp.io/docs/latest/framework/architecture/modularity/extending/customizing-application-modules-extending-entities
    • https://abp.io/support/questions/3517

    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 punta,

    Since your solution is tiered, I'll call out where each file goes.

    1. Add a physical LastName column on AbpUsers

    In *.Domain.Shared/YourProjectNameModuleExtensionConfigurator.cs:

    using System.ComponentModel.DataAnnotations;
    using Volo.Abp.Identity;
    using Volo.Abp.Localization;
    using Volo.Abp.ObjectExtending;
    
    ObjectExtensionManager.Instance.Modules()
        .ConfigureIdentity(identity =>
        {
            identity.ConfigureUser(user =>
            {
                user.AddOrUpdateProperty<string>(
                    "LastName",
                    property =>
                    {
                        property.Attributes.Add(new StringLengthAttribute(64));
                        property.DisplayName = new FixedLocalizableString("Last Name");
                        property.UI.OnCreateForm.IsVisible = true;
                        property.UI.OnEditForm.IsVisible = true;
                        property.UI.OnTable.IsVisible = true;
                        property.Configuration[IdentityModuleExtensionConsts.ConfigurationNames.AllowUserToEdit] = true;
                    });
            });
        });
    

    In *.EntityFrameworkCore/YourProjectNameEfCoreEntityExtensionMappings.cs:

    ObjectExtensionManager.Instance
        .MapEfCoreProperty<IdentityUser, string>(
            "LastName",
            (entityBuilder, propertyBuilder) =>
            {
                propertyBuilder.HasMaxLength(64);
            });
    

    Then Add-Migration Added_LastName_To_AbpUsers + run DbMigrator.

    The AllowUserToEdit = true line is what makes LastName automatically render on /Account/Manage (Personal info tab) — no extra code needed for that page.

    2. Show Surname as "Middle Name" only on the UI

    The Surname label is read from two different localization resources:

    • AccountResource — used on /Account/Manage (Pro Account module)
    • IdentityResource — used on /Identity/Users admin pages

    Override both for the change to be consistent. Don't translate or rename the property in code, just override the localization keys.

    Create Localization/AccountOverrides/en.json and Localization/IdentityOverrides/en.json in your *.AuthServer project, both with:

    {
      "Culture": "en",
      "Texts": {
        "DisplayName:Surname": "Middle Name"
      }
    }
    

    Mark them as EmbeddedResource in the .csproj, then in YourProjectNameAuthServerModule.ConfigureServices:

    Configure<AbpLocalizationOptions>(options =>
    {
        options.Resources
            .Get<Volo.Abp.Account.Localization.AccountResource>()
            .AddVirtualJson("/Localization/AccountOverrides");
    
        options.Resources
            .Get<Volo.Abp.Identity.Localization.IdentityResource>()
            .AddVirtualJson("/Localization/IdentityOverrides");
    });
    

    For other languages just add zh-Hans.json, de.json, etc. in the same folders.

    3. Reorder Register page fields: Name → Middle Name (Surname) → Last Name

    The RegisterModel you'll be subclassing is Volo.Abp.Account.Public.Web.Pages.Account.RegisterModel from the Pro Account module (NuGet package Volo.Abp.Account.Pro.Public.Web). Its default PostInput only contains UserName/EmailAddress/Password/Code — Name and Surname aren't collected during registration out of the box, so you need to extend it.

    In *.AuthServer/Pages/Account/Register.cshtml.cs:

    using System.ComponentModel.DataAnnotations;
    using System.Net.Http;
    using System.Threading.Tasks;
    using Microsoft.AspNetCore.Authentication;
    using Microsoft.AspNetCore.Identity;
    using Microsoft.AspNetCore.Mvc;
    using Microsoft.Extensions.Options;
    using Volo.Abp.Account.ExternalProviders;
    using Volo.Abp.Account.Public.Web;
    using Volo.Abp.Account.Public.Web.Pages.Account;
    using Volo.Abp.Data;
    using Volo.Abp.Identity;
    using Volo.Abp.Security.Claims;
    using IdentityUser = Volo.Abp.Identity.IdentityUser;
    
    namespace YourCompany.YourProject.AuthServer.Pages.Account;
    
    public class CustomRegisterModel : RegisterModel
    {
        [BindProperty]
        public new CustomPostInput Input { get; set; } = new();
    
        public CustomRegisterModel(
            IAuthenticationSchemeProvider schemeProvider,
            IOptions<AbpAccountOptions> accountOptions,
            IAccountExternalProviderAppService accountExternalProviderAppService,
            ICurrentPrincipalAccessor currentPrincipalAccessor,
            IHttpClientFactory httpClientFactory)
            : base(schemeProvider, accountOptions, accountExternalProviderAppService, currentPrincipalAccessor, httpClientFactory)
        {
        }
    
        public override Task<IActionResult> OnPostAsync()
        {
            // Sync the extended input back to the base Input so base.OnPostAsync sees UserName/Email/Password/Code.
            base.Input = new PostInput
            {
                UserName = Input.UserName,
                EmailAddress = Input.EmailAddress,
                Password = Input.Password,
                Code = Input.Code
            };
            return base.OnPostAsync();
        }
    
        protected override async Task<IdentityUser> RegisterLocalUserAsync()
        {
            // Base call creates the user via AccountAppService.RegisterAsync (UserName/Email/Password only).
            var user = await base.RegisterLocalUserAsync();
    
            user.Name = Input.Name;
            user.Surname = Input.Surname;
            user.SetProperty("LastName", Input.LastName);
    
            (await UserManager.UpdateAsync(user)).CheckErrors();
            return user;
        }
    
        public class CustomPostInput : PostInput
        {
            [Required]
            [StringLength(64)]
            public string Name { get; set; }
    
            [StringLength(64)]
            public string Surname { get; set; }
    
            [StringLength(64)]
            public string LastName { get; set; }
        }
    }
    

    Then *.AuthServer/Pages/Account/Register.cshtml (copy the original from Volo.Abp.Account.Pro.Public.Web/Pages/Account/Register.cshtml and adjust). The important parts:

    @model YourCompany.YourProject.AuthServer.Pages.Account.CustomRegisterModel
    
    ...
    
    @* keep the default UserName / Email / Password blocks here *@
    
    @* === Custom fields in the order you want === *@
    <div class="form-floating mb-2">
        <input asp-for="Input.Name" type="text" class="form-control" placeholder="Name">
        <label asp-for="Input.Name">Name</label>
        <span asp-validation-for="Input.Name" />
    </div>
    <div class="form-floating mb-2">
        <input asp-for="Input.Surname" type="text" class="form-control" placeholder="Middle Name">
        <label asp-for="Input.Surname">Middle Name</label>
        <span asp-validation-for="Input.Surname" />
    </div>
    <div class="form-floating mb-3">
        <input asp-for="Input.LastName" type="text" class="form-control" placeholder="Last Name">
        <label asp-for="Input.LastName">Last Name</label>
        <span asp-validation-for="Input.LastName" />
    </div>
    

    ABP's Razor Pages override is path-based: a same-path .cshtml in the AuthServer project takes precedence over the one shipped in Volo.Abp.Account.Pro.Public.Web.

    Quick recap on file locations (tiered)

    | File | Project | |---|---| | ModuleExtensionConfigurator (LastName property) | *.Domain.Shared | | EfCoreEntityExtensionMappings + Migration | *.EntityFrameworkCore | | Register.cshtml + Register.cshtml.cs | *.AuthServer | | AccountOverrides/*.json + IdentityOverrides/*.json + Configure<AbpLocalizationOptions> | *.AuthServer |

    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 punta,

    Since your solution is tiered, I'll call out where each file goes.

    1. Add a physical LastName column on AbpUsers

    In *.Domain.Shared/YourProjectNameModuleExtensionConfigurator.cs:

    using System.ComponentModel.DataAnnotations;
    using Volo.Abp.Identity;
    using Volo.Abp.Localization;
    using Volo.Abp.ObjectExtending;
    
    ObjectExtensionManager.Instance.Modules()
        .ConfigureIdentity(identity =>
        {
            identity.ConfigureUser(user =>
            {
                user.AddOrUpdateProperty<string>(
                    "LastName",
                    property =>
                    {
                        property.Attributes.Add(new StringLengthAttribute(64));
                        property.DisplayName = new FixedLocalizableString("Last Name");
                        property.UI.OnCreateForm.IsVisible = true;
                        property.UI.OnEditForm.IsVisible = true;
                        property.UI.OnTable.IsVisible = true;
                        property.Configuration[IdentityModuleExtensionConsts.ConfigurationNames.AllowUserToEdit] = true;
                    });
            });
        });
    

    In *.EntityFrameworkCore/YourProjectNameEfCoreEntityExtensionMappings.cs:

    ObjectExtensionManager.Instance
        .MapEfCoreProperty<IdentityUser, string>(
            "LastName",
            (entityBuilder, propertyBuilder) =>
            {
                propertyBuilder.HasMaxLength(64);
            });
    

    Then Add-Migration Added_LastName_To_AbpUsers + run DbMigrator.

    The AllowUserToEdit = true line is what makes LastName automatically render on /Account/Manage (Personal info tab) — no extra code needed for that page.

    2. Show Surname as "Middle Name" only on the UI

    The Surname label is read from two different localization resources:

    • AccountResource — used on /Account/Manage (Pro Account module)
    • IdentityResource — used on /Identity/Users admin pages

    Override both for the change to be consistent. Don't translate or rename the property in code, just override the localization keys.

    Create Localization/AccountOverrides/en.json and Localization/IdentityOverrides/en.json in your *.AuthServer project, both with:

    {
      "Culture": "en",
      "Texts": {
        "DisplayName:Surname": "Middle Name"
      }
    }
    

    Mark them as EmbeddedResource in the .csproj, then in YourProjectNameAuthServerModule.ConfigureServices:

    Configure<AbpLocalizationOptions>(options =>
    {
        options.Resources
            .Get<Volo.Abp.Account.Localization.AccountResource>()
            .AddVirtualJson("/Localization/AccountOverrides");
    
        options.Resources
            .Get<Volo.Abp.Identity.Localization.IdentityResource>()
            .AddVirtualJson("/Localization/IdentityOverrides");
    });
    

    For other languages just add zh-Hans.json, de.json, etc. in the same folders.

    3. Reorder Register page fields: Name → Middle Name (Surname) → Last Name

    The RegisterModel you'll be subclassing is Volo.Abp.Account.Public.Web.Pages.Account.RegisterModel from the Pro Account module (NuGet package Volo.Abp.Account.Pro.Public.Web). Its default PostInput only contains UserName/EmailAddress/Password/Code — Name and Surname aren't collected during registration out of the box, so you need to extend it.

    In *.AuthServer/Pages/Account/Register.cshtml.cs:

    using System.ComponentModel.DataAnnotations;
    using System.Net.Http;
    using System.Threading.Tasks;
    using Microsoft.AspNetCore.Authentication;
    using Microsoft.AspNetCore.Identity;
    using Microsoft.AspNetCore.Mvc;
    using Microsoft.Extensions.Options;
    using Volo.Abp.Account.ExternalProviders;
    using Volo.Abp.Account.Public.Web;
    using Volo.Abp.Account.Public.Web.Pages.Account;
    using Volo.Abp.Data;
    using Volo.Abp.Identity;
    using Volo.Abp.Security.Claims;
    using IdentityUser = Volo.Abp.Identity.IdentityUser;
    
    namespace YourCompany.YourProject.AuthServer.Pages.Account;
    
    public class CustomRegisterModel : RegisterModel
    {
        [BindProperty]
        public new CustomPostInput Input { get; set; } = new();
    
        public CustomRegisterModel(
            IAuthenticationSchemeProvider schemeProvider,
            IOptions<AbpAccountOptions> accountOptions,
            IAccountExternalProviderAppService accountExternalProviderAppService,
            ICurrentPrincipalAccessor currentPrincipalAccessor,
            IHttpClientFactory httpClientFactory)
            : base(schemeProvider, accountOptions, accountExternalProviderAppService, currentPrincipalAccessor, httpClientFactory)
        {
        }
    
        public override Task<IActionResult> OnPostAsync()
        {
            // Sync the extended input back to the base Input so base.OnPostAsync sees UserName/Email/Password/Code.
            base.Input = new PostInput
            {
                UserName = Input.UserName,
                EmailAddress = Input.EmailAddress,
                Password = Input.Password,
                Code = Input.Code
            };
            return base.OnPostAsync();
        }
    
        protected override async Task<IdentityUser> RegisterLocalUserAsync()
        {
            // Base call creates the user via AccountAppService.RegisterAsync (UserName/Email/Password only).
            var user = await base.RegisterLocalUserAsync();
    
            user.Name = Input.Name;
            user.Surname = Input.Surname;
            user.SetProperty("LastName", Input.LastName);
    
            (await UserManager.UpdateAsync(user)).CheckErrors();
            return user;
        }
    
        public class CustomPostInput : PostInput
        {
            [Required]
            [StringLength(64)]
            public string Name { get; set; }
    
            [StringLength(64)]
            public string Surname { get; set; }
    
            [StringLength(64)]
            public string LastName { get; set; }
        }
    }
    

    Then *.AuthServer/Pages/Account/Register.cshtml (copy the original from Volo.Abp.Account.Pro.Public.Web/Pages/Account/Register.cshtml and adjust). The important parts:

    @model YourCompany.YourProject.AuthServer.Pages.Account.CustomRegisterModel
    
    ...
    
    @* keep the default UserName / Email / Password blocks here *@
    
    @* === Custom fields in the order you want === *@
    <div class="form-floating mb-2">
        <input asp-for="Input.Name" type="text" class="form-control" placeholder="Name">
        <label asp-for="Input.Name">Name</label>
        <span asp-validation-for="Input.Name" />
    </div>
    <div class="form-floating mb-2">
        <input asp-for="Input.Surname" type="text" class="form-control" placeholder="Middle Name">
        <label asp-for="Input.Surname">Middle Name</label>
        <span asp-validation-for="Input.Surname" />
    </div>
    <div class="form-floating mb-3">
        <input asp-for="Input.LastName" type="text" class="form-control" placeholder="Last Name">
        <label asp-for="Input.LastName">Last Name</label>
        <span asp-validation-for="Input.LastName" />
    </div>
    

    ABP's Razor Pages override is path-based: a same-path .cshtml in the AuthServer project takes precedence over the one shipped in Volo.Abp.Account.Pro.Public.Web.

    Quick recap on file locations (tiered)

    | File | Project | |---|---| | ModuleExtensionConfigurator (LastName property) | *.Domain.Shared | | EfCoreEntityExtensionMappings + Migration | *.EntityFrameworkCore | | Register.cshtml + Register.cshtml.cs | *.AuthServer | | AccountOverrides/*.json + IdentityOverrides/*.json + Configure<AbpLocalizationOptions> | *.AuthServer |

    Thanks

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

    Thanks a lot, It is something wider than what I thought! Let me work on it accordingly.

    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

    ok : )

    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 28, 2026, 11:44
1
ABP Assistant
🔐 You need to be logged in to use the chatbot. Please log in first.