diff --git a/src/Extensions.Hosting.Identity.EntityFrameworkCore.Sqlite/HostBuilderEntityFrameworkCoreExtensions.cs b/src/Extensions.Hosting.Identity.EntityFrameworkCore.Sqlite/HostBuilderEntityFrameworkCoreExtensions.cs
index 5d8da84e..70998d68 100644
--- a/src/Extensions.Hosting.Identity.EntityFrameworkCore.Sqlite/HostBuilderEntityFrameworkCoreExtensions.cs
+++ b/src/Extensions.Hosting.Identity.EntityFrameworkCore.Sqlite/HostBuilderEntityFrameworkCoreExtensions.cs
@@ -2,6 +2,8 @@
// ReactiveUI Association Incorporated licenses this file to you under the MIT license.
// See the LICENSE file in the project root for full license information.
+using System.Diagnostics.CodeAnalysis;
+
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Identity;
@@ -24,6 +26,119 @@ public static class HostBuilderEntityFrameworkCoreExtensions
/// The receiver instance.
extension(IHostApplicationBuilder builder)
{
+ /// Configures Entity Framework Core with SQLite and ASP.NET Core Identity using IHostApplicationBuilder.
+ /// This method provides integration with the modern IHostApplicationBuilder pattern and
+ /// sets up both Entity Framework Core and ASP.NET Core Identity with the specified user and role types.
+ /// The type of the Entity Framework Core DbContext to use for data access.
+ /// The type representing application users for ASP.NET Core Identity.
+ /// The type representing application roles for ASP.NET Core Identity.
+ /// The name of the connection string in the configuration. Cannot be null or whitespace.
+ /// The same IHostApplicationBuilder instance so that additional calls can be chained.
+ [SuppressMessage(
+ "Design",
+ "SST2307",
+ Justification = "Explicit Identity types preserve the public overload signatures released in version 4.1.0.")]
+ public IHostApplicationBuilder AddSqliteWithIdentity<
+ [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] TContext,
+ TUser,
+ TRole>(string connectionStringName)
+ where TContext : DbContext
+ where TUser : class
+ where TRole : class =>
+ builder.AddSqliteWithIdentity(connectionStringName, ServiceLifetime.Scoped);
+
+ /// Configures SQLite and Identity with an explicit service lifetime.
+ /// The Entity Framework Core DbContext type.
+ /// The Identity user type.
+ /// The Identity role type.
+ /// The configured connection string name.
+ /// The lifetime with which to register the DbContext service.
+ /// The same IHostApplicationBuilder instance so that additional calls can be chained.
+ /// Thrown if connectionStringName is null or consists only of white-space characters.
+ /// Thrown if the specified connection string is not found in the configuration.
+ [SuppressMessage(
+ "Design",
+ "SST2307",
+ Justification = "Explicit Identity types preserve the public overload signatures released in version 4.1.0.")]
+ public IHostApplicationBuilder AddSqliteWithIdentity<
+ [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] TContext,
+ TUser,
+ TRole>(
+ string connectionStringName,
+ ServiceLifetime serviceLifetime)
+ where TContext : DbContext
+ where TUser : class
+ where TRole : class
+ {
+ _ = builder ?? throw new ArgumentNullException(nameof(builder));
+
+ ArgumentException.ThrowIfNullOrWhiteSpace(connectionStringName);
+
+ var conString = builder.Configuration.GetConnectionString(connectionStringName);
+ _ = builder.Services
+ .AddDbContext(
+ options => options.UseSqlite(conString ?? throw new InvalidOperationException($"Connection string '{connectionStringName}' not found.")),
+ serviceLifetime,
+ serviceLifetime == ServiceLifetime.Singleton ? ServiceLifetime.Singleton : ServiceLifetime.Scoped)
+ .AddDefaultIdentity()
+ .AddRoles()
+ .AddEntityFrameworkStores();
+ return builder;
+ }
+
+ /// Configures Entity Framework Core with SQLite and ASP.NET Core Identity (user only) using IHostApplicationBuilder.
+ /// This method provides integration with the modern IHostApplicationBuilder pattern and
+ /// sets up both Entity Framework Core and ASP.NET Core Identity with the specified user type only.
+ /// The type of the Entity Framework Core DbContext to use for data access.
+ /// The type representing application users for ASP.NET Core Identity.
+ /// The name of the connection string in the configuration. Cannot be null or whitespace.
+ /// The same IHostApplicationBuilder instance so that additional calls can be chained.
+ [SuppressMessage(
+ "Design",
+ "SST2307",
+ Justification = "Explicit Identity types preserve the public overload signatures released in version 4.1.0.")]
+ public IHostApplicationBuilder AddSqliteWithIdentity<
+ [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] TContext,
+ TUser>(string connectionStringName)
+ where TContext : DbContext
+ where TUser : class =>
+ builder.AddSqliteWithIdentity(connectionStringName, ServiceLifetime.Scoped);
+
+ /// Configures SQLite and user-only Identity with an explicit service lifetime.
+ /// The Entity Framework Core DbContext type.
+ /// The Identity user type.
+ /// The configured connection string name.
+ /// The lifetime with which to register the DbContext service.
+ /// The same IHostApplicationBuilder instance so that additional calls can be chained.
+ /// Thrown if connectionStringName is null or consists only of white-space characters.
+ /// Thrown if the specified connection string is not found in the configuration.
+ [SuppressMessage(
+ "Design",
+ "SST2307",
+ Justification = "Explicit Identity types preserve the public overload signatures released in version 4.1.0.")]
+ public IHostApplicationBuilder AddSqliteWithIdentity<
+ [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] TContext,
+ TUser>(
+ string connectionStringName,
+ ServiceLifetime serviceLifetime)
+ where TContext : DbContext
+ where TUser : class
+ {
+ _ = builder ?? throw new ArgumentNullException(nameof(builder));
+
+ ArgumentException.ThrowIfNullOrWhiteSpace(connectionStringName);
+
+ var conString = builder.Configuration.GetConnectionString(connectionStringName);
+ _ = builder.Services
+ .AddDbContext(
+ options => options.UseSqlite(conString ?? throw new InvalidOperationException($"Connection string '{connectionStringName}' not found.")),
+ serviceLifetime,
+ serviceLifetime == ServiceLifetime.Singleton ? ServiceLifetime.Singleton : ServiceLifetime.Scoped)
+ .AddDefaultIdentity()
+ .AddEntityFrameworkStores();
+ return builder;
+ }
+
/// Configures Entity Framework Core with SQLite using the IHostApplicationBuilder pattern.
/// This method provides integration with the modern IHostApplicationBuilder pattern
/// introduced in .NET 7+. It registers the DbContext with SQLite using the connection string
@@ -220,6 +335,142 @@ public IHostBuilder UseWebHostServices(
/// The receiver instance.
extension(IServiceCollection services)
{
+ /// Configures Entity Framework Core with a SQLite provider and sets up ASP.NET Core Identity using the specified context, user, and role types.
+ /// This method registers the DbContext with the SQLite provider and configures ASP.NET Core
+ /// Identity to use the specified user and role types with Entity Framework Core stores. It is typically called
+ /// during application startup to enable authentication and authorization using SQLite as the backing
+ /// store.
+ /// The type of the Entity Framework Core DbContext to use for data access.
+ /// The type representing application users for ASP.NET Core Identity.
+ /// The type representing application roles for ASP.NET Core Identity.
+ /// The web host builder context containing configuration and environment information. Cannot be null.
+ /// The name of the connection string in the configuration to use for the SQLite database. Cannot be null or
+ /// whitespace.
+ /// The same IServiceCollection instance so that additional calls can be chained.
+ [SuppressMessage(
+ "Design",
+ "SST2307",
+ Justification = "Explicit Identity types preserve the public overload signatures released in version 4.1.0.")]
+ public IServiceCollection UseEntityFrameworkCoreSqlite<
+ [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] TContext,
+ TUser,
+ TRole>(
+ WebHostBuilderContext context,
+ string connectionStringName)
+ where TContext : DbContext
+ where TUser : class
+ where TRole : class =>
+ services.UseEntityFrameworkCoreSqlite(
+ context,
+ connectionStringName,
+ ServiceLifetime.Scoped);
+
+ /// Configures SQLite and Identity with an explicit service lifetime.
+ /// The Entity Framework Core DbContext type.
+ /// The Identity user type.
+ /// The Identity role type.
+ /// The web host builder context.
+ /// The configured connection string name.
+ /// The lifetime with which to register the DbContext service.
+ /// The same IServiceCollection instance so that additional calls can be chained.
+ /// Thrown if connectionStringName is null or consists only of white-space characters.
+ /// Thrown if the specified connection string is not found in the configuration.
+ [SuppressMessage(
+ "Design",
+ "SST2307",
+ Justification = "Explicit Identity types preserve the public overload signatures released in version 4.1.0.")]
+ public IServiceCollection UseEntityFrameworkCoreSqlite<
+ [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] TContext,
+ TUser,
+ TRole>(
+ WebHostBuilderContext context,
+ string connectionStringName,
+ ServiceLifetime serviceLifetime)
+ where TContext : DbContext
+ where TUser : class
+ where TRole : class
+ {
+ _ = services ?? throw new ArgumentNullException(nameof(services));
+ _ = context ?? throw new ArgumentNullException(nameof(context));
+
+ ArgumentException.ThrowIfNullOrWhiteSpace(connectionStringName);
+
+ var conString = context.Configuration.GetConnectionString(connectionStringName);
+ _ = services
+ .AddDbContext(
+ options => options.UseSqlite(conString ?? throw new InvalidOperationException($"Connection string '{connectionStringName}' not found.")),
+ serviceLifetime,
+ serviceLifetime == ServiceLifetime.Singleton ? ServiceLifetime.Singleton : ServiceLifetime.Scoped)
+ .AddDefaultIdentity()
+ .AddRoles()
+ .AddEntityFrameworkStores();
+ return services;
+ }
+
+ /// Configures Entity Framework Core with a SQLite provider and ASP.NET Core Identity using the specified DbContext and user type.
+ /// This method registers the specified DbContext with a SQLite provider and configures ASP.NET
+ /// Core Identity to use Entity Framework Core stores. It is typically called during application startup to enable
+ /// authentication and data access using SQLite.
+ /// The type of the DbContext to use for Entity Framework Core operations.
+ /// The type representing the user entity for ASP.NET Core Identity.
+ /// The WebHostBuilderContext containing application configuration and environment information.
+ /// The name of the connection string in the configuration to use for the SQLite database. Cannot be null or
+ /// whitespace.
+ /// The IServiceCollection instance configured for SQLite and Identity.
+ [SuppressMessage(
+ "Design",
+ "SST2307",
+ Justification = "Explicit Identity types preserve the public overload signatures released in version 4.1.0.")]
+ public IServiceCollection UseEntityFrameworkCoreSqlite<
+ [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] TContext,
+ TUser>(
+ WebHostBuilderContext context,
+ string connectionStringName)
+ where TContext : DbContext
+ where TUser : class =>
+ services.UseEntityFrameworkCoreSqlite(
+ context,
+ connectionStringName,
+ ServiceLifetime.Scoped);
+
+ /// Configures SQLite and user-only Identity with an explicit service lifetime.
+ /// The Entity Framework Core DbContext type.
+ /// The Identity user type.
+ /// The web host builder context.
+ /// The configured connection string name.
+ /// The lifetime with which to register the DbContext service.
+ /// The IServiceCollection instance with Entity Framework Core and Identity services configured for SQLite.
+ /// Thrown if connectionStringName is null or consists only of white-space characters.
+ /// Thrown if the specified connection string is not found in the configuration.
+ [SuppressMessage(
+ "Design",
+ "SST2307",
+ Justification = "Explicit Identity types preserve the public overload signatures released in version 4.1.0.")]
+ public IServiceCollection UseEntityFrameworkCoreSqlite<
+ [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] TContext,
+ TUser>(
+ WebHostBuilderContext context,
+ string connectionStringName,
+ ServiceLifetime serviceLifetime)
+ where TContext : DbContext
+ where TUser : class
+ {
+ _ = services ?? throw new ArgumentNullException(nameof(services));
+ _ = context ?? throw new ArgumentNullException(nameof(context));
+
+ ArgumentException.ThrowIfNullOrWhiteSpace(connectionStringName);
+
+ var conString = context.Configuration.GetConnectionString(connectionStringName);
+ _ = services
+ .AddDbContext(
+ options => options.UseSqlite(conString ?? throw new InvalidOperationException($"Connection string '{connectionStringName}' not found.")),
+ serviceLifetime,
+ serviceLifetime == ServiceLifetime.Singleton ? ServiceLifetime.Singleton : ServiceLifetime.Scoped)
+ .AddDefaultIdentity()
+ .AddEntityFrameworkStores();
+ return services;
+ }
+
/// Configures Entity Framework Core with a SQLite provider and caller-provided Identity services.
/// This method registers the DbContext with the SQLite provider and configures ASP.NET Core
/// Identity using the supplied callback. It is typically called during application startup to enable
diff --git a/src/Extensions.Hosting.Identity.EntityFrameworkCore/HostBuilderEntityFrameworkCoreExtensions.cs b/src/Extensions.Hosting.Identity.EntityFrameworkCore/HostBuilderEntityFrameworkCoreExtensions.cs
index cb393975..70bcac1c 100644
--- a/src/Extensions.Hosting.Identity.EntityFrameworkCore/HostBuilderEntityFrameworkCoreExtensions.cs
+++ b/src/Extensions.Hosting.Identity.EntityFrameworkCore/HostBuilderEntityFrameworkCoreExtensions.cs
@@ -2,6 +2,8 @@
// ReactiveUI Association Incorporated licenses this file to you under the MIT license.
// See the LICENSE file in the project root for full license information.
+using System.Diagnostics.CodeAnalysis;
+
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Identity;
@@ -66,6 +68,119 @@ public string GetRequiredConnectionString(string connectionStringName)
/// The receiver instance.
extension(IHostApplicationBuilder builder)
{
+ /// Configures Entity Framework Core with SQL Server and ASP.NET Core Identity using IHostApplicationBuilder.
+ /// This method provides integration with the modern IHostApplicationBuilder pattern and
+ /// sets up both Entity Framework Core and ASP.NET Core Identity with the specified user and role types.
+ /// The type of the Entity Framework Core DbContext to use for data access.
+ /// The type representing application users for ASP.NET Core Identity.
+ /// The type representing application roles for ASP.NET Core Identity.
+ /// The name of the connection string in the configuration. Cannot be null or whitespace.
+ /// The same IHostApplicationBuilder instance so that additional calls can be chained.
+ [SuppressMessage(
+ "Design",
+ "SST2307",
+ Justification = "Explicit Identity types preserve the public overload signatures released in version 4.1.0.")]
+ public IHostApplicationBuilder AddSqlServerWithIdentity<
+ [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] TContext,
+ TUser,
+ TRole>(string connectionStringName)
+ where TContext : DbContext
+ where TUser : class
+ where TRole : class =>
+ builder.AddSqlServerWithIdentity(connectionStringName, ServiceLifetime.Scoped);
+
+ /// Configures SQL Server and Identity with an explicit service lifetime.
+ /// The Entity Framework Core DbContext type.
+ /// The Identity user type.
+ /// The Identity role type.
+ /// The configured connection string name.
+ /// The lifetime with which to register the DbContext service.
+ /// The same IHostApplicationBuilder instance so that additional calls can be chained.
+ /// Thrown if connectionStringName is null or consists only of white-space characters.
+ /// Thrown if the specified connection string is not found in the configuration.
+ [SuppressMessage(
+ "Design",
+ "SST2307",
+ Justification = "Explicit Identity types preserve the public overload signatures released in version 4.1.0.")]
+ public IHostApplicationBuilder AddSqlServerWithIdentity<
+ [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] TContext,
+ TUser,
+ TRole>(
+ string connectionStringName,
+ ServiceLifetime serviceLifetime)
+ where TContext : DbContext
+ where TUser : class
+ where TRole : class
+ {
+ _ = builder ?? throw new ArgumentNullException(nameof(builder));
+
+ ArgumentException.ThrowIfNullOrWhiteSpace(connectionStringName);
+
+ var conString = builder.Configuration.GetConnectionString(connectionStringName);
+ _ = builder.Services
+ .AddDbContext(
+ options => options.UseSqlServer(conString ?? throw new InvalidOperationException($"Connection string '{connectionStringName}' not found.")),
+ serviceLifetime,
+ serviceLifetime == ServiceLifetime.Singleton ? ServiceLifetime.Singleton : ServiceLifetime.Scoped)
+ .AddDefaultIdentity()
+ .AddRoles()
+ .AddEntityFrameworkStores();
+ return builder;
+ }
+
+ /// Configures Entity Framework Core with SQL Server and ASP.NET Core Identity (user only) using IHostApplicationBuilder.
+ /// This method provides integration with the modern IHostApplicationBuilder pattern and
+ /// sets up both Entity Framework Core and ASP.NET Core Identity with the specified user type only.
+ /// The type of the Entity Framework Core DbContext to use for data access.
+ /// The type representing application users for ASP.NET Core Identity.
+ /// The name of the connection string in the configuration. Cannot be null or whitespace.
+ /// The same IHostApplicationBuilder instance so that additional calls can be chained.
+ [SuppressMessage(
+ "Design",
+ "SST2307",
+ Justification = "Explicit Identity types preserve the public overload signatures released in version 4.1.0.")]
+ public IHostApplicationBuilder AddSqlServerWithIdentity<
+ [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] TContext,
+ TUser>(string connectionStringName)
+ where TContext : DbContext
+ where TUser : class =>
+ builder.AddSqlServerWithIdentity(connectionStringName, ServiceLifetime.Scoped);
+
+ /// Configures SQL Server and user-only Identity with an explicit service lifetime.
+ /// The Entity Framework Core DbContext type.
+ /// The Identity user type.
+ /// The configured connection string name.
+ /// The lifetime with which to register the DbContext service.
+ /// The same IHostApplicationBuilder instance so that additional calls can be chained.
+ /// Thrown if connectionStringName is null or consists only of white-space characters.
+ /// Thrown if the specified connection string is not found in the configuration.
+ [SuppressMessage(
+ "Design",
+ "SST2307",
+ Justification = "Explicit Identity types preserve the public overload signatures released in version 4.1.0.")]
+ public IHostApplicationBuilder AddSqlServerWithIdentity<
+ [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] TContext,
+ TUser>(
+ string connectionStringName,
+ ServiceLifetime serviceLifetime)
+ where TContext : DbContext
+ where TUser : class
+ {
+ _ = builder ?? throw new ArgumentNullException(nameof(builder));
+
+ ArgumentException.ThrowIfNullOrWhiteSpace(connectionStringName);
+
+ var conString = builder.Configuration.GetConnectionString(connectionStringName);
+ _ = builder.Services
+ .AddDbContext(
+ options => options.UseSqlServer(conString ?? throw new InvalidOperationException($"Connection string '{connectionStringName}' not found.")),
+ serviceLifetime,
+ serviceLifetime == ServiceLifetime.Singleton ? ServiceLifetime.Singleton : ServiceLifetime.Scoped)
+ .AddDefaultIdentity()
+ .AddEntityFrameworkStores();
+ return builder;
+ }
+
/// Configures Entity Framework Core with SQL Server using the IHostApplicationBuilder pattern.
/// This method provides integration with the modern IHostApplicationBuilder pattern
/// introduced in .NET 7+. It registers the DbContext with SQL Server using the connection string
@@ -261,6 +376,142 @@ public IHostBuilder UseWebHostServices(
/// The receiver instance.
extension(IServiceCollection services)
{
+ /// Configures SQL Server and ASP.NET Core Identity with the specified context, user, and role types.
+ /// This method sets up Entity Framework Core to use SQL Server as the database provider and
+ /// configures ASP.NET Core Identity with the specified user and role types. It is typically called during
+ /// application startup to register data access and identity services. The method also adds the Entity Framework
+ /// stores for Identity, enabling user and role management backed by the specified DbContext.
+ /// The type of the Entity Framework Core DbContext to use for data access.
+ /// The type representing application users for ASP.NET Core Identity. Must be a reference type.
+ /// The type representing application roles for ASP.NET Core Identity. Must be a reference type.
+ /// The web host builder context containing configuration and environment information. Cannot be null.
+ /// The name of the connection string in the application's configuration to use for the SQL Server database. Cannot
+ /// be null or whitespace.
+ /// The same IServiceCollection instance so that additional calls can be chained.
+ [SuppressMessage(
+ "Design",
+ "SST2307",
+ Justification = "Explicit Identity types preserve the public overload signatures released in version 4.1.0.")]
+ public IServiceCollection UseEntityFrameworkCoreSqlServer<
+ [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] TContext,
+ TUser,
+ TRole>(
+ WebHostBuilderContext context,
+ string connectionStringName)
+ where TContext : DbContext
+ where TUser : class
+ where TRole : class =>
+ services.UseEntityFrameworkCoreSqlServer(
+ context,
+ connectionStringName,
+ ServiceLifetime.Scoped);
+
+ /// Configures SQL Server and Identity with an explicit service lifetime.
+ /// The Entity Framework Core DbContext type.
+ /// The Identity user type.
+ /// The Identity role type.
+ /// The web host builder context.
+ /// The configured connection string name.
+ /// The lifetime with which to register the DbContext service.
+ /// The same IServiceCollection instance so that additional calls can be chained.
+ /// Thrown if connectionStringName is null or consists only of white-space characters.
+ /// Thrown if the specified connection string is not found in the configuration.
+ [SuppressMessage(
+ "Design",
+ "SST2307",
+ Justification = "Explicit Identity types preserve the public overload signatures released in version 4.1.0.")]
+ public IServiceCollection UseEntityFrameworkCoreSqlServer<
+ [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] TContext,
+ TUser,
+ TRole>(
+ WebHostBuilderContext context,
+ string connectionStringName,
+ ServiceLifetime serviceLifetime)
+ where TContext : DbContext
+ where TUser : class
+ where TRole : class
+ {
+ _ = services ?? throw new ArgumentNullException(nameof(services));
+ _ = context ?? throw new ArgumentNullException(nameof(context));
+
+ ArgumentException.ThrowIfNullOrWhiteSpace(connectionStringName);
+
+ var conString = context.Configuration.GetConnectionString(connectionStringName);
+ _ = services
+ .AddDbContext(
+ options => options.UseSqlServer(conString ?? throw new InvalidOperationException($"Connection string '{connectionStringName}' not found.")),
+ serviceLifetime,
+ serviceLifetime == ServiceLifetime.Singleton ? ServiceLifetime.Singleton : ServiceLifetime.Scoped)
+ .AddDefaultIdentity()
+ .AddRoles()
+ .AddEntityFrameworkStores();
+ return services;
+ }
+
+ /// Configures SQL Server with the specified DbContext and Identity user type.
+ /// This method adds the DbContext, ASP.NET Core Identity, and Entity Framework stores to the
+ /// service collection, enabling authentication and data access using SQL Server. The connection string must be
+ /// defined in the application's configuration under the provided name.
+ /// The type of the DbContext to register for use with SQL Server.
+ /// The type of the user entity to use with ASP.NET Core Identity.
+ /// The web host builder context containing configuration information. Cannot be null.
+ /// The name of the connection string in the configuration to use for the SQL Server database. Cannot be null or
+ /// whitespace.
+ /// The IServiceCollection instance configured for SQL Server and Identity.
+ [SuppressMessage(
+ "Design",
+ "SST2307",
+ Justification = "Explicit Identity types preserve the public overload signatures released in version 4.1.0.")]
+ public IServiceCollection UseEntityFrameworkCoreSqlServer<
+ [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] TContext,
+ TUser>(
+ WebHostBuilderContext context,
+ string connectionStringName)
+ where TContext : DbContext
+ where TUser : class =>
+ services.UseEntityFrameworkCoreSqlServer(
+ context,
+ connectionStringName,
+ ServiceLifetime.Scoped);
+
+ /// Configures SQL Server and user-only Identity with an explicit service lifetime.
+ /// The Entity Framework Core DbContext type.
+ /// The Identity user type.
+ /// The web host builder context.
+ /// The configured connection string name.
+ /// The lifetime with which to register the DbContext service.
+ /// The IServiceCollection instance with Entity Framework Core and identity services configured for SQL Server.
+ /// Thrown if connectionStringName is null or consists only of white-space characters.
+ /// Thrown if the specified connection string is not found in the configuration.
+ [SuppressMessage(
+ "Design",
+ "SST2307",
+ Justification = "Explicit Identity types preserve the public overload signatures released in version 4.1.0.")]
+ public IServiceCollection UseEntityFrameworkCoreSqlServer<
+ [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] TContext,
+ TUser>(
+ WebHostBuilderContext context,
+ string connectionStringName,
+ ServiceLifetime serviceLifetime)
+ where TContext : DbContext
+ where TUser : class
+ {
+ _ = services ?? throw new ArgumentNullException(nameof(services));
+ _ = context ?? throw new ArgumentNullException(nameof(context));
+
+ ArgumentException.ThrowIfNullOrWhiteSpace(connectionStringName);
+
+ var conString = context.Configuration.GetConnectionString(connectionStringName);
+ _ = services
+ .AddDbContext(
+ options => options.UseSqlServer(conString ?? throw new InvalidOperationException($"Connection string '{connectionStringName}' not found.")),
+ serviceLifetime,
+ serviceLifetime == ServiceLifetime.Singleton ? ServiceLifetime.Singleton : ServiceLifetime.Scoped)
+ .AddDefaultIdentity()
+ .AddEntityFrameworkStores();
+ return services;
+ }
+
/// Configures SQL Server and ASP.NET Core Identity with caller-provided identity services.
/// The type of the Entity Framework Core DbContext to use for data access.
/// The web host builder context containing configuration and environment information. Cannot be null.
diff --git a/src/tests/Extensions.Hosting.DataLogging.Tests/SqlServerIdentityCompatibilityTests.cs b/src/tests/Extensions.Hosting.DataLogging.Tests/SqlServerIdentityCompatibilityTests.cs
new file mode 100644
index 00000000..9796f8bb
--- /dev/null
+++ b/src/tests/Extensions.Hosting.DataLogging.Tests/SqlServerIdentityCompatibilityTests.cs
@@ -0,0 +1,145 @@
+// Copyright (c) 2019-2026 ReactiveUI Association Incorporated. All rights reserved.
+// ReactiveUI Association Incorporated licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for full license information.
+
+extern alias SqlServer;
+using SqlServerExtensions = SqlServer::ReactiveMarbles.Extensions.Hosting.Identity.EntityFrameworkCore.HostBuilderEntityFrameworkCoreExtensions;
+
+namespace Extensions.Hosting.DataLogging.Tests;
+
+/// Verifies the SQL Server Identity overloads shipped in version 4.1.0.
+public sealed class SqlServerIdentityCompatibilityTests
+{
+ /// The configured connection string name.
+ private const string ConnectionStringName = "Identity";
+
+ /// The provider connection string used for registration.
+ private const string ConnectionString = "Server=(localdb)\\MSSQLLocalDB;Database=IdentityCompatibility;Trusted_Connection=True;";
+
+ /// Verifies application-builder overloads register custom Identity types and context lifetimes.
+ /// Whether to configure custom roles.
+ /// Whether to use the overload with the default lifetime.
+ /// The expected context lifetime.
+ /// A task representing the asynchronous test.
+ [Test]
+ [Arguments(false, true, ServiceLifetime.Scoped)]
+ [Arguments(true, true, ServiceLifetime.Scoped)]
+ [Arguments(false, false, ServiceLifetime.Scoped)]
+ [Arguments(true, false, ServiceLifetime.Scoped)]
+ [Arguments(false, false, ServiceLifetime.Singleton)]
+ [Arguments(true, false, ServiceLifetime.Singleton)]
+ [Arguments(false, false, ServiceLifetime.Transient)]
+ [Arguments(true, false, ServiceLifetime.Transient)]
+ public async Task AddSqlServerWithIdentity_CustomTypes_RegisterManagersAndLifetime(bool roles, bool defaultLifetime, ServiceLifetime lifetime)
+ {
+ var builder = Host.CreateApplicationBuilder();
+ _ = builder.Configuration.AddInMemoryCollection(
+ [
+ new KeyValuePair("ConnectionStrings:Identity", ConnectionString),
+ ]);
+
+ var result = (roles, defaultLifetime) switch
+ {
+ (true, true) => SqlServerExtensions.AddSqlServerWithIdentity(builder, ConnectionStringName),
+ (true, false) => SqlServerExtensions.AddSqlServerWithIdentity(builder, ConnectionStringName, lifetime),
+ (false, true) => SqlServerExtensions.AddSqlServerWithIdentity(builder, ConnectionStringName),
+ (false, false) => SqlServerExtensions.AddSqlServerWithIdentity(builder, ConnectionStringName, lifetime),
+ };
+
+ await Assert.That(ReferenceEquals(result, builder)).IsTrue();
+ await VerifyRegistrationAsync(builder.Services, roles, lifetime);
+ }
+
+ /// Verifies service-collection overloads register custom Identity types and context lifetimes.
+ /// Whether to configure custom roles.
+ /// Whether to use the overload with the default lifetime.
+ /// The expected context lifetime.
+ /// A task representing the asynchronous test.
+ [Test]
+ [Arguments(false, true, ServiceLifetime.Scoped)]
+ [Arguments(true, true, ServiceLifetime.Scoped)]
+ [Arguments(false, false, ServiceLifetime.Scoped)]
+ [Arguments(true, false, ServiceLifetime.Scoped)]
+ [Arguments(false, false, ServiceLifetime.Singleton)]
+ [Arguments(true, false, ServiceLifetime.Singleton)]
+ [Arguments(false, false, ServiceLifetime.Transient)]
+ [Arguments(true, false, ServiceLifetime.Transient)]
+ public async Task UseEntityFrameworkCoreSqlServer_CustomTypes_RegisterManagersAndLifetime(bool roles, bool defaultLifetime, ServiceLifetime lifetime)
+ {
+ var configuration = new ConfigurationBuilder().AddInMemoryCollection(
+ [
+ new KeyValuePair("ConnectionStrings:Identity", ConnectionString),
+ ]).Build();
+ var context = new WebHostBuilderContext { Configuration = configuration };
+ var services = new ServiceCollection();
+ _ = services.AddLogging();
+ var result = (roles, defaultLifetime) switch
+ {
+ (true, true) => SqlServerExtensions.UseEntityFrameworkCoreSqlServer(services, context, ConnectionStringName),
+ (true, false) => SqlServerExtensions.UseEntityFrameworkCoreSqlServer(services, context, ConnectionStringName, lifetime),
+ (false, true) => SqlServerExtensions.UseEntityFrameworkCoreSqlServer(services, context, ConnectionStringName),
+ (false, false) => SqlServerExtensions.UseEntityFrameworkCoreSqlServer(services, context, ConnectionStringName, lifetime),
+ };
+
+ await Assert.That(ReferenceEquals(result, services)).IsTrue();
+ await VerifyRegistrationAsync(services, roles, lifetime);
+ }
+
+ /// Verifies actual context, store, and manager resolution for the selected Identity types.
+ /// The configured services.
+ /// Whether custom role services are expected.
+ /// The expected context lifetime.
+ /// A task representing the asynchronous test.
+ private static async Task VerifyRegistrationAsync(IServiceCollection services, bool roles, ServiceLifetime lifetime)
+ {
+ var contextType = roles ? typeof(RoleContext) : typeof(UserContext);
+ ServiceDescriptor? contextDescriptor = null;
+ foreach (var descriptor in services)
+ {
+ if (descriptor.ServiceType == contextType)
+ {
+ contextDescriptor = descriptor;
+ break;
+ }
+ }
+
+ await Assert.That(contextDescriptor).IsNotNull();
+ await Assert.That(contextDescriptor!.Lifetime).IsEqualTo(lifetime);
+ await using var provider = services.BuildServiceProvider(new ServiceProviderOptions { ValidateScopes = true });
+ using var firstScope = provider.CreateScope();
+ using var secondScope = provider.CreateScope();
+ var first = (DbContext)firstScope.ServiceProvider.GetRequiredService(contextType);
+ var repeated = (DbContext)firstScope.ServiceProvider.GetRequiredService(contextType);
+ var second = (DbContext)secondScope.ServiceProvider.GetRequiredService(contextType);
+ await Assert.That(first.Database.ProviderName).IsEqualTo("Microsoft.EntityFrameworkCore.SqlServer");
+ await Assert.That(first.Database.GetDbConnection().DataSource).IsEqualTo(@"(localdb)\MSSQLLocalDB");
+ await Assert.That(first.Database.GetDbConnection().Database).IsEqualTo("IdentityCompatibility");
+ await Assert.That(ReferenceEquals(first, repeated)).IsEqualTo(lifetime != ServiceLifetime.Transient);
+ await Assert.That(ReferenceEquals(first, second)).IsEqualTo(lifetime == ServiceLifetime.Singleton);
+ await Assert.That(firstScope.ServiceProvider.GetRequiredService>()).IsNotNull();
+ await Assert.That(firstScope.ServiceProvider.GetRequiredService>()).IsNotNull();
+ if (roles)
+ {
+ await Assert.That(firstScope.ServiceProvider.GetRequiredService>()).IsNotNull();
+ await Assert.That(firstScope.ServiceProvider.GetRequiredService>()).IsNotNull();
+ }
+ else
+ {
+ await Assert.That(firstScope.ServiceProvider.GetService>()).IsNull();
+ }
+ }
+
+ /// Provides a custom application user.
+ public sealed class CustomUser : IdentityUser;
+
+ /// Provides a custom application role.
+ public sealed class CustomRole : IdentityRole;
+
+ /// Provides a context storing custom users without roles.
+ /// The configured context options.
+ public sealed class UserContext(DbContextOptions options) : IdentityUserContext(options);
+
+ /// Provides a context storing custom users and roles.
+ /// The configured context options.
+ public sealed class RoleContext(DbContextOptions options) : IdentityDbContext(options);
+}
diff --git a/src/tests/Extensions.Hosting.DataLogging.Tests/SqliteIdentityCompatibilityTests.cs b/src/tests/Extensions.Hosting.DataLogging.Tests/SqliteIdentityCompatibilityTests.cs
new file mode 100644
index 00000000..2acaa35f
--- /dev/null
+++ b/src/tests/Extensions.Hosting.DataLogging.Tests/SqliteIdentityCompatibilityTests.cs
@@ -0,0 +1,144 @@
+// Copyright (c) 2019-2026 ReactiveUI Association Incorporated. All rights reserved.
+// ReactiveUI Association Incorporated licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for full license information.
+
+extern alias Sqlite;
+using SqliteExtensions = Sqlite::ReactiveMarbles.Extensions.Hosting.Identity.EntityFrameworkCore.HostBuilderEntityFrameworkCoreExtensions;
+
+namespace Extensions.Hosting.DataLogging.Tests;
+
+/// Verifies the SQLite Identity overloads shipped in version 4.1.0.
+public sealed class SqliteIdentityCompatibilityTests
+{
+ /// The configured connection string name.
+ private const string ConnectionStringName = "Identity";
+
+ /// The provider connection string used for registration.
+ private const string ConnectionString = "Data Source=:memory:";
+
+ /// Verifies application-builder overloads register custom Identity types and context lifetimes.
+ /// Whether to configure custom roles.
+ /// Whether to use the overload with the default lifetime.
+ /// The expected context lifetime.
+ /// A task representing the asynchronous test.
+ [Test]
+ [Arguments(false, true, ServiceLifetime.Scoped)]
+ [Arguments(true, true, ServiceLifetime.Scoped)]
+ [Arguments(false, false, ServiceLifetime.Scoped)]
+ [Arguments(true, false, ServiceLifetime.Scoped)]
+ [Arguments(false, false, ServiceLifetime.Singleton)]
+ [Arguments(true, false, ServiceLifetime.Singleton)]
+ [Arguments(false, false, ServiceLifetime.Transient)]
+ [Arguments(true, false, ServiceLifetime.Transient)]
+ public async Task AddSqliteWithIdentity_CustomTypes_RegisterManagersAndLifetime(bool roles, bool defaultLifetime, ServiceLifetime lifetime)
+ {
+ var builder = Host.CreateApplicationBuilder();
+ _ = builder.Configuration.AddInMemoryCollection(
+ [
+ new KeyValuePair("ConnectionStrings:Identity", ConnectionString),
+ ]);
+
+ var result = (roles, defaultLifetime) switch
+ {
+ (true, true) => SqliteExtensions.AddSqliteWithIdentity(builder, ConnectionStringName),
+ (true, false) => SqliteExtensions.AddSqliteWithIdentity(builder, ConnectionStringName, lifetime),
+ (false, true) => SqliteExtensions.AddSqliteWithIdentity(builder, ConnectionStringName),
+ (false, false) => SqliteExtensions.AddSqliteWithIdentity(builder, ConnectionStringName, lifetime),
+ };
+
+ await Assert.That(ReferenceEquals(result, builder)).IsTrue();
+ await VerifyRegistrationAsync(builder.Services, roles, lifetime);
+ }
+
+ /// Verifies service-collection overloads register custom Identity types and context lifetimes.
+ /// Whether to configure custom roles.
+ /// Whether to use the overload with the default lifetime.
+ /// The expected context lifetime.
+ /// A task representing the asynchronous test.
+ [Test]
+ [Arguments(false, true, ServiceLifetime.Scoped)]
+ [Arguments(true, true, ServiceLifetime.Scoped)]
+ [Arguments(false, false, ServiceLifetime.Scoped)]
+ [Arguments(true, false, ServiceLifetime.Scoped)]
+ [Arguments(false, false, ServiceLifetime.Singleton)]
+ [Arguments(true, false, ServiceLifetime.Singleton)]
+ [Arguments(false, false, ServiceLifetime.Transient)]
+ [Arguments(true, false, ServiceLifetime.Transient)]
+ public async Task UseEntityFrameworkCoreSqlite_CustomTypes_RegisterManagersAndLifetime(bool roles, bool defaultLifetime, ServiceLifetime lifetime)
+ {
+ var configuration = new ConfigurationBuilder().AddInMemoryCollection(
+ [
+ new KeyValuePair("ConnectionStrings:Identity", ConnectionString),
+ ]).Build();
+ var context = new WebHostBuilderContext { Configuration = configuration };
+ var services = new ServiceCollection();
+ _ = services.AddLogging();
+ var result = (roles, defaultLifetime) switch
+ {
+ (true, true) => SqliteExtensions.UseEntityFrameworkCoreSqlite(services, context, ConnectionStringName),
+ (true, false) => SqliteExtensions.UseEntityFrameworkCoreSqlite(services, context, ConnectionStringName, lifetime),
+ (false, true) => SqliteExtensions.UseEntityFrameworkCoreSqlite(services, context, ConnectionStringName),
+ (false, false) => SqliteExtensions.UseEntityFrameworkCoreSqlite(services, context, ConnectionStringName, lifetime),
+ };
+
+ await Assert.That(ReferenceEquals(result, services)).IsTrue();
+ await VerifyRegistrationAsync(services, roles, lifetime);
+ }
+
+ /// Verifies actual context, store, and manager resolution for the selected Identity types.
+ /// The configured services.
+ /// Whether custom role services are expected.
+ /// The expected context lifetime.
+ /// A task representing the asynchronous test.
+ private static async Task VerifyRegistrationAsync(IServiceCollection services, bool roles, ServiceLifetime lifetime)
+ {
+ var contextType = roles ? typeof(RoleContext) : typeof(UserContext);
+ ServiceDescriptor? contextDescriptor = null;
+ foreach (var descriptor in services)
+ {
+ if (descriptor.ServiceType == contextType)
+ {
+ contextDescriptor = descriptor;
+ break;
+ }
+ }
+
+ await Assert.That(contextDescriptor).IsNotNull();
+ await Assert.That(contextDescriptor!.Lifetime).IsEqualTo(lifetime);
+ await using var provider = services.BuildServiceProvider(new ServiceProviderOptions { ValidateScopes = true });
+ using var firstScope = provider.CreateScope();
+ using var secondScope = provider.CreateScope();
+ var first = (DbContext)firstScope.ServiceProvider.GetRequiredService(contextType);
+ var repeated = (DbContext)firstScope.ServiceProvider.GetRequiredService(contextType);
+ var second = (DbContext)secondScope.ServiceProvider.GetRequiredService(contextType);
+ await Assert.That(first.Database.ProviderName).IsEqualTo("Microsoft.EntityFrameworkCore.Sqlite");
+ await Assert.That(first.Database.GetConnectionString()).IsEqualTo(ConnectionString);
+ await Assert.That(ReferenceEquals(first, repeated)).IsEqualTo(lifetime != ServiceLifetime.Transient);
+ await Assert.That(ReferenceEquals(first, second)).IsEqualTo(lifetime == ServiceLifetime.Singleton);
+ await Assert.That(firstScope.ServiceProvider.GetRequiredService>()).IsNotNull();
+ await Assert.That(firstScope.ServiceProvider.GetRequiredService>()).IsNotNull();
+ if (roles)
+ {
+ await Assert.That(firstScope.ServiceProvider.GetRequiredService>()).IsNotNull();
+ await Assert.That(firstScope.ServiceProvider.GetRequiredService>()).IsNotNull();
+ }
+ else
+ {
+ await Assert.That(firstScope.ServiceProvider.GetService>()).IsNull();
+ }
+ }
+
+ /// Provides a custom application user.
+ public sealed class CustomUser : IdentityUser;
+
+ /// Provides a custom application role.
+ public sealed class CustomRole : IdentityRole;
+
+ /// Provides a context storing custom users without roles.
+ /// The configured context options.
+ public sealed class UserContext(DbContextOptions options) : IdentityUserContext(options);
+
+ /// Provides a context storing custom users and roles.
+ /// The configured context options.
+ public sealed class RoleContext(DbContextOptions options) : IdentityDbContext(options);
+}