DX.Data.Xpo.Identity
26.1.3.40
dotnet add package DX.Data.Xpo.Identity --version 26.1.3.40
NuGet\Install-Package DX.Data.Xpo.Identity -Version 26.1.3.40
<PackageReference Include="DX.Data.Xpo.Identity" Version="26.1.3.40" />
<PackageVersion Include="DX.Data.Xpo.Identity" Version="26.1.3.40" />
<PackageReference Include="DX.Data.Xpo.Identity" />
paket add DX.Data.Xpo.Identity --version 26.1.3.40
#r "nuget: DX.Data.Xpo.Identity, 26.1.3.40"
#:package DX.Data.Xpo.Identity@26.1.3.40
#addin nuget:?package=DX.Data.Xpo.Identity&version=26.1.3.40
#tool nuget:?package=DX.Data.Xpo.Identity&version=26.1.3.40
DX.Data.Xpo.Identity
Abstract XPO-based storage for ASP.NET Core Identity (Microsoft.AspNetCore.Identity) / legacy ASP.NET Identity, supporting the full dozen-plus database engines XPO itself supports (SQL Server, PostgreSQL, MySQL, SQLite, Oracle, DB2, Firebird, and more). This package contains no mapper wiring — install DX.Data.Xpo.Identity.AutoMapper or DX.Data.Xpo.Identity.Mapster (not both) on top of it to get a ready-to-register set of IUserStore/IRoleStore implementations.
Target frameworks: net462, net8.0, net9.0, net10.0
DevExpress dependency: DevExpress.Xpo 26.1.*
Behaviour changes in 26.1.3.40
- Security: updates of existing user tokens are now saved. Before, a redeemed two-factor recovery code stayed valid, and resetting the authenticator key or regenerating recovery codes had no effect.
LockoutEndis persisted, so users who reach the failed-attempt limit are now actually locked out.- The ConcurrencyStamp is checked: updating a stale copy of a user or role fails with
ConcurrencyFailure(as the EF Core stores do), and roles get a new stamp on update too. AddToRoleAsyncwith an unknown role or user throwsInvalidOperationExceptioninstead of silently succeeding.- If the token store fails,
SetAuthenticationTokenAsync,ResetAuthenticatorKeyAsyncandGenerateNewTwoFactorRecoveryCodesAsyncthrow instead of reporting success. - Fixed:
AddLoginAsync(threw once any login existed),GetUsersForClaimAsync(always threw),RoleManager.Roles(NotImplementedException); cancellation tokens are honoured and null arguments throwArgumentNullException. - Performance: adding/removing a role membership writes the link row directly,
RemoveClaimsAsyncfilters in the database (on a case-insensitive collation it also removes claims differing only in case), and deleting a user or role on a SQL database no longer loads every member of each role.
Install
dotnet add package DX.Data.Xpo.Identity
You'll almost always want DX.Data.Xpo.Identity.AutoMapper or DX.Data.Xpo.Identity.Mapster instead of (well, on top of) this package directly — see the root README for the "pick one, not both" note.
Interfaces (XPInterfaces.cs)
The persistent-object contracts your XPO entity classes must implement:
public interface IXPUser<TKey>
{
string NormalizedUserName { get; set; }
string NormalizedEmail { get; set; }
string Email { get; set; }
string PasswordHash { get; set; }
string SecurityStamp { get; set; }
string PhoneNumber { get; set; }
bool TwoFactorEnabled { get; set; }
bool LockoutEnabled { get; set; }
string RefreshToken { get; set; } // ties into DX.Data.IIdentityRefreshToken
DateTime? RefreshTokenExpiryTime { get; set; }
IList RolesList { get; }
IList ClaimsList { get; }
IList LoginsList { get; }
IList TokenList { get; }
// ...
}
public interface IXPRole<TKey> { /* Name, NormalizedName, ... */ }
public interface IXPUserRole<TKey> { /* ... */ }
public interface IXPUserLogin<TKey> { void InitializeUserLogin(...); /* ... */ }
public interface IXPBaseClaim<TKey> { Claim ToClaim(); void InitializeFromClaim(Claim claim); }
public interface IXPUserClaim<TKey> : IXPBaseClaim<TKey> { void InitializeUserClaim(...); }
public interface IXPRoleClaim<TKey> : IXPBaseClaim<TKey> { void InitializeRoleClaim(...); }
public interface IXPUserToken<TKey> { /* Name, Value, LoginProvider ... */ }
Then the "queryable store" interfaces (each extending IQueryableDataStore<TKey, TModel> from DX.Data) that the mapper packages implement concretely:
public interface IQueryableUserStore<TKey, TUser, TUserRole, TUserToken> : IQueryableDataStore<TKey, TUser> { /* FindByUserNameAsync, AddToRoleAsync, GetRolesAsync, ... */ }
public interface IQueryableRoleStore<TKey, TRole> : IQueryableDataStore<TKey, TRole> { /* FindByNameAsync, ... */ }
public interface IQueryableUserClaimStore<TKey, TUserClaim> : IQueryableDataStore<TKey, TUserClaim> { /* GetUserClaimsAsync, ... */ }
public interface IQueryableRoleClaimStore<TKey, TRoleClaim> : IQueryableDataStore<TKey, TRoleClaim> { /* GetRoleClaimsAsync, ... */ }
public interface IQueryableUserLoginStore<TKey, TUserLogin> : IQueryableDataStore<TKey, TUserLogin> { /* FindUserLoginAsync, ... */ }
public interface IQueryableUserTokenStore<TKey, TUserToken> : IQueryableDataStore<TKey, TUserToken> { /* FindTokenAsync, ... */ }
Abstract store base classes (XPIdentityDataStores.cs)
Six abstract classes, each XPDataStore<...> + the matching IQueryable*Store interface, implementing every Identity operation (AddClaimsAsync, AddLoginAsync, AddToRoleAsync, GetRolesAsync, IsInRoleAsync, RemoveClaimsAsync, ReplaceClaimAsync, SetPasswordHashAsync, GetSecurityStampAsync, etc.) against XPO's XPCollection/CriteriaOperator:
XPBaseUserStore<TKey, TUser, TRole, TUserClaim, TUserRole, TUserLogin, TUserToken, TRoleClaim, TXPOUser, TXPORole, TXPOLogin, TXPOClaim, TXPOToken>XPBaseRoleStore<...>XPBaseUserLoginStore<...>XPBaseUserClaimStore<...>XPBaseUserTokenStore<...>XPBaseRoleClaimStore<...>
These are the classes DX.Data.Xpo.Identity.AutoMapper/.Mapster derive from (XPAutoMapperUserStore, XPMapsterUserStore, etc.), plugging in the mapper-specific ToModel/ToDBModel/Query<T> implementations.
Concrete ASP.NET Core Identity adapters (XPUserStore.cs, XPRoleStore.cs, #if NETCOREAPP)
public class XPUserStore<TKey, TUser, TRole, TUserClaim, TUserRole, TUserLogin, TUserToken, TRoleClaim,
TXPOUser, TXPORole, TXPOLogin, TXPOClaim, TXPOToken>
: UserStoreBase<TUser, TRole, TKey, TUserClaim, TUserRole, TUserLogin, TUserToken, TRoleClaim>
{
public XPUserStore(IQueryableUserStore<...> userStore, IQueryableRoleStore<...> roleStore,
IQueryableUserClaimStore<...> claimStore, IQueryableUserLoginStore<...> loginStore,
IQueryableUserTokenStore<...> tokenStore, IdentityErrorDescriber? describer = null);
}
public class XPRoleStore<TKey, TRole, TUserRole, TRoleClaim, TXPORole, TXPOClaim>
: RoleStoreBase<TRole, TKey, TUserRole, TRoleClaim>
{
public XPRoleStore(IQueryableRoleStore<TKey, TRole> roleStore, IQueryableRoleClaimStore<TKey, TRoleClaim> roleClaimStore,
IdentityErrorDescriber? describer = null);
}
These are the actual Microsoft.AspNetCore.Identity.UserStoreBase/RoleStoreBase implementations that get registered as IUserStore<TUser>/IRoleStore<TRole> — they compose the six IQueryable*Store services above rather than talking to XPO directly. You don't normally construct these yourself; RegisterServices.AddStores<TKey>(...) builds and registers them for you via reflection based on the concrete types you supply.
A non-.NET-Core legacy XPUserStore<...> (targeting Microsoft.AspNet.Identity's classic IUserStore<TUser, TKey> + friends) also exists in the same file for old-style ASP.NET Identity 2.x consumers.
RegisterServices.cs — DI wiring
public static class RegisterIdentityServices
{
public static void RegisterXPIdentityValidators<...>(this IServiceCollection services); // registers 6 FluentValidation validators
public static IServiceCollection AddStores<TKey>(this IServiceCollection services, /* concrete user/role/etc. type params */);
}
AddStores<TKey> is the reflection-heavy method that builds closed generic XPUserStore<...>/XPRoleStore<...> types via MakeGenericType and registers them as IUserStore<>/IRoleStore<> with TryAddScoped — resolving XpoDatabase either from an already-registered instance or by building one scoped to a specific connection name. You typically don't call this directly either; DX.Data.Xpo.Identity.AutoMapper's AddXpoAutoMapperIdentityStores(...) (or the Mapster equivalent) calls it for you as the last step of a one-line registration.
Persistent entity classes (XpoDiagramCode/)
Default, ready-to-use XPO persistent classes implementing the interfaces above: XpoDxUser, XpoDxRole, XpoDxUserClaim, XpoDxUserLogin, XpoDxUserToken, XpoDxRoleClaim, and the shared XpoDxBase/XpoDxBaseClaim. These live in the DX.Data.Xpo.Identity.Persistent namespace and are what AddXpoAutoMapperIdentityStores/AddXpoMapsterIdentityStores default to if you don't supply your own persistent types. XpoDxUser overrides OnDeleting to detach the user from all roles and delete its claims before the user record itself is removed (referential-integrity cleanup XPO doesn't do for you automatically on a many-to-many).
Notes
- Since
23.2.3.31you install exactly one ofDX.Data.Xpo.Identity.AutoMapper/DX.Data.Xpo.Identity.Mapsteralongside this package — never both. Bits.csdefinesDxIdentityRoleFlags(FLAG_USERS,FLAG_FULL), a legacy loading-flags scheme from an older, non-reflection-based registration path that's mostly superseded now.XPUserMapper<...>/XPRoleMapper<...>andXPRoleStoreValidator<...>are[Obsolete]/commented-out legacy types — use theAutoMapper/Mapsterpackages' mapping instead.
See the root README for the quick-start DI configuration sample and full package list.
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net8.0 is compatible. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. net9.0 is compatible. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 is compatible. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
| .NET Framework | net462 is compatible. net463 was computed. net47 was computed. net471 was computed. net472 was computed. net48 was computed. net481 was computed. |
-
.NETFramework 4.6.2
- DevExpress.Xpo (>= 26.1.6)
- DX.Data.Xpo (>= 26.1.3.40)
- DX.Utils (>= 26.1.3.41)
- FluentValidation (>= 9.5.4)
- Microsoft.AspNet.Identity.Core (>= 2.2.4)
-
net10.0
- DevExpress.Xpo (>= 26.1.6)
- DX.Data.Xpo (>= 26.1.3.40)
- DX.Utils (>= 26.1.3.41)
- FluentValidation (>= 12.1.1)
- Microsoft.Extensions.Caching.Abstractions (>= 10.0.14)
- Microsoft.Extensions.Configuration.Abstractions (>= 10.0.14)
- Microsoft.Extensions.Identity.Core (>= 10.0.14)
- Microsoft.Extensions.Identity.Stores (>= 10.0.14)
- Microsoft.Extensions.Logging (>= 10.0.14)
-
net8.0
- DevExpress.Xpo (>= 26.1.6)
- DX.Data.Xpo (>= 26.1.3.40)
- DX.Utils (>= 26.1.3.41)
- FluentValidation (>= 12.1.1)
- Microsoft.Extensions.Caching.Abstractions (>= 10.0.14)
- Microsoft.Extensions.Configuration.Abstractions (>= 10.0.14)
- Microsoft.Extensions.Identity.Core (>= 8.0.33)
- Microsoft.Extensions.Identity.Stores (>= 8.0.33)
- Microsoft.Extensions.Logging (>= 10.0.14)
-
net9.0
- DevExpress.Xpo (>= 26.1.6)
- DX.Data.Xpo (>= 26.1.3.40)
- DX.Utils (>= 26.1.3.41)
- FluentValidation (>= 12.1.1)
- Microsoft.Extensions.Caching.Abstractions (>= 10.0.14)
- Microsoft.Extensions.Configuration.Abstractions (>= 10.0.14)
- Microsoft.Extensions.Identity.Core (>= 9.0.22)
- Microsoft.Extensions.Identity.Stores (>= 9.0.22)
- Microsoft.Extensions.Logging (>= 10.0.14)
NuGet packages (3)
Showing the top 3 NuGet packages that depend on DX.Data.Xpo.Identity:
| Package | Downloads |
|---|---|
|
DX.Blazor.Identity
Blazor Server and WASM Identity Authentication Library |
|
|
DX.Data.Xpo.Identity.Mapster
XPO Based storage provider for ASP.NET Identity with Mapster implementation |
|
|
DX.Data.Xpo.Identity.AutoMapper
XPO Based storage provider for ASP.NET Identity with Automapper implementation |
GitHub repositories
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|---|---|
| 26.1.3.40 | 114 | 11/4/2026 |
| 26.1.3.39 | 193 | 10/26/2026 |
| 26.1.3.38 | 174 | 10/14/2026 |
| 26.1.3.37 | 239 | 8/6/2026 |
| 26.1.3.36 | 237 | 7/31/2026 |
| 26.1.3.35 | 239 | 7/14/2026 |
| 24.2.3.34 | 437 | 12/13/2024 |
| 24.1.6.34 | 386 | 10/19/2024 |
| 24.1.6.33 | 307 | 9/25/2024 |
| 24.1.5.33 | 353 | 9/17/2024 |
| 24.1.5.32 | 330 | 9/17/2024 |
| 23.2.3.31 | 431 | 1/11/2024 |
| 23.2.3.19 | 378 | 12/19/2023 |
| 23.2.3.18 | 303 | 12/14/2023 |
| 23.1.3.17 | 482 | 6/16/2023 |
| 22.2.3.17 | 854 | 1/4/2023 |
| 22.1.6.17 | 536 | 11/29/2022 |
| 22.1.4.17 | 567 | 11/1/2022 |
| 22.1.4.16 | 912 | 8/3/2022 |
| 21.2.8.16 | 871 | 6/16/2022 |
26.1.3.40: Security and bug fixes found by a new unit test suite. SECURITY: updates of existing user tokens were never saved, so a redeemed two-factor recovery code stayed valid, and resetting the authenticator key or regenerating recovery codes had no effect. Fixed: LockoutEnd was not persisted, so lockout after failed attempts never took effect; the ConcurrencyStamp was not checked (an update from a stale copy now fails with ConcurrencyFailure, as in EF Core, and roles get a new stamp too); AddLoginAsync threw 'Custom function Equals not found' once any login existed; GetUsersForClaimAsync always threw; AddToRole with an unknown role silently succeeded (now InvalidOperationException); RoleManager.Roles threw NotImplementedException; FindUserRoleAsync recursed infinitely; CancellationTokens are honoured and null arguments throw ArgumentNullException; the legacy XPUserMapper/XPRoleMapper keep the ConcurrencyStamp. Performance: AddToRole/RemoveFromRole write the link row directly instead of loading every member of the role; RemoveClaims filters in the database; deleting a user or role removes its role links inside a database transaction first, so it no longer loads every member of each role (role of 3,000 users on SQLite: user delete 60 to 8 ms, role delete 566 to 74 ms). Behaviour changes: AddToRole with an unknown role or user throws; stale updates fail with ConcurrencyFailure; a failing token store makes SetAuthenticationToken/ResetAuthenticatorKey/GenerateNewTwoFactorRecoveryCodes throw instead of reporting success; users reaching the failed-attempt limit are now actually locked out.
26.1.3.39: Microsoft.Extensions.Identity.Core/Stores per target framework (8.0 for net8.0, 9.0 for net9.0, 10.0 for net10.0): the 10.0 packages put a newer Microsoft.AspNetCore.Cryptography.Internal in a net8.0 app, whose Data Protection then failed to decrypt antiforgery tokens on Linux. Also built against DX.Data.Xpo 26.1.3.39 (System.Security.Cryptography.Xml 8.0.5).
26.1.3.37: Rebuilt against DX.Data.Xpo 26.1.3.38 to pick up the System.Security.Cryptography.Xml 8.0.4 fix (NU1903) transitively.
26.1.3.36: Added a README.md with technical documentation, now embedded in the package via PackageReadmeFile.
26.1.3.35: Upgrade to DevExpress v26.1.3
24.2.3.34: Dropped NET461, NET6 and NET7 support, Upgrade to DevExpress v24.2.3
24.1.5.34: Typo ID -> Id fixed in XPBaseUserStore.AddToRoleAsync
24.1.5.32: Drop .NET Framework support, Upgrade to DevExpress v24.1.5
23.2.3.31: Upgraded complete package with use of Microsoft.Extensions.Identity base packages, support for .NET/Blazor 8 use newer DataStore approach
Easier registration in Startup and DTO Models don't need to implement IXPUser interfaces anymore.
23.2.3.19: NETStandard2.1 Removed
23.2.3.18: Upgraded to .NET 8 and DX v23.2.3
23.1.3.22: Upgraded to DX v23.1.3
22.2.3.22: Upgraded to DX v22.2.3
22.1.6.22: Upgraded several packages incl DX v22.1.6
22.1.4.17: Upgrade from .NET Framework to 4.6.1 to 4.6.2
22.1.4.16: Upgrade to DevExpress v22.1.4
21.2.8.19: IQuery Users properly implemented, DX Upgrade v21.2.8 and preparations for Blazor Identity Support
21.2.4.14: .NET 6.0 and DevExpress v21.2.4 upgrade
21.1.5.12: Upgrade to DevExpress v21.1.5
20.1.7.12: Fix on XpoDxRole and XpoDxUser. When Role with users is deleted, users are detached first, before role is deleted
20.1.7.11: Upgrade to DX v10.1.7. Fix on XPUserStore.Users and XPRoleStore.Roles, better exception raising, validation results improved, batch inserts/updates improved, support for Store (insert key = default(TKey) or update) in batch
20.1.3.10: Upgrade to DX v20.1.3
20.1.2.9-pre-20064: Upgrade to DX v20.1.2-pre-20064 and .NET preview 5
19.2.6.9: DI Extensionmethod now uses an options object for initialisation. (Recommended way)
Other refactorings done as well with this
19.2.6.8: Upgrade to DX v19.2.6
19.2.5.8: Signature-change on IDataValidator<..>.Deleting(..)
19.2.5.7: dotnet standard v2.1 for dotnet core v3.1 support + upgrade to DX v19.2.5
19.2.4.6: Upgrade to DevExpress v19.2.4
19.1.5.6: Upgrade to DevExpress v19.1.5 and XPDataStore constructor uses interface types for mapper and validator
19.1.4.4: Upgrade to DevExpress v19.1.4
19.1.3.5: Several fixes and adjustments to get the DataMapper and Validator to work on DI for .NET Core
19.1.3.4: Upgrade to DevExpress v19.1.3 and fixes on DTO Mapper and Validator design patterns in the Stores
18.2.3.4: Upgrade to DevExpress v18.2.3
18.1.4.3: BREAKING CHANGE ON EXISTING DATA: Rename table XpoDxUserToken in your db to DXUserTokens !!
18.1.4.2: XPUserStore FindByIdAsync(TKey userId) didn't call FindByIdAsync(object userId) because of invalid cast for .NET Framework
18.1.4.1: Upgrade to DevExpress v18.1.4 and bug fixed on LockoutEndDateUtc.
17.2.6.5: Small bug fixed in extension method to configure storage provider
17.2.6.4: Database Namechange on BaseClaim, UserClaim and RoleClaim
17.2.6.3: Upgraded to DevExpress v17.2.6
17.2.5.2: DI Extension methods for .NET Core 2 added and upgraded to DevExpress v17.2.5
17.2.4.3: Initial .NET Core v2 support
17.2.4.2: Upgrade to DevExpress v17.2.4 and changed .NET Framework to v4.6.1
17.2.3.1: Initial dual mode package for .NET Framework and .NET Standard 2.0
Not operational on .NET Standard 2.0 yet