giorgos07 / daarto Goto Github PK
View Code? Open in Web Editor NEWDapper implementation of ASP.NET Core Identity stores.
Dapper implementation of ASP.NET Core Identity stores.
Hi, accordingly to this issue the identity managers, including the UserManager, are based on the assumption that the user store is based on an unit of work pattern so basically the only methods actually performing crud operations are the ones in the IUserStore interface. I didn't read all your code so I'd like to ask if you had taken that issue in account with your implementation.
DapperStoreOptions
includes an instance of SqlServerDbConnectionStore
which is only every used for its GetType()
method. It would seem to make more sense to have a property of DapperStoreOptions:
DbConnectionFactoryType = typeof(SqlServerDbConnectionStore),
Hi, im very new to ASP.NET but can anyone explain how this can work if it does not create any required tables into database???
Maybe this have some code to create tables in database?
Thanks.
Hello,
I would like to use Guid ID type as ID of Users and Roles...
In the current extension method the 'string' type is hardcoded in more place (one example):
public static void AddUserRolesTable<TUserRolesTable, TUserRole>(this DapperStoreOptions options)
where TUserRolesTable : UserRolesTable<IdentityRole, string, TUserRole>
where TUserRole : IdentityUserRole<string>, new() {
options.AddUserRolesTable<TUserRolesTable, IdentityRole, string, TUserRole>();
I think the string type should be replaced with TKey.
requeting author to update the framework to .NET Core 6
Dapper is mapping the Id from the Roles table in this query to the ApplicationUser object:
const string command = "SELECT * " +
"FROM dbo.Users AS u " +
"INNER JOIN dbo.UserRoles AS ur ON u.Id = ur.UserId " +
"INNER JOIN dbo.Roles AS r ON ur.RoleId = r.Id " +
"WHERE r.Name = @RoleName;";
SELECT u.* fixes the issue.
Same issue in GetUsersForClaimAsync.
At the moment the calls to the table
classes (interacting with the database) return a boolean for a successful or unsuccessful create update and delete operations and the stores create fairly unhelpful IdentityErrors
instance if the operation fails.
I would like to have the table classes in my own classes inheriting from these to be able to return more specific data about why the sql operation failed. The easiest way to do this is to move the returned IdentityResult
on to the Table
classes.
I have implemented this and can submit a pull request if you would like to see the diff.
The line
if (roleType != null) {
...
} else {
services.TryAddScoped(
typeof(IUsersOnlyTable<,,,,>).MakeGenericType(userType, keyType, userClaimType, userLoginType, userTokenType),
typeof(UsersTable<,,,,,>).MakeGenericType(userType, keyType, userClaimType, roleType, userLoginType, userTokenType)
will throw an error if roleType
is null because roleType
is used as an argument in the second call to the MakeGenericType
method
I believe the second call should use the userRoleType
as the 4th argument, not rollType
typeof(UsersTable<,,,,,>).MakeGenericType(userType, keyType, userClaimType, userRoleType, userLoginType, userTokenType)
Grabbed the test code from here: #14
I tried overriding database names by using the class AppUser.cs and AppUsersTable.cs and setting the table name from AspNetUsers to Users:
public class AppUser : IdentityUser
{
public override string Email { get; set; }
public override string UserName { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
}
AppUsersTable.cs:
public class AppUsersTable: UsersTable<AppUser, string, IdentityUserClaim<string>, IdentityUserRole<string>,
IdentityUserLogin<string>, IdentityUserToken<string>>
{
public AppUsersTable(IDbConnectionFactory dbConnectionFactory) : base(dbConnectionFactory) { }
public override async Task<bool> CreateAsync(AppUser user) {
const string sql = "INSERT INTO [dbo].[Users] " +
"VALUES (@Id, @UserName, @NormalizedUserName, @Email, @NormalizedEmail, @EmailConfirmed,
@PasswordHash, @SecurityStamp, @ConcurrencyStamp, " +
"@PhoneNumber, @PhoneNumberConfirmed, @TwoFactorEnabled, @LockoutEnd, @LockoutEnabled,
@AccessFailedCount, @FirstName, @LastName);";
var rowsInserted = await DbConnection.ExecuteAsync(sql, new {
user.Id,
user.UserName,
user.NormalizedUserName,
user.Email,
user.NormalizedEmail,
user.EmailConfirmed,
user.PasswordHash,
user.SecurityStamp,
user.ConcurrencyStamp,
user.PhoneNumber,
user.PhoneNumberConfirmed,
user.TwoFactorEnabled,
user.LockoutEnd,
user.LockoutEnabled,
user.AccessFailedCount,
user.FirstName,
user.LastName
});
return rowsInserted == 1;
}
public override async Task<bool> UpdateAsync(AppUser user, IList<IdentityUserClaim<string>> claims, IList<IdentityUserRole<string>> roles, IList<IdentityUserLogin<string>> logins, IList<IdentityUserToken<string>> tokens) {
const string updateUserSql =
"UPDATE [dbo].[Users] " +
"SET [UserName] = @UserName, [NormalizedUserName] = @NormalizedUserName, [Email] = @Email, [NormalizedEmail] = @NormalizedEmail, [EmailConfirmed] = @EmailConfirmed, " +
"[PasswordHash] = @PasswordHash, [SecurityStamp] = @SecurityStamp, [ConcurrencyStamp] = @ConcurrencyStamp, [PhoneNumber] = @PhoneNumber, " +
"[PhoneNumberConfirmed] = @PhoneNumberConfirmed, [TwoFactorEnabled] = @TwoFactorEnabled, [LockoutEnd] = @LockoutEnd, [LockoutEnabled] = @LockoutEnabled, " +
"[AccessFailedCount] = @AccessFailedCount , [FirstName] = @FirstName, [LastName] = @LastName " +
"WHERE [Id] = @Id;";
using (var transaction = DbConnection.BeginTransaction()) {
await DbConnection.ExecuteAsync(updateUserSql, new {
user.UserName,
user.NormalizedUserName,
user.Email,
user.NormalizedEmail,
user.EmailConfirmed,
user.PasswordHash,
user.SecurityStamp,
user.ConcurrencyStamp,
user.PhoneNumber,
user.PhoneNumberConfirmed,
user.TwoFactorEnabled,
user.LockoutEnd,
user.LockoutEnabled,
user.AccessFailedCount,
user.FirstName,
user.LastName,
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);
}
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);
}
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);
}
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);
}
try {
transaction.Commit();
} catch {
transaction.Rollback();
return false;
}
}
return true;
}
public override async Task<IEnumerable<AppUser>> GetUsersInRoleAsync(string roleName)
{
const string sql = "SELECT [u].* " +
"FROM [dbo].[Users] AS [u] " +
"INNER JOIN [dbo].[AspNetUserRoles] AS [ur] ON [u].[Id] = [ur].[UserId] " +
"INNER JOIN [dbo].[AspNetRoles] AS [r] ON [ur].[RoleId] = [r].[Id] " +
"WHERE [r].[Name] = @RoleName;";
var users = await DbConnection.QueryAsync<AppUser>(sql, new { RoleName = roleName });
return users;
}
public override async Task<IEnumerable<AppUser>> GetUsersForClaimAsync(Claim claim)
{
const string sql = "SELECT [u].* " +
"FROM [dbo].[Users] AS [u] " +
"INNER JOIN [dbo].[AspNetUserClaims] AS [uc] ON [u].[Id] = [uc].[UserId] " +
"WHERE [uc].[ClaimType] = @ClaimType AND [uc].[ClaimValue] = @ClaimValue;";
var users = await DbConnection.QueryAsync<AppUser>(sql, new
{
ClaimType = claim.Type,
ClaimValue = claim.Value
});
return users;
}
Startup.cs - ConfigureServices:
services.AddIdentity<IdentityUser, ExtendedIdentityRole>()
.AddDapperStores(options => {
options.AddRolesTable<ExtendedRolesTable, ExtendedIdentityRole>();
options.AddUsersTable<AppUsersTable, AppUser>();
})
.AddDefaultUI()
.AddDefaultTokenProviders();
//services.AddDefaultIdentity<IdentityUser>()
// .AddDapperStores();
services.Configure<IdentityOptions>(options =>
{
options.Password.RequireDigit = false;
options.Password.RequiredLength = 5;
options.Password.RequireLowercase = true;
options.Password.RequireNonAlphanumeric = false;
options.Password.RequireUppercase = false;
});
services.AddRazorPages();
services.AddServerSideBlazor();
services
.AddScoped<AuthenticationStateProvider, RevalidatingIdentityAuthenticationStateProvider<IdentityUser>
>();
services.AddSingleton<WeatherForecastService>();
Error:
Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware[1]
An unhandled exception has occurred while executing the request.
System.Data.SqlClient.SqlException (0x80131904): Invalid object name 'dbo.AspNetUsers'.
at System.Data.SqlClient.SqlCommand.<>c.<ExecuteDbDataReaderAsync>b__126_0(Task`1 result)
at System.Threading.Tasks.ContinuationResultTaskFromResultTask`2.InnerInvoke()
at System.Threading.Tasks.Task.<>c.<.cctor>b__274_0(Object obj)
at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object
state)
--- End of stack trace from previous location where exception was thrown ---
at System.Threading.Tasks.Task.ExecuteWithThreadLocal(Task& currentTaskSlot, Thread threadPoolThread)
--- End of stack trace from previous location where exception was thrown ---
at Dapper.SqlMapper.QueryRowAsync[T](IDbConnection cnn, Row row, Type effectiveType, CommandDefinition
command) in /_/Dapper/SqlMapper.Async.cs:line 483
at AspNetCore.Identity.Dapper.UsersTable`6.FindByNameAsync(String normalizedUserName)
at AspNetCore.Identity.Dapper.UserStore`8.FindByNameAsync(String normalizedUserName, CancellationToken
cancellationToken)
at Microsoft.AspNetCore.Identity.UserManager`1.FindByNameAsync(String userName)
at Microsoft.AspNetCore.Identity.UserValidator`1.ValidateUserName(UserManager`1 manager, TUser user, ICollection`1
errors)
at Microsoft.AspNetCore.Identity.UserValidator`1.ValidateAsync(UserManager`1 manager, TUser user)
at Microsoft.AspNetCore.Identity.UserManager`1.ValidateUserAsync(TUser user)
at Microsoft.AspNetCore.Identity.UserManager`1.CreateAsync(TUser user)
at Microsoft.AspNetCore.Identity.UserManager`1.CreateAsync(TUser user, String password)
at Microsoft.AspNetCore.Identity.UI.V4.Pages.Account.Internal.RegisterModel`1.OnPostAsync(String returnUrl)
at Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure.ExecutorFactory.GenericTaskHandlerMethod.Convert[T](Object
taskAsObject)
at Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure.ExecutorFactory.GenericTaskHandlerMethod.Execute(Object receiver,
Object[] arguments)
at Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure.PageActionInvoker.InvokeHandlerMethodAsync()
at Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure.PageActionInvoker.InvokeNextPageFilterAsync()
at Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure.PageActionInvoker.Rethrow(PageHandlerExecutedContext context)
at Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure.PageActionInvoker.Next(State& next, Scope& scope, Object& state,
Boolean& isCompleted)
at Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure.PageActionInvoker.InvokeInnerFilterAsync()
at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeNextResourceFilter>g__Awaited|24_0(ResourceInvoker
invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)
at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeNextResourceFilter>g__Awaited|24_0(ResourceInvoker
invoker, Task lastTask, Stat
e next, Scope scope, Object state, Boolean isCompleted)
at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.Rethrow(ResourceExecutedContextSealed context)
at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.Next(State& next, Scope& scope, Object& state, Boolean&
isCompleted)
at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeFilterPipelineAsync>g__Awaited|19_0(ResourceInvoker
invoker, Task lastTask, Sta
te next, Scope scope, Object state, Boolean isCompleted)
at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeAsync>g__Awaited|17_0(ResourceInvoker invoker,
Task
task, IDisposable scope)
at Microsoft.AspNetCore.Routing.EndpointMiddleware.<Invoke>g__AwaitRequestTask|6_0(Endpoint endpoint, Task
requestTask, ILogger logger)
at Microsoft.AspNetCore.Authorization.AuthorizationMiddleware.Invoke(HttpContext context)
at Microsoft.AspNetCore.Authentication.AuthenticationMiddleware.Invoke(HttpContext context)
at Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware.Invoke(HttpContext context)
ClientConnectionId:0506a842-1daa-449b-af88-6630ae741bcb
Error Number:208,State:1,Class:16
I am edited source core for adaptive PorstgreSQL db. How I can send to git it?
:( public override IQueryable<TUser> Users => throw new NotSupportedException();
I've implemented a custom UsersTable to handle our custom IdentityUser:
public class AppUser : IdentityUser
{
[LogReportColumn(HeaderText = "User Name")]
[ExcelMap(SourceFileMappingField = "User Name")]
[Display(Name = "UserName", ResourceType = typeof(Resources.SharedResource))]
public override string UserName { get; set; }
[LogReportColumn(HeaderText = "Email")]
[ExcelMap(SourceFileMappingField = "Email")]
[ProtectedPersonalData, Required(ErrorMessageResourceName = "Required", ErrorMessageResourceType = typeof(Resources.SharedResource)), StringLength(256)]
[EmailAddress(ErrorMessageResourceName = "EmailWrongFormat", ErrorMessageResourceType = typeof(Resources.SharedResource))]
[Display(Name = "Email", ResourceType = typeof(Resources.SharedResource))]
public override string Email { get; set; }
[LogReportColumn(HeaderText = "First Name")]
[ExcelMap(SourceFileMappingField = "First Name")]
[PersonalData, Required(ErrorMessageResourceName = "Required", ErrorMessageResourceType = typeof(Resources.SharedResource)), StringLength(250)]
[Display(Name = "FirstName", ResourceType = typeof(Resources.SharedResource))]
public string FirstName { get; set; }
[LogReportColumn(HeaderText = "Last Name")]
[ExcelMap(SourceFileMappingField = "Last Name")]
[PersonalData, Required(ErrorMessageResourceName = "Required", ErrorMessageResourceType = typeof(Resources.SharedResource)), StringLength(250)]
[Display(Name = "LastName", ResourceType = typeof(Resources.SharedResource))]
public string LastName { get; set; }
}
public class AppUsersTable: UsersTable<AppUser, string, IdentityUserClaim<string>, IdentityUserRole<string>, IdentityUserLogin<string>, IdentityUserToken<string>>
{
public AppUsersTable(IDbConnectionFactory dbConnectionFactory) : base(dbConnectionFactory) { }
public override async Task<bool> CreateAsync(AppUser user) {
const string sql = "INSERT INTO [dbo].[AspNetUsers] " +
"VALUES (@Id, @UserName, @NormalizedUserName, @Email, @NormalizedEmail, @EmailConfirmed, @PasswordHash, @SecurityStamp, @ConcurrencyStamp, " +
"@PhoneNumber, @PhoneNumberConfirmed, @TwoFactorEnabled, @LockoutEnd, @LockoutEnabled, @AccessFailedCount, @FirstName, @LastName);";
var rowsInserted = await DbConnection.ExecuteAsync(sql, new {
user.Id,
user.UserName,
user.NormalizedUserName,
user.Email,
user.NormalizedEmail,
user.EmailConfirmed,
user.PasswordHash,
user.SecurityStamp,
user.ConcurrencyStamp,
user.PhoneNumber,
user.PhoneNumberConfirmed,
user.TwoFactorEnabled,
user.LockoutEnd,
user.LockoutEnabled,
user.AccessFailedCount,
user.FirstName,
user.LastName
});
return rowsInserted == 1;
}
public override async Task<bool> UpdateAsync(AppUser user, IList<IdentityUserClaim<string>> claims, IList<IdentityUserRole<string>> roles, IList<IdentityUserLogin<string>> logins, IList<IdentityUserToken<string>> tokens) {
const string updateUserSql =
"UPDATE [dbo].[AspNetUsers] " +
"SET [UserName] = @UserName, [NormalizedUserName] = @NormalizedUserName, [Email] = @Email, [NormalizedEmail] = @NormalizedEmail, [EmailConfirmed] = @EmailConfirmed, " +
"[PasswordHash] = @PasswordHash, [SecurityStamp] = @SecurityStamp, [ConcurrencyStamp] = @ConcurrencyStamp, [PhoneNumber] = @PhoneNumber, " +
"[PhoneNumberConfirmed] = @PhoneNumberConfirmed, [TwoFactorEnabled] = @TwoFactorEnabled, [LockoutEnd] = @LockoutEnd, [LockoutEnabled] = @LockoutEnabled, " +
"[AccessFailedCount] = @AccessFailedCount , [FirstName] = @FirstName, [LastName] = @LastName " +
"WHERE [Id] = @Id;";
using (var transaction = DbConnection.BeginTransaction()) {
await DbConnection.ExecuteAsync(updateUserSql, new {
user.UserName,
user.NormalizedUserName,
user.Email,
user.NormalizedEmail,
user.EmailConfirmed,
user.PasswordHash,
user.SecurityStamp,
user.ConcurrencyStamp,
user.PhoneNumber,
user.PhoneNumberConfirmed,
user.TwoFactorEnabled,
user.LockoutEnd,
user.LockoutEnabled,
user.AccessFailedCount,
user.FirstName,
user.LastName,
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);
}
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);
}
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);
}
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);
}
try {
transaction.Commit();
} catch {
transaction.Rollback();
return false;
}
}
return true;
}
public override async Task<IEnumerable<AppUser>> GetUsersInRoleAsync(string roleName)
{
const string sql = "SELECT [u].* " +
"FROM [dbo].[AspNetUsers] AS [u] " +
"INNER JOIN [dbo].[AspNetUserRoles] AS [ur] ON [u].[Id] = [ur].[UserId] " +
"INNER JOIN [dbo].[AspNetRoles] AS [r] ON [ur].[RoleId] = [r].[Id] " +
"WHERE [r].[Name] = @RoleName;";
var users = await DbConnection.QueryAsync<AppUser>(sql, new { RoleName = roleName });
return users;
}
public override async Task<IEnumerable<AppUser>> GetUsersForClaimAsync(Claim claim)
{
const string sql = "SELECT [u].* " +
"FROM [dbo].[AspNetUsers] AS [u] " +
"INNER JOIN [dbo].[AspNetUserClaims] AS [uc] ON [u].[Id] = [uc].[UserId] " +
"WHERE [uc].[ClaimType] = @ClaimType AND [uc].[ClaimValue] = @ClaimValue;";
var users = await DbConnection.QueryAsync<AppUser>(sql, new
{
ClaimType = claim.Type,
ClaimValue = claim.Value
});
return users;
}
}
With Startup amended to:
var connectionString = Configuration.GetConnectionString("DefaultConnection");
services.AddIdentity<AppUser, IdentityRole>(options =>
{
options.SignIn.RequireConfirmedAccount = true;
options.User.RequireUniqueEmail = true;
})
.AddRoles<IdentityRole>()
.AddDapperStores(options =>
{
options.ConnectionString = connectionString;
options.AddUsersTable<AppUsersTable, AppUser>();
})
.AddDefaultTokenProviders();
everything works fine, except the insert and update of an AppUser. The overridden methods CreateUserAsync and UpdateAsync are not called.
Do I miss something?
Hello,
If I would like use another schema than "dbo" I need overwrite all of the Table queries.
It would be great if I can change the schema with option parameter.
Thank you!
When trying to RemoveFromRoleAsync using the following method:
This line does not remove the user role:
It does not generate an error, but just doesn't remove the role from the User Roles table.
Any suggestions?
DapperStoreOptionsExtension adds the scoped IService
options.Services.AddScoped(typeof(IUsersTable<,,,,,>).MakeGeneric....
However if a roleType is not provided, DI looks for an implementation of IUsersOnlyTable<,,,,>
. There are 2 possible solutions depending on what you want:
IUsersTable
and IUsersOnlyTable
in the AddUsersTable
method of DapperStoreOptionsExtension
IUsersTable
with 1 for the service IUsersOnlyTable
and the same implementationType, along the lines of:var usersTableServiceType = typeof(IUsersTable<,,,,,>).MakeGenericType(userType, keyType, userClaimType, userRoleType, userLoginType, userTokenType);
if (roleType != null) {
...
services.TryAddScoped(
usersTableServiceType,
typeof(UsersTable<,,,,,>).MakeGenericType(userType, keyType, userClaimType, userRoleType, userLoginType, userTokenType)
);
....
} else {
var serviceType = services.FirstOrDefault(s => s.ServiceType == usersTableServiceType);
usersTableServiceType = typeof(IUsersOnlyTable<,,,,>).MakeGenericType(userType, keyType, userClaimType, userLoginType, userTokenType);
if (serviceType == null)
{
services.TryAddScoped(usersTableServiceType, typeof(UsersTable<,,,,,>).MakeGenericType(userType, keyType, userClaimType, userRoleType, userLoginType, userTokenType));
}
else
{
services.Remove(serviceType);
services.TryAddScoped(usersTableServiceType, serviceType.ImplementationType);
}
userStoreType = typeof(UserOnlyStore<,,,,>).MakeGenericType(userType, keyType, userClaimType, userLoginType, userTokenType);
}
There is a missing parameter in the SQL for UpdateAsync in the UserTable.cs file.
The query requires an id parameter to execute correctly. This can be seen if you try to call
_userManager.AddToRoleAsync
for example when you want to register a user with some kind of Roles
await DbConnection.ExecuteAsync(updateUserSql, new {
user.UserName,
user.NormalizedUserName,
user.Email,
user.NormalizedEmail,
user.EmailConfirmed,
user.PasswordHash,
user.SecurityStamp,
user.ConcurrencyStamp,
user.PhoneNumber,
user.PhoneNumberConfirmed,
user.TwoFactorEnabled,
user.LockoutEnd,
user.LockoutEnabled,
user.AccessFailedCount,
user.Id // <<== missing
I am trying to get a list of Roles by using _roleManager.Roles. Here is my code:
public class RoleManagerController : Controller
{
private RoleManager _roleManager;public RoleManagerController( RoleManager<ExtendedIdentityRole> roleManager ) { _roleManager = roleManager; } public IActionResult Index() { var models = _roleManager.Roles; return View(models); }
}
But it looks like this property is not implemented:
Is there another way to get the list of Roles?
Thanks,
Pete
At the moment each of the 8 classes inheriting from IdentityTable
create their own open connection to the DB at instantiation, all of which will only be closed when the scoped instance is disposed. This seems inefficient. I am a long way from an expert on the topic, but I wonder whether a more efficient method of doing this could be created by using any/all of:
IDbConnectionFactory
- now really an IdbConnectionStore
implement IDisposable instead of the IdentityTables
, and rather than create have a getOrCreate
property.IdentityTable
instances when required, rather than at instantiationexecute
or query
methods is called. It may however be more efficient to leave the connection open - I am unsure.A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.