diff --git a/samples/Daarto.WebUI/Daarto.WebUI.csproj b/samples/Daarto.WebUI/Daarto.WebUI.csproj
index ea183f9..e3f5d17 100644
--- a/samples/Daarto.WebUI/Daarto.WebUI.csproj
+++ b/samples/Daarto.WebUI/Daarto.WebUI.csproj
@@ -4,7 +4,7 @@
aspnet-Daarto.WebUI-0BC4D06A-1285-4595-85EA-07A68DD02CF3
-
+
diff --git a/samples/Daarto.WebUI/Data/Tables/ExtendedRolesTable.cs b/samples/Daarto.WebUI/Data/Tables/ExtendedRolesTable.cs
index f9d1f1d..7530527 100644
--- a/samples/Daarto.WebUI/Data/Tables/ExtendedRolesTable.cs
+++ b/samples/Daarto.WebUI/Data/Tables/ExtendedRolesTable.cs
@@ -9,9 +9,9 @@ namespace Daarto.WebUI.Data.Tables
{
public class ExtendedRolesTable : RolesTable>
{
- public ExtendedRolesTable(IDbConnectionFactory dbConnectionFactory) : base(dbConnectionFactory) { }
+ public ExtendedRolesTable(IDbConnectionStore dbConnectionFactory) : base(dbConnectionFactory) { }
- public override async Task CreateAsync(ExtendedIdentityRole role) {
+ public override async Task CreateAsync(ExtendedIdentityRole role) {
const string sql = "INSERT INTO [dbo].[AspNetRoles] " +
"VALUES (@Id, @Name, @NormalizedName, @ConcurrencyStamp, @Description);";
var rowsInserted = await DbConnection.ExecuteAsync(sql, new {
@@ -21,10 +21,15 @@ public override async Task CreateAsync(ExtendedIdentityRole role) {
role.ConcurrencyStamp,
role.Description
});
- return rowsInserted == 1;
+ return rowsInserted == 1
+ ? IdentityResult.Success
+ : IdentityResult.Failed(new IdentityError {
+ Code = string.Empty,
+ Description = $"Role '{role.Name}' could not be created."
+ });
}
- public override async Task UpdateAsync(ExtendedIdentityRole role, IList> claims = null) {
+ public override async Task UpdateAsync(ExtendedIdentityRole role, IList> claims = null) {
const string updateRoleSql = "UPDATE [dbo].[AspNetRoles] " +
"SET [Name] = @Name, [NormalizedName] = @NormalizedName, [ConcurrencyStamp] = @ConcurrencyStamp, [Description] = @Description " +
"WHERE [Id] = @Id;";
@@ -55,10 +60,13 @@ public override async Task UpdateAsync(ExtendedIdentityRole role, IList
/// Creates a new role in the store.
///
/// The role to create in the store.
- Task CreateAsync(TRole role);
+ Task CreateAsync(TRole role);
///
/// Deletes a role from the store.
///
/// The id of the role to delete from the store.
- Task DeleteAsync(TKey roleId);
+ Task DeleteAsync(TKey roleId);
///
/// Finds the role who has the specified id.
///
@@ -41,6 +41,6 @@ public interface IRolesTable
///
/// The role to update in the store.
/// The claims of the role.
- Task UpdateAsync(TRole role, IList claims = null);
+ Task UpdateAsync(TRole role, IList claims = null);
}
}
diff --git a/src/AspNetCore.Identity.Dapper/Abstract/IUsersTable.cs b/src/AspNetCore.Identity.Dapper/Abstract/IUsersTable.cs
index 699cee2..10232d9 100644
--- a/src/AspNetCore.Identity.Dapper/Abstract/IUsersTable.cs
+++ b/src/AspNetCore.Identity.Dapper/Abstract/IUsersTable.cs
@@ -25,12 +25,12 @@ public interface IUsersOnlyTable
/// The user to create in the store.
- Task CreateAsync(TUser user);
+ Task CreateAsync(TUser user);
///
/// Deletes a user from the store.
///
/// The id of the user to delete from the store.
- Task DeleteAsync(TKey userId);
+ Task DeleteAsync(TKey userId);
///
/// Finds the user who has the specified id.
///
@@ -53,7 +53,7 @@ public interface IUsersOnlyTableThe claims of the user.
/// The logins of the user.
/// The tokens of the user.
- Task UpdateAsync(TUser user, IList claims, IList logins, IList tokens);
+ Task UpdateAsync(TUser user, IList claims, IList logins, IList tokens);
///
/// Gets the users that own the specified claim.
///
@@ -86,7 +86,7 @@ public interface IUsersTableThe roles of the user.
/// The logins of the user.
/// The tokens of the user.
- Task UpdateAsync(TUser user, IList claims, IList roles, IList logins, IList tokens);
+ Task UpdateAsync(TUser user, IList claims, IList roles, IList logins, IList tokens);
///
/// Gets the users that belong to the specified role.
///
diff --git a/src/AspNetCore.Identity.Dapper/AspNetCore.Identity.Dapper.csproj b/src/AspNetCore.Identity.Dapper/AspNetCore.Identity.Dapper.csproj
index 53d2060..79277b1 100644
--- a/src/AspNetCore.Identity.Dapper/AspNetCore.Identity.Dapper.csproj
+++ b/src/AspNetCore.Identity.Dapper/AspNetCore.Identity.Dapper.csproj
@@ -24,9 +24,9 @@
ea4ca2df-aa2d-4174-8f13-b1e594019d86
-
+
-
-
+
+
diff --git a/src/AspNetCore.Identity.Dapper/DapperStoreOptions.cs b/src/AspNetCore.Identity.Dapper/DapperStoreOptions.cs
index 1395276..8b24986 100644
--- a/src/AspNetCore.Identity.Dapper/DapperStoreOptions.cs
+++ b/src/AspNetCore.Identity.Dapper/DapperStoreOptions.cs
@@ -1,4 +1,5 @@
-using System.Data;
+using System;
+using System.Data;
using Microsoft.Extensions.DependencyInjection;
namespace AspNetCore.Identity.Dapper
@@ -14,8 +15,8 @@ public class DapperStoreOptions
///
public string ConnectionString { get; set; }
///
- /// A factory for creating instances of .
+ /// The type of the DbConnectionFactory (A factory for creating instances of )
///
- public IDbConnectionFactory DbConnectionFactory { get; set; }
+ public Type DbConnectionStoreType { get; set; }
}
}
diff --git a/src/AspNetCore.Identity.Dapper/IDbConnectionFactory.cs b/src/AspNetCore.Identity.Dapper/IDbConnectionFactory.cs
index bba84f8..6523f80 100644
--- a/src/AspNetCore.Identity.Dapper/IDbConnectionFactory.cs
+++ b/src/AspNetCore.Identity.Dapper/IDbConnectionFactory.cs
@@ -1,11 +1,12 @@
-using System.Data;
+using System;
+using System.Data;
namespace AspNetCore.Identity.Dapper
{
///
/// A factory for creating instances of .
///
- public interface IDbConnectionFactory
+ public interface IDbConnectionStore : IDisposable
{
///
/// The connection string to use for connecting to Microsoft SQL Server.
@@ -14,6 +15,6 @@ public interface IDbConnectionFactory
///
/// Creates a new instance of the underlying .
///
- IDbConnection Create();
+ IDbConnection GetOrCreateConnection();
}
}
diff --git a/src/AspNetCore.Identity.Dapper/IdentityBuilderExtensions.cs b/src/AspNetCore.Identity.Dapper/IdentityBuilderExtensions.cs
index 1ddb799..e50e23b 100644
--- a/src/AspNetCore.Identity.Dapper/IdentityBuilderExtensions.cs
+++ b/src/AspNetCore.Identity.Dapper/IdentityBuilderExtensions.cs
@@ -1,4 +1,5 @@
using System;
+using System.Linq;
using System.Reflection;
using AspNetCore.Identity.Dapper;
using Microsoft.AspNetCore.Identity;
@@ -32,14 +33,14 @@ private static void AddStores(IServiceCollection services, Type userType, Type r
var configuration = serviceProvider.GetRequiredService();
var dbConnectionContextOptions = new DapperStoreOptions {
ConnectionString = configuration.GetConnectionString("DefaultConnection"),
- DbConnectionFactory = new SqlServerDbConnectionFactory(),
+ DbConnectionStoreType = typeof(SqlServerDbConnectionStore),
Services = services
};
configureAction?.Invoke(dbConnectionContextOptions);
dbConnectionContextOptions.Services = null;
var keyType = identityUserType.GenericTypeArguments[0];
- services.TryAddScoped(typeof(IDbConnectionFactory), x => {
- var dbConnectionFactoryInstance = (IDbConnectionFactory)Activator.CreateInstance(dbConnectionContextOptions.DbConnectionFactory.GetType());
+ services.TryAddScoped(typeof(IDbConnectionStore), x => {
+ var dbConnectionFactoryInstance = (IDbConnectionStore)Activator.CreateInstance(dbConnectionContextOptions.DbConnectionStoreType);
dbConnectionFactoryInstance.ConnectionString = dbConnectionContextOptions.ConnectionString;
return dbConnectionFactoryInstance;
});
@@ -49,13 +50,14 @@ private static void AddStores(IServiceCollection services, Type userType, Type r
var userLoginType = typeof(IdentityUserLogin<>).MakeGenericType(keyType);
var roleClaimType = typeof(IdentityRoleClaim<>).MakeGenericType(keyType);
var userTokenType = typeof(IdentityUserToken<>).MakeGenericType(keyType);
+ var usersTableServiceType = typeof(IUsersTable<,,,,,>).MakeGenericType(userType, keyType, userClaimType, userRoleType, userLoginType, userTokenType);
if (roleType != null) {
var identityRoleType = FindGenericBaseType(roleType, typeof(IdentityRole<>));
if (identityRoleType == null) {
throw new InvalidOperationException($"Method {nameof(AddDapperStores)} can only be called with a role that derives from IdentityRole.");
}
services.TryAddScoped(
- typeof(IUsersTable<,,,,,>).MakeGenericType(userType, keyType, userClaimType, userRoleType, userLoginType, userTokenType),
+ usersTableServiceType,
typeof(UsersTable<,,,,,>).MakeGenericType(userType, keyType, userClaimType, userRoleType, userLoginType, userTokenType)
);
services.TryAddScoped(typeof(IRolesTable<,,>).MakeGenericType(roleType, keyType, roleClaimType), typeof(RolesTable<,,>).MakeGenericType(roleType, keyType, roleClaimType));
@@ -64,10 +66,14 @@ private static void AddStores(IServiceCollection services, Type userType, Type r
services.TryAddScoped(typeof(IRoleStore<>).MakeGenericType(roleType), typeof(RoleStore<,,,>).MakeGenericType(roleType, keyType, userRoleType, roleClaimType));
userStoreType = typeof(UserStore<,,,,,,,>).MakeGenericType(userType, roleType, keyType, userClaimType, userRoleType, userLoginType, userTokenType, roleClaimType);
} else {
- services.TryAddScoped(
- typeof(IUsersOnlyTable<,,,,>).MakeGenericType(userType, keyType, userClaimType, userLoginType, userTokenType),
- typeof(UsersTable<,,,,,>).MakeGenericType(userType, keyType, userClaimType, roleType, userLoginType, userTokenType)
- );
+ var matchingServiceType = services.FirstOrDefault(s => s.ServiceType == usersTableServiceType);
+ usersTableServiceType = typeof(IUsersOnlyTable<,,,,>).MakeGenericType(userType, keyType, userClaimType, userLoginType, userTokenType);
+ if (matchingServiceType == null) {
+ services.TryAddScoped(usersTableServiceType, typeof(UsersTable<,,,,,>).MakeGenericType(userType, keyType, userClaimType, userRoleType, userLoginType, userTokenType));
+ } else {
+ services.Remove(matchingServiceType);
+ services.TryAddScoped(usersTableServiceType, matchingServiceType.ImplementationType);
+ }
userStoreType = typeof(UserOnlyStore<,,,,>).MakeGenericType(userType, keyType, userClaimType, userLoginType, userTokenType);
}
services.TryAddScoped(typeof(IUserClaimsTable<,>).MakeGenericType(keyType, userClaimType), typeof(UserClaimsTable<,>).MakeGenericType(keyType, userClaimType));
diff --git a/src/AspNetCore.Identity.Dapper/SqlServerDbConnectionFactory.cs b/src/AspNetCore.Identity.Dapper/SqlServerDbConnectionFactory.cs
index 96ae414..7a3db6c 100644
--- a/src/AspNetCore.Identity.Dapper/SqlServerDbConnectionFactory.cs
+++ b/src/AspNetCore.Identity.Dapper/SqlServerDbConnectionFactory.cs
@@ -6,18 +6,36 @@ namespace AspNetCore.Identity.Dapper
///
/// Creates a new instance for connecting to Microsoft SQL Server.
///
- public class SqlServerDbConnectionFactory : IDbConnectionFactory
+ public class SqlServerDbConnectionStore : IDbConnectionStore
{
///
/// The connection string to use for connecting to Microsoft SQL Server.
///
public string ConnectionString { get; set; }
+ private IDbConnection _sqlConnection;
///
- public IDbConnection Create() {
- var sqlConnection = new SqlConnection(ConnectionString);
- sqlConnection.Open();
- return sqlConnection;
+ public IDbConnection GetOrCreateConnection() {
+ // we could open the connection here and it will remain open until explicitly closed, but if we return a closed connection (as below),
+ // dapper will open and close with each query or transaction
+ return _sqlConnection ??= new SqlConnection(ConnectionString);
+ }
+
+ private bool _disposed = false;
+ ///
+ public void Dispose() {
+ Dispose(true);
+ }
+ ///
+ /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
+ ///
+ /// Indicates whether the method call comes from a Dispose method (its value is true) or from a finalizer (its value is false).
+ protected virtual void Dispose(bool disposing) {
+ if (!_disposed && disposing) {
+ // Free any other managed objects here.
+ _sqlConnection?.Dispose();
+ _disposed = true;
+ }
}
}
}
diff --git a/src/AspNetCore.Identity.Dapper/Stores/RoleStore.cs b/src/AspNetCore.Identity.Dapper/Stores/RoleStore.cs
index 56d42f6..feeeb8f 100644
--- a/src/AspNetCore.Identity.Dapper/Stores/RoleStore.cs
+++ b/src/AspNetCore.Identity.Dapper/Stores/RoleStore.cs
@@ -99,27 +99,19 @@ public override async Task AddClaimAsync(TRole role, Claim claim, CancellationTo
}
///
- public override async Task CreateAsync(TRole role, CancellationToken cancellationToken) {
+ public override Task CreateAsync(TRole role, CancellationToken cancellationToken) {
cancellationToken.ThrowIfCancellationRequested();
ThrowIfDisposed();
role.ThrowIfNull(nameof(role));
- var created = await RolesTable.CreateAsync(role);
- return created ? IdentityResult.Success : IdentityResult.Failed(new IdentityError {
- Code = string.Empty,
- Description = $"Role '{role.Name}' could not be created."
- });
+ return RolesTable.CreateAsync(role);
}
///
- public override async Task DeleteAsync(TRole role, CancellationToken cancellationToken) {
+ public override Task DeleteAsync(TRole role, CancellationToken cancellationToken) {
cancellationToken.ThrowIfCancellationRequested();
ThrowIfDisposed();
role.ThrowIfNull(nameof(role));
- var deleted = await RolesTable.DeleteAsync(role.Id);
- return deleted ? IdentityResult.Success : IdentityResult.Failed(new IdentityError {
- Code = string.Empty,
- Description = $"Role '{role.Name}' could not be deleted."
- });
+ return RolesTable.DeleteAsync(role.Id);
}
///
@@ -204,16 +196,12 @@ public override Task SetRoleNameAsync(TRole role, string roleName, CancellationT
}
///
- public override async Task UpdateAsync(TRole role, CancellationToken cancellationToken) {
+ public override Task UpdateAsync(TRole role, CancellationToken cancellationToken) {
cancellationToken.ThrowIfCancellationRequested();
ThrowIfDisposed();
role.ThrowIfNull(nameof(role));
role.ConcurrencyStamp = Guid.NewGuid().ToString();
- var updated = await RolesTable.UpdateAsync(role, RoleClaims);
- return updated ? IdentityResult.Success : IdentityResult.Failed(new IdentityError {
- Code = string.Empty,
- Description = $"Role '{role.Name}' could not be updated."
- });
+ return RolesTable.UpdateAsync(role, RoleClaims);
}
}
}
diff --git a/src/AspNetCore.Identity.Dapper/Stores/UserOnlyStore.cs b/src/AspNetCore.Identity.Dapper/Stores/UserOnlyStore.cs
index eed3c2e..b25dd2d 100644
--- a/src/AspNetCore.Identity.Dapper/Stores/UserOnlyStore.cs
+++ b/src/AspNetCore.Identity.Dapper/Stores/UserOnlyStore.cs
@@ -95,27 +95,19 @@ public override async Task AddLoginAsync(TUser user, UserLoginInfo login, Cancel
}
///
- public override async Task CreateAsync(TUser user, CancellationToken cancellationToken) {
+ public override Task CreateAsync(TUser user, CancellationToken cancellationToken) {
cancellationToken.ThrowIfCancellationRequested();
ThrowIfDisposed();
user.ThrowIfNull(nameof(user));
- var created = await UsersTable.CreateAsync(user);
- return created ? IdentityResult.Success : IdentityResult.Failed(new IdentityError {
- Code = string.Empty,
- Description = $"User '{user.UserName}' could not be created."
- });
+ return UsersTable.CreateAsync(user);
}
///
- public override async Task DeleteAsync(TUser user, CancellationToken cancellationToken) {
+ public override Task DeleteAsync(TUser user, CancellationToken cancellationToken) {
cancellationToken.ThrowIfCancellationRequested();
ThrowIfDisposed();
user.ThrowIfNull(nameof(user));
- var deleted = await UsersTable.DeleteAsync(user.Id);
- return deleted ? IdentityResult.Success : IdentityResult.Failed(new IdentityError {
- Code = string.Empty,
- Description = $"User '{user.UserName}' could not be deleted."
- });
+ return UsersTable.DeleteAsync(user.Id);
}
///
@@ -212,16 +204,12 @@ public override async Task ReplaceClaimAsync(TUser user, Claim claim, Claim newC
}
///
- public override async Task UpdateAsync(TUser user, CancellationToken cancellationToken) {
+ public override Task UpdateAsync(TUser user, CancellationToken cancellationToken) {
cancellationToken.ThrowIfCancellationRequested();
ThrowIfDisposed();
user.ThrowIfNull(nameof(user));
user.ConcurrencyStamp = Guid.NewGuid().ToString();
- var updated = await UsersTable.UpdateAsync(user, UserClaims, UserLogins, UserTokens);
- return updated ? IdentityResult.Success : IdentityResult.Failed(new IdentityError {
- Code = string.Empty,
- Description = $"User '{user.UserName}' could not be deleted."
- });
+ return UsersTable.UpdateAsync(user, UserClaims, UserLogins, UserTokens);
}
///
diff --git a/src/AspNetCore.Identity.Dapper/Stores/UserStore.cs b/src/AspNetCore.Identity.Dapper/Stores/UserStore.cs
index 7429260..86188ed 100644
--- a/src/AspNetCore.Identity.Dapper/Stores/UserStore.cs
+++ b/src/AspNetCore.Identity.Dapper/Stores/UserStore.cs
@@ -137,27 +137,19 @@ public override async Task AddToRoleAsync(TUser user, string normalizedRoleName,
}
///
- public override async Task CreateAsync(TUser user, CancellationToken cancellationToken = default) {
+ public override Task CreateAsync(TUser user, CancellationToken cancellationToken = default) {
cancellationToken.ThrowIfCancellationRequested();
ThrowIfDisposed();
user.ThrowIfNull(nameof(user));
- var created = await UsersTable.CreateAsync(user);
- return created ? IdentityResult.Success : IdentityResult.Failed(new IdentityError {
- Code = string.Empty,
- Description = $"User '{user.UserName}' could not be created."
- });
+ return UsersTable.CreateAsync(user);
}
///
- public override async Task DeleteAsync(TUser user, CancellationToken cancellationToken) {
+ public override Task DeleteAsync(TUser user, CancellationToken cancellationToken) {
cancellationToken.ThrowIfCancellationRequested();
ThrowIfDisposed();
user.ThrowIfNull(nameof(user));
- var deleted = await UsersTable.DeleteAsync(user.Id);
- return deleted ? IdentityResult.Success : IdentityResult.Failed(new IdentityError {
- Code = string.Empty,
- Description = $"User '{user.UserName}' could not be deleted."
- });
+ return UsersTable.DeleteAsync(user.Id);
}
///
@@ -333,16 +325,12 @@ public override async Task ReplaceClaimAsync(TUser user, Claim claim, Claim newC
}
///
- public override async Task UpdateAsync(TUser user, CancellationToken cancellationToken) {
+ public override Task UpdateAsync(TUser user, CancellationToken cancellationToken) {
cancellationToken.ThrowIfCancellationRequested();
ThrowIfDisposed();
user.ThrowIfNull(nameof(user));
user.ConcurrencyStamp = Guid.NewGuid().ToString();
- var updated = await UsersTable.UpdateAsync(user, UserClaims, UserRoles, UserLogins, UserTokens);
- return updated ? IdentityResult.Success : IdentityResult.Failed(new IdentityError {
- Code = string.Empty,
- Description = $"User '{user.UserName}' could not be deleted."
- });
+ return UsersTable.UpdateAsync(user, UserClaims, UserRoles, UserLogins, UserTokens);
}
///
diff --git a/src/AspNetCore.Identity.Dapper/Tables/IdentityTable.cs b/src/AspNetCore.Identity.Dapper/Tables/IdentityTable.cs
index c56f271..b5b57cc 100644
--- a/src/AspNetCore.Identity.Dapper/Tables/IdentityTable.cs
+++ b/src/AspNetCore.Identity.Dapper/Tables/IdentityTable.cs
@@ -1,30 +1,30 @@
-using System.Data;
+using System;
+using System.Data;
namespace AspNetCore.Identity.Dapper
{
///
/// A base class for all identity tables.
///
- public class IdentityTable : TableBase
+ public abstract class IdentityTable
{
///
/// Creates a new instance of .
///
- ///
- public IdentityTable(IDbConnectionFactory dbConnectionFactory) {
- DbConnection = dbConnectionFactory.Create();
+ ///
+ protected IdentityTable(IDbConnectionStore dbConnectionStore) {
+ _dbConnectionStore = dbConnectionStore;
}
+ private readonly IDbConnectionStore _dbConnectionStore;
+ private IDbConnection _dbConnection;
+
///
/// The type of the database connection class used to access the store.
///
- protected IDbConnection DbConnection { get; }
-
- ///
- protected override void OnDispose() {
- if (DbConnection != null) {
- DbConnection.Dispose();
- }
+ protected IDbConnection DbConnection
+ {
+ get => _dbConnection ??= _dbConnectionStore.GetOrCreateConnection();
}
}
}
diff --git a/src/AspNetCore.Identity.Dapper/Tables/RoleClaimsTable.cs b/src/AspNetCore.Identity.Dapper/Tables/RoleClaimsTable.cs
index 93e95df..908f238 100644
--- a/src/AspNetCore.Identity.Dapper/Tables/RoleClaimsTable.cs
+++ b/src/AspNetCore.Identity.Dapper/Tables/RoleClaimsTable.cs
@@ -22,7 +22,7 @@ public class RoleClaimsTable :
/// Creates a new instance of .
///
/// A factory for creating instances of .
- public RoleClaimsTable(IDbConnectionFactory dbConnectionFactory) : base(dbConnectionFactory) { }
+ public RoleClaimsTable(IDbConnectionStore dbConnectionFactory) : base(dbConnectionFactory) { }
///
public virtual async Task> GetClaimsAsync(TKey roleId) {
diff --git a/src/AspNetCore.Identity.Dapper/Tables/RolesTable.cs b/src/AspNetCore.Identity.Dapper/Tables/RolesTable.cs
index b0cca8b..7f39f09 100644
--- a/src/AspNetCore.Identity.Dapper/Tables/RolesTable.cs
+++ b/src/AspNetCore.Identity.Dapper/Tables/RolesTable.cs
@@ -25,10 +25,10 @@ public class RolesTable :
/// Creates a new instance of .
///
/// A factory for creating instances of .
- public RolesTable(IDbConnectionFactory dbConnectionFactory) : base(dbConnectionFactory) { }
+ public RolesTable(IDbConnectionStore dbConnectionFactory) : base(dbConnectionFactory) { }
///
- public virtual async Task CreateAsync(TRole role) {
+ public virtual async Task CreateAsync(TRole role) {
const string sql = "INSERT INTO [dbo].[AspNetRoles] " +
"VALUES (@Id, @Name, @NormalizedName, @ConcurrencyStamp);";
var rowsInserted = await DbConnection.ExecuteAsync(sql, new {
@@ -37,16 +37,26 @@ public virtual async Task CreateAsync(TRole role) {
role.NormalizedName,
role.ConcurrencyStamp
});
- return rowsInserted == 1;
+ return rowsInserted == 1
+ ? IdentityResult.Success
+ : IdentityResult.Failed(new IdentityError {
+ Code = string.Empty,
+ Description = $"Role '{role.Name}' could not be created."
+ });
}
///
- public virtual async Task DeleteAsync(TKey roleId) {
+ public virtual async Task DeleteAsync(TKey roleId) {
const string sql = "DELETE " +
"FROM [dbo].[AspNetRoles] " +
"WHERE [Id] = @Id;";
var rowsDeleted = await DbConnection.ExecuteAsync(sql, new { Id = roleId });
- return rowsDeleted == 1;
+ return rowsDeleted == 1
+ ? IdentityResult.Success
+ : IdentityResult.Failed(new IdentityError {
+ Code = string.Empty,
+ Description = $"Role id '{roleId}' could not be deleted."
+ });
}
///
@@ -68,7 +78,7 @@ public virtual async Task FindByNameAsync(string normalizedName) {
}
///
- public virtual async Task UpdateAsync(TRole role, IList claims = null) {
+ public virtual async Task UpdateAsync(TRole role, IList claims = null) {
const string updateRoleSql = "UPDATE [dbo].[AspNetRoles] " +
"SET [Name] = @Name, [NormalizedName] = @NormalizedName, [ConcurrencyStamp] = @ConcurrencyStamp " +
"WHERE [Id] = @Id;";
@@ -98,10 +108,13 @@ public virtual async Task UpdateAsync(TRole role, IList claims
transaction.Commit();
} catch {
transaction.Rollback();
- return false;
+ return IdentityResult.Failed(new IdentityError {
+ Code = string.Empty,
+ Description = $"Role '{role.Name}' could not be updated."
+ });
}
}
- return true;
+ return IdentityResult.Success;
}
}
}
diff --git a/src/AspNetCore.Identity.Dapper/Tables/TableBase.cs b/src/AspNetCore.Identity.Dapper/Tables/TableBase.cs
deleted file mode 100644
index 466be4e..0000000
--- a/src/AspNetCore.Identity.Dapper/Tables/TableBase.cs
+++ /dev/null
@@ -1,40 +0,0 @@
-using System;
-
-namespace AspNetCore.Identity.Dapper
-{
- ///
- /// A base implementation for all tables.
- ///
- public abstract class TableBase : IDisposable
- {
- private bool _disposed = false;
-
- ///
- public void Dispose() {
- // Dispose of unmanaged resources.
- Dispose(true);
- // Suppress finalization.
- GC.SuppressFinalize(this);
- }
-
- ///
- /// A method to free any other managed objects on classes that onherit from .
- ///
- protected abstract void OnDispose();
-
- ///
- /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
- ///
- /// Indicates whether the method call comes from a Dispose method (its value is true) or from a finalizer (its value is false).
- protected virtual void Dispose(bool disposing) {
- if (_disposed) {
- return;
- }
- if (disposing) {
- // Free any other managed objects here.
- OnDispose();
- }
- _disposed = true;
- }
- }
-}
diff --git a/src/AspNetCore.Identity.Dapper/Tables/UserClaimsTable.cs b/src/AspNetCore.Identity.Dapper/Tables/UserClaimsTable.cs
index 46b0b6b..21839fc 100644
--- a/src/AspNetCore.Identity.Dapper/Tables/UserClaimsTable.cs
+++ b/src/AspNetCore.Identity.Dapper/Tables/UserClaimsTable.cs
@@ -22,7 +22,7 @@ public class UserClaimsTable :
/// Creates a new instance of .
///
/// A factory for creating instances of .
- public UserClaimsTable(IDbConnectionFactory dbConnectionFactory) : base(dbConnectionFactory) { }
+ public UserClaimsTable(IDbConnectionStore dbConnectionFactory) : base(dbConnectionFactory) { }
///
public virtual async Task> GetClaimsAsync(TKey userId) {
diff --git a/src/AspNetCore.Identity.Dapper/Tables/UserLoginsTable.cs b/src/AspNetCore.Identity.Dapper/Tables/UserLoginsTable.cs
index 10f76fa..bf0a923 100644
--- a/src/AspNetCore.Identity.Dapper/Tables/UserLoginsTable.cs
+++ b/src/AspNetCore.Identity.Dapper/Tables/UserLoginsTable.cs
@@ -24,7 +24,7 @@ public class UserLoginsTable :
/// Creates a new instance of .
///
/// A factory for creating instances of .
- public UserLoginsTable(IDbConnectionFactory dbConnectionFactory) : base(dbConnectionFactory) { }
+ public UserLoginsTable(IDbConnectionStore dbConnectionFactory) : base(dbConnectionFactory) { }
///
public virtual async Task> GetLoginsAsync(TKey userId) {
diff --git a/src/AspNetCore.Identity.Dapper/Tables/UserRolesTable.cs b/src/AspNetCore.Identity.Dapper/Tables/UserRolesTable.cs
index 756390a..754139b 100644
--- a/src/AspNetCore.Identity.Dapper/Tables/UserRolesTable.cs
+++ b/src/AspNetCore.Identity.Dapper/Tables/UserRolesTable.cs
@@ -24,7 +24,7 @@ public class UserRolesTable :
/// Creates a new instance of .
///
/// A factory for creating instances of .
- public UserRolesTable(IDbConnectionFactory dbConnectionFactory) : base(dbConnectionFactory) { }
+ public UserRolesTable(IDbConnectionStore dbConnectionFactory) : base(dbConnectionFactory) { }
///
public virtual async Task> GetRolesAsync(TKey userId) {
diff --git a/src/AspNetCore.Identity.Dapper/Tables/UserTokensTable.cs b/src/AspNetCore.Identity.Dapper/Tables/UserTokensTable.cs
index 8df8616..8a96788 100644
--- a/src/AspNetCore.Identity.Dapper/Tables/UserTokensTable.cs
+++ b/src/AspNetCore.Identity.Dapper/Tables/UserTokensTable.cs
@@ -22,7 +22,7 @@ public class UserTokensTable :
/// Creates a new instance of .
///
/// A factory for creating instances of .
- public UserTokensTable(IDbConnectionFactory dbConnectionFactory) : base(dbConnectionFactory) { }
+ public UserTokensTable(IDbConnectionStore dbConnectionFactory) : base(dbConnectionFactory) { }
///
public virtual async Task> GetTokensAsync(TKey userId) {
diff --git a/src/AspNetCore.Identity.Dapper/Tables/UsersTable.cs b/src/AspNetCore.Identity.Dapper/Tables/UsersTable.cs
index 690af4b..215a740 100644
--- a/src/AspNetCore.Identity.Dapper/Tables/UsersTable.cs
+++ b/src/AspNetCore.Identity.Dapper/Tables/UsersTable.cs
@@ -32,10 +32,10 @@ public class UsersTable.
///
/// A factory for creating instances of .
- public UsersTable(IDbConnectionFactory dbConnectionFactory) : base(dbConnectionFactory) { }
+ public UsersTable(IDbConnectionStore dbConnectionFactory) : base(dbConnectionFactory) { }
///
- public virtual async Task CreateAsync(TUser user) {
+ public virtual async Task CreateAsync(TUser user) {
const string sql = "INSERT INTO [dbo].[AspNetUsers] " +
"VALUES (@Id, @UserName, @NormalizedUserName, @Email, @NormalizedEmail, @EmailConfirmed, @PasswordHash, @SecurityStamp, @ConcurrencyStamp, " +
"@PhoneNumber, @PhoneNumberConfirmed, @TwoFactorEnabled, @LockoutEnd, @LockoutEnabled, @AccessFailedCount);";
@@ -56,16 +56,26 @@ public virtual async Task CreateAsync(TUser user) {
user.LockoutEnabled,
user.AccessFailedCount
});
- return rowsInserted == 1;
+ return rowsInserted == 1
+ ? IdentityResult.Success
+ : IdentityResult.Failed(new IdentityError {
+ Code = string.Empty,
+ Description = $"User '{user.UserName}' could not be created."
+ });
}
///
- public virtual async Task DeleteAsync(TKey userId) {
+ public virtual async Task DeleteAsync(TKey userId) {
const string sql = "DELETE " +
"FROM [dbo].[AspNetUsers] " +
"WHERE [Id] = @Id;";
var rowsDeleted = await DbConnection.ExecuteAsync(sql, new { Id = userId });
- return rowsDeleted == 1;
+ return rowsDeleted == 1
+ ? IdentityResult.Success
+ : IdentityResult.Failed(new IdentityError {
+ Code = string.Empty,
+ Description = $"User Id '{userId}' could not be deleted."
+ });
}
///
@@ -96,12 +106,12 @@ public virtual async Task FindByEmailAsync(string normalizedEmail) {
}
///
- public virtual Task UpdateAsync(TUser user, IList claims, IList logins, IList tokens) {
+ public virtual Task UpdateAsync(TUser user, IList claims, IList logins, IList tokens) {
return UpdateAsync(user, claims, null, logins, tokens);
}
///
- public virtual async Task UpdateAsync(TUser user, IList claims, IList roles, IList logins, IList tokens) {
+ public virtual async Task UpdateAsync(TUser user, IList claims, IList roles, IList logins, IList tokens) {
const string updateUserSql =
"UPDATE [dbo].[AspNetUsers] " +
"SET [UserName] = @UserName, [NormalizedUserName] = @NormalizedUserName, [Email] = @Email, [NormalizedEmail] = @NormalizedEmail, [EmailConfirmed] = @EmailConfirmed, " +
@@ -128,66 +138,28 @@ public virtual async Task UpdateAsync(TUser user, IList claims
user.Id
}, transaction);
if (claims?.Count() > 0) {
- const string deleteClaimsSql = "DELETE " +
- "FROM [dbo].[AspNetUserClaims] " +
- "WHERE [UserId] = @UserId;";
- await DbConnection.ExecuteAsync(deleteClaimsSql, new { UserId = user.Id }, transaction);
- const string insertClaimsSql = "INSERT INTO [dbo].[AspNetUserClaims] (UserId, ClaimType, ClaimValue) " +
- "VALUES (@UserId, @ClaimType, @ClaimValue);";
- await DbConnection.ExecuteAsync(insertClaimsSql, claims.Select(x => new {
- UserId = user.Id,
- x.ClaimType,
- x.ClaimValue
- }), transaction);
+ await ReplaceClaims(claims, user.Id, transaction);
}
if (roles?.Count() > 0) {
- const string deleteRolesSql = "DELETE " +
- "FROM [dbo].[AspNetUserRoles] " +
- "WHERE [UserId] = @UserId;";
- await DbConnection.ExecuteAsync(deleteRolesSql, new { UserId = user.Id }, transaction);
- const string insertRolesSql = "INSERT INTO [dbo].[AspNetUserRoles] (UserId, RoleId) " +
- "VALUES (@UserId, @RoleId);";
- await DbConnection.ExecuteAsync(insertRolesSql, roles.Select(x => new {
- UserId = user.Id,
- x.RoleId
- }), transaction);
+ await ReplaceRoles(roles, user.Id, transaction);
}
if (logins?.Count() > 0) {
- const string deleteLoginsSql = "DELETE " +
- "FROM [dbo].[AspNetUserLogins] " +
- "WHERE [UserId] = @UserId;";
- await DbConnection.ExecuteAsync(deleteLoginsSql, new { UserId = user.Id }, transaction);
- const string insertLoginsSql = "INSERT INTO [dbo].[AspNetUserLogins] (LoginProvider, ProviderKey, ProviderDisplayName, UserId) " +
- "VALUES (@LoginProvider, @ProviderKey, @ProviderDisplayName, @UserId);";
- await DbConnection.ExecuteAsync(insertLoginsSql, logins.Select(x => new {
- x.LoginProvider,
- x.ProviderKey,
- x.ProviderDisplayName,
- UserId = user.Id
- }), transaction);
+ await ReplaceLogins(logins, user.Id, transaction);
}
if (tokens?.Count() > 0) {
- const string deleteTokensSql = "DELETE " +
- "FROM [dbo].[AspNetUserTokens] " +
- "WHERE [UserId] = @UserId;";
- await DbConnection.ExecuteAsync(deleteTokensSql, new { UserId = user.Id }, transaction);
- const string insertTokensSql = "INSERT INTO [dbo].[AspNetUserTokens] (UserId, LoginProvider, Name, Value) " +
- "VALUES (@UserId, @LoginProvider, @Name, @Value);";
- await DbConnection.ExecuteAsync(insertTokensSql, tokens.Select(x => new {
- x.UserId,
- x.LoginProvider,
- x.Name,
- x.Value
- }), transaction);
+ await ReplaceTokens(tokens, user.Id, transaction);
}
try {
transaction.Commit();
} catch {
transaction.Rollback();
- return false;
+ return IdentityResult.Failed(new IdentityError {
+ Code = string.Empty,
+ Description = $"User '{user.UserName}' could not be updated."
+ });
}
}
- return true;
+ return IdentityResult.Success;
}
///
@@ -213,5 +185,62 @@ public virtual async Task> GetUsersForClaimAsync(Claim claim)
});
return users;
}
+ ///
+ protected virtual async Task ReplaceClaims(IList claims, TKey userId, IDbTransaction transaction) {
+ const string deleteClaimsSql = "DELETE " +
+ "FROM [dbo].[AspNetUserClaims] " +
+ "WHERE [UserId] = @UserId;";
+ await DbConnection.ExecuteAsync(deleteClaimsSql, new { UserId = userId }, transaction);
+ const string insertClaimsSql = "INSERT INTO [dbo].[AspNetUserClaims] (UserId, ClaimType, ClaimValue) " +
+ "VALUES (@UserId, @ClaimType, @ClaimValue);";
+ return await DbConnection.ExecuteAsync(insertClaimsSql, claims.Select(x => new {
+ UserId = userId,
+ x.ClaimType,
+ x.ClaimValue
+ }), transaction);
+ }
+ ///
+ protected virtual async Task ReplaceRoles(IList roles, TKey userId, IDbTransaction transaction) {
+ const string deleteRolesSql = "DELETE " +
+ "FROM [dbo].[AspNetUserRoles] " +
+ "WHERE [UserId] = @UserId;";
+ await DbConnection.ExecuteAsync(deleteRolesSql, new { UserId = userId }, transaction);
+ const string insertRolesSql = "INSERT INTO [dbo].[AspNetUserRoles] (UserId, RoleId) " +
+ "VALUES (@UserId, @RoleId);";
+ return await DbConnection.ExecuteAsync(insertRolesSql, roles.Select(x => new {
+ UserId = userId,
+ x.RoleId
+ }), transaction);
+ }
+ ///
+ protected virtual async Task ReplaceLogins(IList logins, TKey userId, IDbTransaction transaction) {
+ const string deleteLoginsSql = "DELETE " +
+ "FROM [dbo].[AspNetUserLogins] " +
+ "WHERE [UserId] = @UserId;";
+ await DbConnection.ExecuteAsync(deleteLoginsSql, new { UserId = userId }, transaction);
+ const string insertLoginsSql = "INSERT INTO [dbo].[AspNetUserLogins] (LoginProvider, ProviderKey, ProviderDisplayName, UserId) " +
+ "VALUES (@LoginProvider, @ProviderKey, @ProviderDisplayName, @UserId);";
+ return await DbConnection.ExecuteAsync(insertLoginsSql, logins.Select(x => new {
+ x.LoginProvider,
+ x.ProviderKey,
+ x.ProviderDisplayName,
+ UserId = userId
+ }), transaction);
+ }
+ ///
+ protected virtual async Task ReplaceTokens(IList tokens, TKey userId, IDbTransaction transaction) {
+ const string deleteTokensSql = "DELETE " +
+ "FROM [dbo].[AspNetUserTokens] " +
+ "WHERE [UserId] = @UserId;";
+ await DbConnection.ExecuteAsync(deleteTokensSql, new { UserId = userId }, transaction);
+ const string insertTokensSql = "INSERT INTO [dbo].[AspNetUserTokens] (UserId, LoginProvider, Name, Value) " +
+ "VALUES (@UserId, @LoginProvider, @Name, @Value);";
+ return await DbConnection.ExecuteAsync(insertTokensSql, tokens.Select(x => new {
+ x.UserId,
+ x.LoginProvider,
+ x.Name,
+ x.Value
+ }), transaction);
+ }
}
}