Хорошо, так что это длинный ответ, будьте готовы потратить следующий день, чтобы вырвать себе волосы из головы:).
Сначала интерфейс IApplicationUser
, который реализуется двумя различными классами:
public interface IApplicationUser
{
string Id { get; set; }
/// <summary>Gets or sets the user name for this user.</summary>
string UserName { get; set; }
}
Реализация 1, это часть моей базы данных:
public class ApplicationUser : IdentityUser<string>, IApplicationUser
{
public DateTime? LockoutEndDateUtc { get; set; }
public bool RequiresPasswordCreation { get; set; }
public string TemporaryToken { get; set; }
}
Реализация 2, для моих проектов .NET Framework:
public class ApplicationUserMvc : IdentityUser<string, ApplicationUserLogin, ApplicationUserRole, ApplicationUserClaim>, IApplicationUser
{
public async Task<ClaimsIdentity> GenerateUserIdentityAsync(UserManager<ApplicationUserMvc, string> manager)
{
// Note the authenticationType must match the one defined in CookieAuthenticationOptions.AuthenticationType
var userIdentity = await manager.CreateIdentityAsync(this, DefaultAuthenticationTypes.ApplicationCookie);
// Add custom user claims here
return userIdentity;
}
...all the other things
}
Затем я создал свой собственный Identity Manager (интерфейс)
public interface IIdentityManager
{
//User manager methods
Task<IIdentityResult> CreateAsync(IApplicationUser user);
//..all methods needed
}
public interface IIdentityResult
{
bool Succeeded { get; set; }
List<string> Errors { get; set; }
}
Тогда мои реальные реализации этого, так что это для моих проектов .NET Core.
public class IdentityManagerCore : IIdentityManager
{
private readonly UserManager<ApplicationUser> _userManager;
private readonly RoleManager<ApplicationRole> _roleManager;
public IdentityManagerCore(UserManager<ApplicationUser> userManager, RoleManager<ApplicationRole> roleManager)
{
_userManager = userManager;
_roleManager = roleManager;
}
public async Task<IIdentityResult> CreateAsync(IApplicationUser user)
{
ApplicationUser realUser = new ApplicationUser()
{
Id = user.Id,
TemporaryToken = user.TemporaryToken,
AccessFailedCount = user.AccessFailedCount,
ConcurrencyStamp = user.ConcurrencyStamp,
Email = user.Email,
EmailConfirmed = user.EmailConfirmed,
LockoutEnabled = user.LockoutEnabled,
LockoutEnd = user.LockoutEnd,
NormalizedEmail = user.NormalizedEmail,
NormalizedUserName = user.NormalizedUserName,
PasswordHash = user.PasswordHash,
PhoneNumber = user.PhoneNumber,
PhoneNumberConfirmed = user.PhoneNumberConfirmed,
RequiresPasswordCreation = user.RequiresPasswordCreation,
SecurityStamp = user.SecurityStamp,
TwoFactorEnabled = user.TwoFactorEnabled,
UserName = user.UserName
};
var result = await _userManager.CreateAsync(realUser);
return ConvertToInterface(result);
}
private IIdentityResult ConvertToInterface(IdentityResult result)
{
IIdentityResult realResult = new IdentityResultCore();
realResult.Succeeded = result.Succeeded;
realResult.Errors = result.Errors?.Select(x => x.Description).ToList();
return realResult;
}
}
public class IdentityResultCore : IdentityResult, IIdentityResult
{
private IEnumerable<string> _errors;
private bool _succeed;
public new bool Succeeded
{
get => base.Succeeded || _succeed;
set => _succeed = value;
}
public new List<string> Errors
{
get => base.Errors?.Select(x => x.Description).ToList() ?? _errors?.ToList();
set => _errors = value;
}
}
UserManager
и RoleManager
вводятся при запуске следующим образом:
services.AddTransient<IIdentityManager, IdentityManagerCore>();
Реализация .NET Framework:
public class IdentityManagerMvc : IIdentityManager
{
private readonly UserManager<ApplicationUserMvc, string> _userManager = null;
private readonly RoleManager<ApplicationRoleMvc, string> _roleManager = null;
internal static IDataProtectionProvider DataProtectionProvider { get; private set; }
public IdentityManagerMvc(DatabaseContextMvc dbContext)
{
var userStore =
new UserStore<ApplicationUserMvc, ApplicationRoleMvc, string, ApplicationUserLogin, ApplicationUserRole, ApplicationUserClaim>(dbContext);
var roleStore = new RoleStore<ApplicationRoleMvc, string, ApplicationUserRole>(dbContext);
_userManager = new UserManager<ApplicationUserMvc, string>(userStore);
_roleManager = new RoleManager<ApplicationRoleMvc, string>(roleStore);
_userManager.UserValidator = new UserValidator<ApplicationUserMvc>(_userManager) { AllowOnlyAlphanumericUserNames = false };
if (DataProtectionProvider == null)
{
DataProtectionProvider = new MachineKeyProtectionProvider();
}
_userManager.UserTokenProvider = new DataProtectorTokenProvider<ApplicationUserMvc, string>(DataProtectionProvider.Create("Identity"));
}
public async Task<IIdentityResult> CreateAsync(IApplicationUser user)
{
ApplicationUserMvc realUser = new ApplicationUserMvc()
{
Id = user.Id,
TemporaryToken = user.TemporaryToken,
AccessFailedCount = user.AccessFailedCount,
ConcurrencyStamp = user.ConcurrencyStamp,
Email = user.Email,
EmailConfirmed = user.EmailConfirmed,
LockoutEnabled = user.LockoutEnabled,
LockoutEnd = user.LockoutEnd,
NormalizedEmail = user.NormalizedEmail,
NormalizedUserName = user.NormalizedUserName,
PasswordHash = user.PasswordHash,
PhoneNumber = user.PhoneNumber,
PhoneNumberConfirmed = user.PhoneNumberConfirmed,
RequiresPasswordCreation = user.RequiresPasswordCreation,
SecurityStamp = user.SecurityStamp,
TwoFactorEnabled = user.TwoFactorEnabled,
UserName = user.UserName
};
var result = await _userManager.CreateAsync(realUser);
return ConvertToInterface(result);
}
private IIdentityResult ConvertToInterface(IdentityResult result)
{
IIdentityResult realResult = new IdentityResultMvc();
realResult.Succeeded = result.Succeeded;
realResult.Errors = result.Errors?.ToList();
return realResult;
}
}
public class IdentityResultMvc : IdentityResult, IIdentityResult
{
private IEnumerable<string> _errors;
private bool _succeed;
public new bool Succeeded
{
get => base.Succeeded || _succeed;
set => _succeed = value;
}
public new List<string> Errors
{
get => base.Errors?.ToList() ?? _errors?.ToList();
set => _errors = value;
}
}
И последнее, но не менее важное: вам понадобится отдельный DatabaseContext
для вашего проекта .NET Framework, этот будет использоваться только в целях «идентификации», чтобы фактически не запрашивать какие-либо данные, только для аутентификации и авторизации.
public class DatabaseContextMvc : IdentityDbContext<ApplicationUserMvc, ApplicationRoleMvc, string, ApplicationUserLogin,
ApplicationUserRole, ApplicationUserClaim>
{
public DatabaseContextMvc() : base("DatabaseContext")
{
Configuration.LazyLoadingEnabled = false;
Configuration.ProxyCreationEnabled = false;
//Database.SetInitializer<DatabaseContextMvc>(null);
}
public void SetTimeout(int minutes)
{
this.Database.CommandTimeout = minutes * 60;
}
public static DatabaseContextMvc Create()
{
return new DatabaseContextMvc();
}
}
В этот момент у вас должны быть все классы, необходимые для его повсеместного использования.
Например, в вашем проекте .NET Framework ваш ApplicationUserManager
может быть таким:
public class ApplicationUserManager : UserManager<ApplicationUserMvc, string>
{
public ApplicationUserManager(IUserStore<ApplicationUserMvc, string> store)
: base(store)
{//look at where i used applicationUserMvc
}
public static ApplicationUserManager Create(IdentityFactoryOptions<ApplicationUserManager> options, IOwinContext context)
{
var userStore =
new UserStore<ApplicationUserMvc, ApplicationRoleMvc, string, ApplicationUserLogin, ApplicationUserRole, ApplicationUserClaim>(context.Get<DatabaseContextMvc>());
//ApplicationUserLogin,UserRole,UserClaim are self created but just override IdentityUserLogin (for example).
var manager = new ApplicationUserManager(userStore);
}
...
}
В любой инъекции зависимостей, которую вы используете в .NET Framework, обязательно зарегистрируйте ваши DatabaseContext
и DatabaseContextMvc
.
Вот мой DatabaseContext
, который находится внутри библиотеки .NET Standard и используется в .NET Core и .NET Framework:
public class DatabaseContext : IdentityDbContext<ApplicationUser, ApplicationRole, string, ApplicationUserClaim, ApplicationUserRole, ApplicationUserLogin, ApplicationRoleClaim, ApplicationUserToken>
{
private IConfiguration _config;
public string ConnectionString { get; }
public DatabaseContext(IConfiguration config) : base()
{
_config = config;
var connectionString = config.GetConnectionString("DatabaseContext");
ConnectionString = connectionString;
}
public void SetTimeout(int minutes)
{
Database.SetCommandTimeout(minutes * 60);
}
public virtual DbSet<Address> Addresses { get; set; }
public static DatabaseContext Create(IConfiguration config)
{
return new DatabaseContext(config);
}
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
//setup code for the DbContextOptions
optionsBuilder
.UseSqlServer(ConnectionString,
providerOptions => providerOptions.CommandTimeout(60))
.UseQueryTrackingBehavior(QueryTrackingBehavior.NoTracking);
base.OnConfiguring(optionsBuilder);
}
ваш фокус, вероятно, уже ушел после этого длинного поста или после реализации, но еще несколько слов:
- Я могу гарантировать, что с этим вы заставите его работать, он прекрасно работает в течение последнего полугода.
- Так что .NET Standard (и, следовательно, EntityFrameworkCore) является основным способом выполнения операций над базой данных, большая часть кода справляется с .NET Framework.
- Вы будете бороться с установкой правильных зависимостей. Это вызовет исключения во время выполнения, с которыми вы столкнетесь очень быстро, но их легко разрешить, .NET Framework просто нужно установить зависимости для себя. Убедитесь, что версии выровнены, используйте версию Consolidate в разделе «Управление пакетами NuGet для решения» (щелкните правой кнопкой мыши решение).
Вам нужно будет сделать это по-netcore-способу для настроек: так что вам нужен appsettings.json и в вашем .NET Framework проекте. Также вам все еще нужно это в Web.Config, это в основном для небольшой части аутентификации.
private IConfiguration GetConfiguartion()
{
var path = Server.MapPath("~/");
var builder = new ConfigurationBuilder()
.SetBasePath(path)
.AddJsonFile("appsettings.json");
return builder.Build();//inject the return IConfiguration in your DI
}
Удачи. Если вы думаете, что это сложно и доставляет много хлопот: правильно, если у вас небольшое приложение, вам лучше конвертировать все в .NET Core / .NET Standard.