Giter Site home page Giter Site logo

daarto's Introduction

Daarto

The main purpose of this repository is to demonstrate a custom implementation of ASP.NET Core Identity stores by using SQL Server and Dapper, in case you do not want to use the default implementation provided by Entity Framework. The sample application uses ASP.NET Core 3.1 and is built by using Visual Studio 2019 Community Edition and SQL Local DB.

Getting Started

The solution contains a project which implements the core interfaces of ASP.NET Core Identity framework by using plain T-SQL. You can use the project as a base for your own implementation but you can also include it in your project as a nuget package.

Please refer to the wiki section if you want to learn more how to use the package and the capabilities it offers.

daarto's People

Contributors

angelobartolome avatar giorgos07 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

daarto's Issues

How it's suppose to work if DB have no tables?

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.

Move IdentityResult return value to IdentityTable classes?

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.

IdentityBuilderExtensions.AddStores ignores AddUsersTable option if implementation is UserOnlyStore

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:

  1. add a scoped service for both IUsersTable and IUsersOnlyTable in the AddUsersTable method of DapperStoreOptionsExtension
  2. Alter the IServicesCollection to replace the IService for 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);
}

Missing parameter in UserTable.UpdateAsync

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

UsersTable override doesn't work

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?

String should be TKey in DapperStoreOptionsExtentions to use different ID type

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.

IdentityBuilderExtensions check null, then uses null value

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)

Cannot override table names

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

Getting List of Roles

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:

public override IQueryable<TRole> Roles => throw new NotSupportedException();

Is there another way to get the list of Roles?

Thanks,
Pete

Database schema (dbo) is hardcoded

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!

UsersTable:GetUsersInRoleAsync - ApplicationUser.Id is Role.Id

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.

Improve DB connection efficiency

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:

  1. Have the scoped IDbConnectionFactory return a single instance rather than create a new connection every time. Have the IDbConnectionFactory - now really an IdbConnectionStore implement IDisposable instead of the IdentityTables, and rather than create have a getOrCreate property.
  2. Only access the connection in the IdentityTable instances when required, rather than at instantiation
  3. Do not open the connection manually - as I understand if the connection is not open Dapper will open and close it automatically when one of the execute or query methods is called. It may however be more efficient to leave the connection open - I am unsure.

Unit of work implementation needed

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 creates unused SqlServerDbConnectionStore

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

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.