我正在使用用户身份验证创建一个Web应用程序。
我使用Microsoft.AspNetCore.Identity.EntityFrameworkCore
UserManager<IdentityUser>
和RoleManager<IdentityRole>
来管理用户及其角色。
当我在连接字符串中使用带有MultipleActiveResultSets=true
的Visual Studio中的localDB功能时,一切正常。
当我想使用安装在我的Ubuntu 16.04服务器上的MySQL服务器时,我可以登录。但是,只要RoleManager
首次尝试连接到数据库,就会出现以下错误:
MySqlException:已经有一个打开的DataReader与之关联 这个必须先关闭的连接
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Identity;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authorization;
using tsv_core.Models;
// For more information on enabling MVC for empty projects, visit http://go.microsoft.com/fwlink/?LinkID=397860
namespace tsv_core.Controllers
{
[Authorize(Roles = "Admins")]
public class AdminController : Controller
{
UserManager<AppUser> userManager;
private IUserValidator<AppUser> userValidator;
private IPasswordValidator<AppUser> passwordValidator;
private IPasswordHasher<AppUser> passwordHasher;
private IRequestLogger _logger;
public AdminController(UserManager<AppUser> usrMgr, IUserValidator<AppUser> userValid, IPasswordValidator<AppUser> passValid, IPasswordHasher<AppUser> passwordHash, IRequestLogger logger)
{
userManager = usrMgr;
userValidator = userValid;
passwordValidator = passValid;
passwordHasher = passwordHash;
_logger = logger;
}
public ViewResult Accounts() => View(userManager.Users);
public ViewResult Create() => View();
[HttpPost]
public async Task<IActionResult> Create(CreateModel model)
{
if (ModelState.IsValid)
{
AppUser user = new AppUser
{
UserName = model.Name,
Email = model.Email
};
IdentityResult result
= await userManager.CreateAsync(user, model.Password);
if (result.Succeeded)
{
return RedirectToAction("Accounts");
}
else
{
foreach (IdentityError error in result.Errors)
{
ModelState.AddModelError("", error.Description);
}
}
}
return View(model);
}
[HttpPost]
public async Task<IActionResult> Delete(string id)
{
AppUser user = await userManager.FindByIdAsync(id);
if (user != null)
{
IdentityResult result = await userManager.DeleteAsync(user);
if (result.Succeeded)
{
return RedirectToAction("Index");
}
else
{
AddErrorsFromResult(result);
}
}
else
{
ModelState.AddModelError("", "User Not Found");
}
return View("Index", userManager.Users);
}
public async Task<IActionResult> Edit(string id)
{
AppUser user = await userManager.FindByIdAsync(id);
if (user != null)
{
return View(user);
}
else
{
return RedirectToAction("Index");
}
}
[HttpPost]
public async Task<IActionResult> Edit(string id, string email,
string password)
{
AppUser user = await userManager.FindByIdAsync(id);
if (user != null)
{
user.Email = email;
IdentityResult validEmail
= await userValidator.ValidateAsync(userManager,
user);
if (!validEmail.Succeeded)
{
AddErrorsFromResult(validEmail);
}
IdentityResult validPass = null;
if (!string.IsNullOrEmpty(password))
{
validPass = await passwordValidator.ValidateAsync(userManager,
user, password);
if (validPass.Succeeded)
{
user.PasswordHash = passwordHasher.HashPassword(user,
password);
}
else
{
AddErrorsFromResult(validPass);
}
}
if ((validEmail.Succeeded && validPass == null)
|| (validEmail.Succeeded
&& password != string.Empty && validPass.Succeeded))
{
IdentityResult result = await userManager.UpdateAsync(user);
if (result.Succeeded)
{
return RedirectToAction("Index");
}
else
{
AddErrorsFromResult(result);
}
}
}
else
{
ModelState.AddModelError("", "User Not Found");
}
return View(user);
}
private void AddErrorsFromResult(IdentityResult result)
{
foreach (IdentityError error in result.Errors)
{
ModelState.AddModelError("", error.Description);
}
}
public ViewResult RequestLogging()
{
return View(_logger.GetRequestRepository());
}
}
}
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using tsv_core.Models;
using Microsoft.AspNetCore.Identity;
namespace tsv_core.Controllers
{
[Authorize(Roles = "Admins")]
public class AccountController : Controller
{
private UserManager<AppUser> userManager;
private SignInManager<AppUser> signInManager;
public AccountController(UserManager<AppUser> userMgr,
SignInManager<AppUser> signinMgr)
{
userManager = userMgr;
signInManager = signinMgr;
}
[AllowAnonymous]
public IActionResult Login(string returnUrl)
{
ViewBag.returnUrl = returnUrl;
return View();
}
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Login(LoginModel details,
string returnUrl)
{
if (ModelState.IsValid)
{
AppUser user = await userManager.FindByEmailAsync(details.Email);
if (user != null)
{
await signInManager.SignOutAsync();
Microsoft.AspNetCore.Identity.SignInResult result =
await signInManager.PasswordSignInAsync(
user, details.Password, false, false);
if (result.Succeeded)
{
return Redirect(returnUrl ?? "/");
}
}
ModelState.AddModelError(nameof(LoginModel.Email), "Invalid user or password");
}
return View(details);
}
public async Task<IActionResult> Logout()
{
await signInManager.SignOutAsync();
return RedirectToAction("Index", "Home");
}
[AllowAnonymous]
public IActionResult AccessDenied()
{
return View();
}
}
}
using System.ComponentModel.DataAnnotations;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
using Microsoft.AspNetCore.Mvc;
using System.Linq;
using tsv_core.Models;
using System.Collections.Generic;
using Microsoft.AspNetCore.Authorization;
namespace tsv_core.Controllers
{
[Authorize(Roles = "Admins")]
public class RoleAdminController : Controller
{
private RoleManager<IdentityRole> roleManager;
private UserManager<AppUser> userManager;
public RoleAdminController(RoleManager<IdentityRole> roleMgr, UserManager<AppUser> userMrg)
{
roleManager = roleMgr;
userManager = userMrg;
}
public ViewResult Index() => View(roleManager.Roles);
public IActionResult Create() => View();
[HttpPost]
public async Task<IActionResult> Create([Required]string name)
{
if (ModelState.IsValid)
{
IdentityResult result
= await roleManager.CreateAsync(new IdentityRole(name));
if (result.Succeeded)
{
return RedirectToAction("Index");
}
else
{
AddErrorsFromResult(result);
}
}
return View(name);
}
[HttpPost]
public async Task<IActionResult> Delete(string id)
{
IdentityRole role = await roleManager.FindByIdAsync(id);
if (role != null)
{
IdentityResult result = await roleManager.DeleteAsync(role);
if (result.Succeeded)
{
return RedirectToAction("Index");
}
else
{
AddErrorsFromResult(result);
}
}
else
{
ModelState.AddModelError("", "No role found");
}
return View("Index", roleManager.Roles);
}
public async Task<IActionResult> Edit(string id)
{
IdentityRole role = await roleManager.FindByIdAsync(id);
List<AppUser> members = new List<AppUser>();
List<AppUser> nonMembers = new List<AppUser>();
foreach (AppUser user in userManager.Users)
{
var list = await userManager.IsInRoleAsync(user, role.Name)
? members : nonMembers;
list.Add(user);
}
return View(new RoleEditModel
{
Role = role,
Members = members,
NonMembers = nonMembers
});
}
[HttpPost]
public async Task<IActionResult> Edit(RoleModificationModel model)
{
IdentityResult result;
if (ModelState.IsValid)
{
foreach (string userId in model.IdsToAdd ?? new string[] { })
{
AppUser user = await userManager.FindByIdAsync(userId);
if (user != null)
{
result = await userManager.AddToRoleAsync(user,
model.RoleName);
if (!result.Succeeded)
{
AddErrorsFromResult(result);
}
}
}
foreach (string userId in model.IdsToDelete ?? new string[] { })
{
AppUser user = await userManager.FindByIdAsync(userId);
if (user != null)
{
result = await userManager.RemoveFromRoleAsync(user,
model.RoleName);
if (!result.Succeeded)
{
AddErrorsFromResult(result);
}
}
}
}
if (ModelState.IsValid)
{
return RedirectToAction(nameof(Index));
}
else
{
return await Edit(model.RoleId);
}
}
private void AddErrorsFromResult(IdentityResult result)
{
foreach (IdentityError error in result.Errors)
{
ModelState.AddModelError("", error.Description);
}
}
}
}
using System.Collections.Generic;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
using Microsoft.AspNetCore.Razor.TagHelpers;
using tsv_core.Models;
namespace tsv_core.Infrastructure
{
[HtmlTargetElement("td", Attributes = "identity-role")]
public class RoleUsersTagHelper : TagHelper
{
private UserManager<AppUser> userManager;
private RoleManager<IdentityRole> roleManager;
public RoleUsersTagHelper(UserManager<AppUser> usermgr,
RoleManager<IdentityRole> rolemgr)
{
userManager = usermgr;
roleManager = rolemgr;
}
[HtmlAttributeName("identity-role")]
public string Role { get; set; }
public override async Task ProcessAsync(TagHelperContext context,
TagHelperOutput output)
{
IdentityRole role = await roleManager.FindByIdAsync(Role);;
List<string> names = new List<string>();
if (role != null)
{
foreach (var user in userManager.Users)
{
if (user != null && await userManager.IsInRoleAsync(user, role.Name))
{
names.Add(user.UserName);
}
}
}
output.Content.SetContent(names.Count == 0 ?
"No Users" : string.Join(", ", names));
}
}
}
@model IEnumerable<IdentityRole>
<div class="bg-primary panel-body"><h4>Roles</h4></div>
<div class="text-danger" asp-validation-summary="ModelOnly"></div>
<table class="table table-condensed table-bordered table-bordered">
<tr><th>ID</th><th>Name</th><th>Users</th><th></th></tr>
@if (Model.Count() == 0)
{
<tr><td colspan="4" class="text-center">No Roles</td></tr>
}
else
{
foreach (var role in Model)
{
<tr>
<td>@role.Id</td>
<td>@role.Name</td>
<td identity-role="@role.Id"></td>
<td>
<form asp-action="Delete" asp-route-id="@role.Id" method="post">
<a class="btn btn-sm btn-primary" asp-action="Edit"
asp-route-id="@role.Id">Edit</a>
<button type="submit"
class="btn btn-sm btn-danger">
Delete
</button>
</form>
</td>
</tr>
}
}
</table>
<a class="btn btn-primary" asp-action="Create">Create</a>
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.EntityFrameworkCore;
using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
using tsv_core.Models;
using tsv_core.Infrastructure;
using MySQL.Data.EntityFrameworkCore.Extensions;
namespace tsv_core
{
public class Startup
{
IConfigurationRoot Configuration;
public Startup(IHostingEnvironment env)
{
Configuration = new ConfigurationBuilder()
.SetBasePath(env.ContentRootPath)
.AddJsonFile("appsettings.json").Build();
}
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
//services.AddDbContext<AppIdentityDbContext>(options => options.UseSqlServer(Configuration["Data:tsvIdentity:ConnectionString"]));
services.AddDbContext<AppIdentityDbContext>(options => options.UseMySQL(Configuration["Data:tsvIdentity:ConnectionString"]));
services.AddIdentity<AppUser, IdentityRole>(opts => opts.User.RequireUniqueEmail = true).AddEntityFrameworkStores<AppIdentityDbContext>();
services.AddTransient<IRequestLogger, TestRequestLogger>();
services.AddMvc();
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
app.UseBrowserLink();
loggerFactory.AddConsole(Configuration.GetSection("Logging"));
loggerFactory.AddDebug();
}
else
{
app.UseExceptionHandler("/Home/Error");
}
app.UseStaticFiles();
app.UseIdentity();
//This position of the LoggingMiddleware is important!! If it would be placed before "app.UseStaticFiles();" the request paths to the static files would be logged too.
//If it would be placed behind app.UseMvc, it wouldn't log anything at all.
app.UseMiddleware<LoggingMiddleware>();
app.UseMvcWithDefaultRoute();
AppIdentityDbContext.CreateAdminAccount(app.ApplicationServices, Configuration).Wait();
}
}
}
这些应该是所有相关的类。
AppUser
只是继承自IdentityUser
,目前没有任何额外字段。
第一次出现错误时听到:
我已经搜索了答案,并遇到了连接池等主题。
我的问题是,当谈到合并时,我不确切知道EF Core已经为我做了什么。
我是否需要在每次使用后使用“using-blocks”处理UserManager和RoleManager,或者是否有完全不同的解决方案?