EF Core 适用于剃须刀页面但不适用于 MVC
EF Core works with razor pages but not MVC
所以我正在开发我的应用程序,由于某种原因,与 UserManager、RoleManager 和 SigninManager 相关的任何事情都无法在视图和控制器中运行。它告诉我它无法访问数据库。我猜这是一个上下文问题。迁移工作正常,我可以在身份系统之外的数据库中创建对象。如果我使用脚手架使用剃刀页面覆盖 auth/identity 中的页面,一切正常(注册、登录等)。当我尝试检查一个角色就像在我对控制器的一个视图操作中测试它时,它给我以下错误:
Microsoft.EntityFrameworkCore.Database.Connection: Error: An error occurred using the connection to database 'DDDTemplate' on server '.'.
这是一个本地数据库设置,在 SQL 服务器上具有可信连接。
Startup.cs
public class Startup
{
private readonly IWebHostEnvironment _env;
public Startup(IConfiguration configuration, IWebHostEnvironment env)
{
Configuration = configuration;
_env = env;
}
public IConfiguration Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.AddApplication();
services.AddInfrastructure(Configuration);
services.AddDatabaseDeveloperPageExceptionFilter();
services.AddSingleton<ICurrentUserService, CurrentUserService>();
services.AddHttpContextAccessor();
services.AddControllersWithViews()
.AddFluentValidation();
//services.AddRazorPages();
if (_env.IsDevelopment())
{
services.AddSingleton<IConsoleLogger, ConsoleLogger>();
}
else
{
services.AddSingleton<IConsoleLogger, NullConsoleLogger>();
}
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
app.UseMigrationsEndPoint();
}
else
{
app.UseExceptionHandler("/Home/Error");
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
Routes.Addroutes(endpoints);
});
}
}
Routes.cs
public static class Routes
{
public static void Addroutes(IEndpointRouteBuilder endpoints)
{
endpoints.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
//endpoints.MapRazorPages();
}
}
appsettings.json
{
"UseInMemoryDatabase": false,
"ConnectionStrings": {
"DefaultConnection": "Data Source=.;Initial Catalog=DDDTemplate;Integrated Security=True;MultipleActiveResultSets=true;"
},
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Information"
}
},
"AllowedHosts": "*"
}
AccountController.cs
public class AccountController : Controller
{
private readonly RoleManager<ApplicationRole> _roleManager;
private readonly UserManager<ApplicationUser> _userManager;
private readonly SignInManager<ApplicationUser> _signInManager;
private readonly IIdentityService _identityService;
public AccountController(RoleManager<ApplicationRole> RoleManager, UserManager<ApplicationUser> userManager, SignInManager<ApplicationUser> signInManager, IIdentityService identityService)
{
_roleManager = RoleManager;
_userManager = userManager;
_signInManager = signInManager;
_identityService = identityService;
}
public IActionResult Index()
{
return View();
}
public IActionResult Register()
{
return View();
}
public IActionResult Login(string t)
{
return View();
}
[HttpPost]
public void Register(RegisterViewModel userInfo)
{
}
[HttpPost]
public async void Login(LoginViewModel loginInfo)
{
try
{
bool x = await _roleManager.RoleExistsAsync("Admin");
Console.WriteLine("test");
}
catch (Exception exception)
{
}
}
}
Login.cshtml 来自 Views/Account 文件夹
@model DDDTemplate.App.LoginViewModel
@using Microsoft.AspNetCore.Identity
@using DDDTemplate.Data.Identity
@inject SignInManager<ApplicationUser> SignInManager
@inject UserManager<ApplicationUser> UserManager
@inject RoleManager<ApplicationRole> RoleManager
@{
ViewData["Title"] = "Login";
}
<div class="text-center">
<h1 class="display-4">Register Account</h1>
<form asp-controller="Account" asp-action="Login" method="post">
<div asp-validation-summary="ModelOnly"></div>
<div>
<label asp-for="Username"></label>
<input asp-for="Username" />
<span asp-validation-for="Username"></span>
</div>
<div>
<label asp-for="Password"></label>
<input asp-for="Password" />
<span asp-validation-for="Password"></span>
</div>
<div>
<input type="submit" value="Login" />
</div>
</form>
</div>
在服务配置期间调用的引用项目的依赖项注入文件中
public static IServiceCollection AddInfrastructure(this IServiceCollection services, IConfiguration configuration)
{
services.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlServer(
configuration.GetConnectionString("DefaultConnection"),
b =>
b.MigrationsAssembly(typeof(ApplicationDbContext).Assembly.FullName)
)
);
services
.AddIdentity<ApplicationUser,ApplicationRole>()
.AddEntityFrameworkStores<ApplicationDbContext>()
.AddDefaultUI()
.AddDefaultTokenProviders(); ;
services.AddScoped<IApplicationDbContext>(provider => provider.GetService<ApplicationDbContext>());
services.AddScoped<IDomainEventService, DomainEventService>();
services.AddTransient<IDateTime, DateTimeService>();
services.AddTransient<IIdentityService, IdentityService>();
services.AddTransient<ICsvFileBuilder, CsvFileBuilder>();
services.AddAuthorization();
return services;
}
ApplicationDBContext.cs
public class ApplicationDbContext : IdentityDbContext<ApplicationUser, ApplicationRole, int>, IApplicationDbContext
{
private IDomainEventService _domainEventService {get;}
public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options, IDomainEventService domainEventService) : base(options)
{
_domainEventService = domainEventService;
}
protected override void OnModelCreating(ModelBuilder builder)
{
base.OnModelCreating(builder);
builder.ApplyConfigurationsFromAssembly(Assembly.GetAssembly(typeof(ApplicationDbContext)));
}
public DbSet<TodoItem> TodoItems { get; set; }
public DbSet<TodoList> TodoLists { get; set; }
public override async Task<int> SaveChangesAsync(CancellationToken cancellationToken = new CancellationToken())
{
foreach (Microsoft.EntityFrameworkCore.ChangeTracking.EntityEntry<AuditableEntity> entry in ChangeTracker.Entries<AuditableEntity>())
{
switch (entry.State)
{
case EntityState.Added:
entry.Entity.CreatedBy = _currentUserService.;
entry.Entity.Created = _dateTime.Now;
break;
case EntityState.Modified:
entry.Entity.LastModifiedBy = _currentUserService.UserId;
entry.Entity.LastModified = _dateTime.Now;
break;
}
}
var result = await base.SaveChangesAsync(cancellationToken);
await DispatchEvents();
return result;
}
private async Task DispatchEvents()
{
while (true)
{
var domainEventEntity = ChangeTracker.Entries<IHasDomainEvent>()
.Select(x => x.Entity.DomainEvents)
.SelectMany(x => x)
.Where(domainEvent => !domainEvent.IsPublished)
.FirstOrDefault();
if (domainEventEntity == null) break;
domainEventEntity.IsPublished = true;
await _domainEventService.Publish(domainEventEntity);
}
}
}
我发现问题是我从一个有任务的异步函数中 returning void。只有某些情况是可以接受的,这不是其中之一。所以在 AccountController.cs 我将以下内容更改为 return Task 而不是 void 并且一切正常:
[HttpPost]
public async Task Login(LoginViewModel loginInfo)
{
try
{
bool x = await _roleManager.RoleExistsAsync("Admin");
Console.WriteLine("test");
}
catch (Exception exception)
{
}
}
所以我正在开发我的应用程序,由于某种原因,与 UserManager、RoleManager 和 SigninManager 相关的任何事情都无法在视图和控制器中运行。它告诉我它无法访问数据库。我猜这是一个上下文问题。迁移工作正常,我可以在身份系统之外的数据库中创建对象。如果我使用脚手架使用剃刀页面覆盖 auth/identity 中的页面,一切正常(注册、登录等)。当我尝试检查一个角色就像在我对控制器的一个视图操作中测试它时,它给我以下错误:
Microsoft.EntityFrameworkCore.Database.Connection: Error: An error occurred using the connection to database 'DDDTemplate' on server '.'.
这是一个本地数据库设置,在 SQL 服务器上具有可信连接。
Startup.cs
public class Startup
{
private readonly IWebHostEnvironment _env;
public Startup(IConfiguration configuration, IWebHostEnvironment env)
{
Configuration = configuration;
_env = env;
}
public IConfiguration Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.AddApplication();
services.AddInfrastructure(Configuration);
services.AddDatabaseDeveloperPageExceptionFilter();
services.AddSingleton<ICurrentUserService, CurrentUserService>();
services.AddHttpContextAccessor();
services.AddControllersWithViews()
.AddFluentValidation();
//services.AddRazorPages();
if (_env.IsDevelopment())
{
services.AddSingleton<IConsoleLogger, ConsoleLogger>();
}
else
{
services.AddSingleton<IConsoleLogger, NullConsoleLogger>();
}
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
app.UseMigrationsEndPoint();
}
else
{
app.UseExceptionHandler("/Home/Error");
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
Routes.Addroutes(endpoints);
});
}
}
Routes.cs
public static class Routes
{
public static void Addroutes(IEndpointRouteBuilder endpoints)
{
endpoints.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
//endpoints.MapRazorPages();
}
}
appsettings.json
{
"UseInMemoryDatabase": false,
"ConnectionStrings": {
"DefaultConnection": "Data Source=.;Initial Catalog=DDDTemplate;Integrated Security=True;MultipleActiveResultSets=true;"
},
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Information"
}
},
"AllowedHosts": "*"
}
AccountController.cs
public class AccountController : Controller
{
private readonly RoleManager<ApplicationRole> _roleManager;
private readonly UserManager<ApplicationUser> _userManager;
private readonly SignInManager<ApplicationUser> _signInManager;
private readonly IIdentityService _identityService;
public AccountController(RoleManager<ApplicationRole> RoleManager, UserManager<ApplicationUser> userManager, SignInManager<ApplicationUser> signInManager, IIdentityService identityService)
{
_roleManager = RoleManager;
_userManager = userManager;
_signInManager = signInManager;
_identityService = identityService;
}
public IActionResult Index()
{
return View();
}
public IActionResult Register()
{
return View();
}
public IActionResult Login(string t)
{
return View();
}
[HttpPost]
public void Register(RegisterViewModel userInfo)
{
}
[HttpPost]
public async void Login(LoginViewModel loginInfo)
{
try
{
bool x = await _roleManager.RoleExistsAsync("Admin");
Console.WriteLine("test");
}
catch (Exception exception)
{
}
}
}
Login.cshtml 来自 Views/Account 文件夹
@model DDDTemplate.App.LoginViewModel
@using Microsoft.AspNetCore.Identity
@using DDDTemplate.Data.Identity
@inject SignInManager<ApplicationUser> SignInManager
@inject UserManager<ApplicationUser> UserManager
@inject RoleManager<ApplicationRole> RoleManager
@{
ViewData["Title"] = "Login";
}
<div class="text-center">
<h1 class="display-4">Register Account</h1>
<form asp-controller="Account" asp-action="Login" method="post">
<div asp-validation-summary="ModelOnly"></div>
<div>
<label asp-for="Username"></label>
<input asp-for="Username" />
<span asp-validation-for="Username"></span>
</div>
<div>
<label asp-for="Password"></label>
<input asp-for="Password" />
<span asp-validation-for="Password"></span>
</div>
<div>
<input type="submit" value="Login" />
</div>
</form>
</div>
在服务配置期间调用的引用项目的依赖项注入文件中
public static IServiceCollection AddInfrastructure(this IServiceCollection services, IConfiguration configuration)
{
services.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlServer(
configuration.GetConnectionString("DefaultConnection"),
b =>
b.MigrationsAssembly(typeof(ApplicationDbContext).Assembly.FullName)
)
);
services
.AddIdentity<ApplicationUser,ApplicationRole>()
.AddEntityFrameworkStores<ApplicationDbContext>()
.AddDefaultUI()
.AddDefaultTokenProviders(); ;
services.AddScoped<IApplicationDbContext>(provider => provider.GetService<ApplicationDbContext>());
services.AddScoped<IDomainEventService, DomainEventService>();
services.AddTransient<IDateTime, DateTimeService>();
services.AddTransient<IIdentityService, IdentityService>();
services.AddTransient<ICsvFileBuilder, CsvFileBuilder>();
services.AddAuthorization();
return services;
}
ApplicationDBContext.cs
public class ApplicationDbContext : IdentityDbContext<ApplicationUser, ApplicationRole, int>, IApplicationDbContext
{
private IDomainEventService _domainEventService {get;}
public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options, IDomainEventService domainEventService) : base(options)
{
_domainEventService = domainEventService;
}
protected override void OnModelCreating(ModelBuilder builder)
{
base.OnModelCreating(builder);
builder.ApplyConfigurationsFromAssembly(Assembly.GetAssembly(typeof(ApplicationDbContext)));
}
public DbSet<TodoItem> TodoItems { get; set; }
public DbSet<TodoList> TodoLists { get; set; }
public override async Task<int> SaveChangesAsync(CancellationToken cancellationToken = new CancellationToken())
{
foreach (Microsoft.EntityFrameworkCore.ChangeTracking.EntityEntry<AuditableEntity> entry in ChangeTracker.Entries<AuditableEntity>())
{
switch (entry.State)
{
case EntityState.Added:
entry.Entity.CreatedBy = _currentUserService.;
entry.Entity.Created = _dateTime.Now;
break;
case EntityState.Modified:
entry.Entity.LastModifiedBy = _currentUserService.UserId;
entry.Entity.LastModified = _dateTime.Now;
break;
}
}
var result = await base.SaveChangesAsync(cancellationToken);
await DispatchEvents();
return result;
}
private async Task DispatchEvents()
{
while (true)
{
var domainEventEntity = ChangeTracker.Entries<IHasDomainEvent>()
.Select(x => x.Entity.DomainEvents)
.SelectMany(x => x)
.Where(domainEvent => !domainEvent.IsPublished)
.FirstOrDefault();
if (domainEventEntity == null) break;
domainEventEntity.IsPublished = true;
await _domainEventService.Publish(domainEventEntity);
}
}
}
我发现问题是我从一个有任务的异步函数中 returning void。只有某些情况是可以接受的,这不是其中之一。所以在 AccountController.cs 我将以下内容更改为 return Task 而不是 void 并且一切正常:
[HttpPost]
public async Task Login(LoginViewModel loginInfo)
{
try
{
bool x = await _roleManager.RoleExistsAsync("Admin");
Console.WriteLine("test");
}
catch (Exception exception)
{
}
}