无法使用 ASP.NET MVC HTML5 样板在存储库设置中正确使用 AutoFac
Unable to employ AutoFac correctly in a repository setting using the ASP.NET MVC HTML5 Boilerplate
当前项目:
- ASP.NET 4.6.2
- MVC 5
- 存储库模式来自 Persistence-Ignorant ASP.NET Identity
- ASP.NET MVC HTML5 样板
我已尽最大努力遵循上面链接的文章中的存储库模式,因为我有 a distinct need for a repository pattern,并且希望将用户 ID 视为 GUID 而不仅仅是 nvarchar(128 ) 字符串。
本质上,我需要在多个站点之间共享 table 结构,因此我使用了一种存储库模式,该模式还允许我在这些多个站点之间共享登录信息。各个站点使用 ASP.NET MVC HTML5 样板模板,因此 AutoFac 被嵌入其中。但是也请注意,一般来说,我在依赖注入方面存在相当大的概念困难,类似于尝试闻蓝色的气味,或尝试品尝温度的味道。我只是不了解它。
我还设法扩展了该存储库模式以允许我启用迁移。如:我能够将数据库模型迁移到数据库。不幸的是,因为我正在处理基础设施层(我称我的存储库模式层为数据库模型的核心和其他一切的基础设施),我无法将主要管理员用户注入数据库,因为没有 Entity Framework 也没有App_Start 在基础设施层。因此,我无法在 Infrastructure:
中使用 ApplicationDbInitializer
namespace Company.Infrastructure.Context {
using Identity;
using Microsoft.AspNet.Identity;
using System;
using Store;
using System.Data.Entity;
public class ApplicationDbInitializer : DropCreateDatabaseAlways<ApplicationDbContext> {
private readonly UserManager<IdentityUser, Guid> _userManager;
private readonly RoleManager<IdentityRole, Guid> _roleManager;
public ApplicationDbInitializer() { }
public ApplicationDbInitializer(UserManager<IdentityUser, Guid> userManager, RoleManager<IdentityRole, Guid> roleManager) {
_userManager = userManager;
_roleManager = roleManager;
}
// This example shows you how to create a new database if the Model changes
//public class ApplicationDbInitializer : DropCreateDatabaseIfModelChanges<ApplicationDbContext>
protected override void Seed(ApplicationDbContext context) {
InitializeIdentityForEf(context);
base.Seed(context);
}
//Create User=Admin@Admin.com with password=Admin@123456 in the Admin role
public void InitializeIdentityForEf(ApplicationDbContext db) {
const string username = "CompanyName";
const string email = "info@companyname.com";
const string password = "password";
const string roleName = "Administrator";
//Create Role Admin if it does not exist
var role = _roleManager.FindByName(roleName);
if(role == null) {
role = new IdentityRole(roleName);
var roleresult = _roleManager.Create(role);
}
var user = _userManager.FindByName(username);
if(user == null) {
user = new IdentityUser { UserName = username, Email = email };
var result = _userManager.Create(user, password);
result = _userManager.SetLockoutEnabled(user.Id, false);
}
// Add user admin to Role Admin if not already added
var rolesForUser = _userManager.GetRoles(user.Id);
if(!rolesForUser.Contains(role.Name)) {
var result = _userManager.AddToRole(user.Id, role.Name);
}
}
}
}
注入主管理员。
因此,我试图通过其中一个网站注入主要管理员用户,但是当我尝试注册修改后的 UserManager
和 UserStore
时,AutoFac 似乎无法接收这些注册,即使它可以很好地接收模板附带的所有其他注册。
AutoFac 是通过 App_Start\Startup.Container.cs
:
注册的
namespace Company.Website {
using System.Reflection;
using System.Web.Mvc;
using Autofac;
using Autofac.Integration.Mvc;
using Services;
using Owin;
using System;
using Microsoft.AspNet.Identity;
using Infrastructure.Identity;
using Infrastructure.Store;
using Core.Repositories;
using Infrastructure.Context;
/// <summary>
/// Register types into the Autofac Inversion of Control (IOC) container. Autofac makes it easy to register common
/// MVC types like the <see cref="UrlHelper"/> using the <see cref="AutofacWebTypesModule"/>. Feel free to change
/// this to another IoC container of your choice but ensure that common MVC types like <see cref="UrlHelper"/> are
/// registered. See http://autofac.readthedocs.org/en/latest/integration/aspnet.html.
/// </summary>
public partial class Startup {
public static void ConfigureContainer(IAppBuilder app) {
var container = CreateContainer();
app.UseAutofacMiddleware(container);
// Register MVC Types
app.UseAutofacMvc();
}
private static IContainer CreateContainer() {
var builder = new ContainerBuilder();
var assembly = Assembly.GetExecutingAssembly();
RegisterServices(builder);
RegisterMvc(builder, assembly);
var container = builder.Build();
SetMvcDependencyResolver(container);
return container;
}
private static void RegisterServices(ContainerBuilder builder) {
builder.RegisterType<BrowserConfigService>().As<IBrowserConfigService>().InstancePerRequest();
builder.RegisterType<CacheService>().As<ICacheService>().SingleInstance();
builder.RegisterType<FeedService>().As<IFeedService>().InstancePerRequest();
builder.RegisterType<LoggingService>().As<ILoggingService>().SingleInstance();
builder.RegisterType<ManifestService>().As<IManifestService>().InstancePerRequest();
builder.RegisterType<OpenSearchService>().As<IOpenSearchService>().InstancePerRequest();
builder.RegisterType<RobotsService>().As<IRobotsService>().InstancePerRequest();
builder.RegisterType<SitemapService>().As<ISitemapService>().InstancePerRequest();
builder.RegisterType<SitemapPingerService>().As<ISitemapPingerService>().InstancePerRequest();
// My additions:
builder.RegisterType<UnitOfWork>().As<IUnitOfWork>().InstancePerRequest();
builder.RegisterType<UserManager<IdentityUser, Guid>>().As<UserManager<IdentityUser, Guid>>().InstancePerRequest();
builder.RegisterType<UserStore>().As<IUserStore<IdentityUser, Guid>>().InstancePerLifetimeScope();
}
private static void RegisterMvc(ContainerBuilder builder, Assembly assembly) {
// Register Common MVC Types
builder.RegisterModule<AutofacWebTypesModule>();
// Register MVC Filters
builder.RegisterFilterProvider();
// Register MVC Controllers
builder.RegisterControllers(assembly);
}
/// <summary>
/// Sets the ASP.NET MVC dependency resolver.
/// </summary>
/// <param name="container">The container.</param>
private static void SetMvcDependencyResolver(IContainer container) {
DependencyResolver.SetResolver(new AutofacDependencyResolver(container));
}
}
}
我的家庭控制器的开头是这样的:
namespace Company.Website.Controllers {
using Boilerplate.Web.Mvc;
using Boilerplate.Web.Mvc.Filters;
using Constants;
using Identity;
using Infrastructure.Models;
using Infrastructure.Context;
using Infrastructure.Store;
using Microsoft.AspNet.Identity;
using Microsoft.Owin.Security;
using Recaptcha.Web;
using Recaptcha.Web.Mvc;
using Services;
using System;
using System.Diagnostics;
using System.Linq;
using System.Net;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Web.Mvc;
public class HomeController : Controller {
private readonly IBrowserConfigService _browserConfigService;
private readonly IFeedService _feedService;
private readonly IManifestService _manifestService;
private readonly IOpenSearchService _openSearchService;
private readonly IRobotsService _robotsService;
private readonly ISitemapService _sitemapService;
private ApplicationDbContext _db;
private readonly UserManager<IdentityUser, Guid> _userManager;
public HomeController(
IBrowserConfigService browserConfigService,
IFeedService feedService,
IManifestService manifestService,
IOpenSearchService openSearchService,
IRobotsService robotsService,
ISitemapService sitemapService,
UserManager<IdentityUser, Guid> userManager) {
_browserConfigService = browserConfigService;
_feedService = feedService;
_manifestService = manifestService;
_openSearchService = openSearchService;
_robotsService = robotsService;
_sitemapService = sitemapService;
_userManager = userManager;
}
重点是我打算使用 Index 方法的初始 运行 将管理员用户注入到数据库中:
var user = _userManager.FindByName(username);
if(user == null) {
user = new IdentityUser { UserName = username, Email = email };
var result = _userManager.Create(user, password);
result = _userManager.SetLockoutEnabled(user.Id, false);
}
这可以从任何页面完成,我只是选择了索引。一旦注入了管理员用户,代码将被停用并删除,因为……好吧,到那时工作就完成了。已创建管理员用户。
问题是,由于默认构造函数中存在 UserManager 条目,整个系统崩溃了。全部归锅:
Autofac.Core.DependencyResolutionException: None of the constructors found with 'Autofac.Core.Activators.Reflection.DefaultConstructorFinder' on type 'Company.Website.Controllers.HomeController' can be invoked with the available services and parameters:
Cannot resolve parameter 'Microsoft.AspNet.Identity.UserManager2[Company.Website.Identity.IdentityUser,System.Guid] userManager' of constructor 'Void .ctor(Company.Website.Services.IBrowserConfigService, Company.Website.Services.IFeedService, Company.Website.Services.IManifestService, Company.Website.Services.IOpenSearchService, Company.Website.Services.IRobotsService, Company.Website.Services.ISitemapService, Microsoft.AspNet.Identity.UserManager
2[Company.Website.Identity.IdentityUser,System.Guid])'.
从默认构造函数中删除该条目,一切正常(当然,Index 中的用户创建代码被注释掉了)。把它放回去,DotNet 惊慌失措。
一段时间以来,我一直在为这个问题苦苦思索,现在已经束手无策了。任何帮助将不胜感激。我有 ,但他们没有为我提供解决方案。
编辑 1: UserStore.cs
的请求,在 Company.Infrastructure.Store
:
下找到
namespace Company.Infrastructure.Store {
using Identity;
using Core.Repositories;
using Core.Models;
using Microsoft.AspNet.Identity;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Claims;
using System.Threading.Tasks;
using Context;
public class UserStore : IUserStore<IdentityUser, Guid>, IUserLoginStore<IdentityUser, Guid>, IUserClaimStore<IdentityUser, Guid>, IUserRoleStore<IdentityUser, Guid>, IUserPasswordStore<IdentityUser, Guid>, IUserSecurityStampStore<IdentityUser, Guid>, IDisposable {
private readonly IUnitOfWork _unitOfWork;
public UserStore(IUnitOfWork unitOfWork) {
_unitOfWork = unitOfWork;
}
#region IUserStore<IdentityUser, Guid> Members
public Task CreateAsync(IdentityUser user) {
if(user == null) throw new ArgumentNullException(nameof(user));
var u = getUser(user);
_unitOfWork.UserRepository.Add(u);
return _unitOfWork.SaveChangesAsync();
}
public Task DeleteAsync(IdentityUser user) {
if(user == null) throw new ArgumentNullException(nameof(user));
var u = getUser(user);
_unitOfWork.UserRepository.Remove(u);
return _unitOfWork.SaveChangesAsync();
}
public Task<IdentityUser> FindByIdAsync(Guid userId) {
var user = _unitOfWork.UserRepository.FindById(userId);
return Task.FromResult(getIdentityUser(user));
}
public Task<IdentityUser> FindByNameAsync(string userName) {
var user = _unitOfWork.UserRepository.FindByUserName(userName);
return Task.FromResult(getIdentityUser(user));
}
public Task UpdateAsync(IdentityUser user) {
if(user == null) throw new ArgumentException("user");
var u = _unitOfWork.UserRepository.FindById(user.Id);
if(u == null) throw new ArgumentException("IdentityUser does not correspond to a User entity.", nameof(user));
PopulateUser(u, user);
_unitOfWork.UserRepository.Update(u);
return _unitOfWork.SaveChangesAsync();
}
#endregion
#region IDisposable Members
public void Dispose() {
// Dispose does nothing since we want Unity to manage the lifecycle of our Unit of Work
}
#endregion
#region IUserClaimStore<IdentityUser, Guid> Members
public Task AddClaimAsync(IdentityUser user, Claim claim) {
if(user == null) throw new ArgumentNullException(nameof(user));
if(claim == null) throw new ArgumentNullException(nameof(claim));
var u = _unitOfWork.UserRepository.FindById(user.Id);
if(u == null) throw new ArgumentException("IdentityUser does not correspond to a User entity.", nameof(user));
var c = new UserClaim {
ClaimType = claim.Type,
ClaimValue = claim.Value,
User = u
};
u.UserClaim.Add(c);
_unitOfWork.UserRepository.Update(u);
return _unitOfWork.SaveChangesAsync();
}
public Task<IList<Claim>> GetClaimsAsync(IdentityUser user) {
if(user == null) throw new ArgumentNullException(nameof(user));
var u = _unitOfWork.UserRepository.FindById(user.Id);
if(u == null) throw new ArgumentException("IdentityUser does not correspond to a User entity.", nameof(user));
return Task.FromResult<IList<Claim>>(u.UserClaim.Select(x => new Claim(x.ClaimType, x.ClaimValue)).ToList());
}
public Task RemoveClaimAsync(IdentityUser user, Claim claim) {
if(user == null) throw new ArgumentNullException(nameof(user));
if(claim == null) throw new ArgumentNullException(nameof(claim));
var u = _unitOfWork.UserRepository.FindById(user.Id);
if(u == null) throw new ArgumentException("IdentityUser does not correspond to a User entity.", nameof(user));
var c = u.UserClaim.FirstOrDefault(x => x.ClaimType == claim.Type && x.ClaimValue == claim.Value);
u.UserClaim.Remove(c);
_unitOfWork.UserRepository.Update(u);
return _unitOfWork.SaveChangesAsync();
}
#endregion
#region IUserLoginStore<IdentityUser, Guid> Members
public Task AddLoginAsync(IdentityUser user, UserLoginInfo login) {
if(user == null) throw new ArgumentNullException(nameof(user));
if(login == null) throw new ArgumentNullException(nameof(login));
var u = _unitOfWork.UserRepository.FindById(user.Id);
if(u == null) throw new ArgumentException("IdentityUser does not correspond to a User entity.", nameof(user));
var l = new UserLogin {
LoginProvider = login.LoginProvider,
ProviderKey = login.ProviderKey,
User = u
};
u.UserLogin.Add(l);
_unitOfWork.UserRepository.Update(u);
return _unitOfWork.SaveChangesAsync();
}
public Task<IdentityUser> FindAsync(UserLoginInfo login) {
if(login == null) throw new ArgumentNullException(nameof(login));
var identityUser = default(IdentityUser);
var l = _unitOfWork.LoginRepository.GetByProviderAndKey(login.LoginProvider, login.ProviderKey);
if(l != null) identityUser = getIdentityUser(l.User);
return Task.FromResult(identityUser);
}
public Task<IList<UserLoginInfo>> GetLoginsAsync(IdentityUser user) {
if(user == null) throw new ArgumentNullException(nameof(user));
var u = _unitOfWork.UserRepository.FindById(user.Id);
if(u == null) throw new ArgumentException("IdentityUser does not correspond to a User entity.", nameof(user));
return Task.FromResult<IList<UserLoginInfo>>(u.UserLogin.Select(x => new UserLoginInfo(x.LoginProvider, x.ProviderKey)).ToList());
}
public Task RemoveLoginAsync(IdentityUser user, UserLoginInfo login) {
if(user == null) throw new ArgumentNullException(nameof(user));
if(login == null) throw new ArgumentNullException(nameof(login));
var u = _unitOfWork.UserRepository.FindById(user.Id);
if(u == null) throw new ArgumentException("IdentityUser does not correspond to a User entity.", nameof(user));
var l = u.UserLogin.FirstOrDefault(x => x.LoginProvider == login.LoginProvider && x.ProviderKey == login.ProviderKey);
u.UserLogin.Remove(l);
_unitOfWork.UserRepository.Update(u);
return _unitOfWork.SaveChangesAsync();
}
#endregion
#region IUserRoleStore<IdentityUser, Guid> Members
public Task AddToRoleAsync(IdentityUser user, string roleName) {
if(user == null) throw new ArgumentNullException(nameof(user));
if(string.IsNullOrWhiteSpace(roleName)) throw new ArgumentException("Argument cannot be null, empty, or whitespace: roleName.");
var u = _unitOfWork.UserRepository.FindById(user.Id);
if(u == null) throw new ArgumentException("IdentityUser does not correspond to a User entity.", nameof(user));
var r = _unitOfWork.RoleRepository.FindByName(roleName);
if(r == null) throw new ArgumentException("roleName does not correspond to a Role entity.", nameof(roleName));
u.Role.Add(r);
_unitOfWork.UserRepository.Update(u);
return _unitOfWork.SaveChangesAsync();
}
public Task<IList<string>> GetRolesAsync(IdentityUser user) {
if(user == null) throw new ArgumentNullException(nameof(user));
var u = _unitOfWork.UserRepository.FindById(user.Id);
if(u == null) throw new ArgumentException("IdentityUser does not correspond to a User entity.", nameof(user));
return Task.FromResult<IList<string>>(u.Role.Select(x => x.Name).ToList());
}
public Task<bool> IsInRoleAsync(IdentityUser user, string roleName) {
if(user == null) throw new ArgumentNullException(nameof(user));
if(string.IsNullOrWhiteSpace(roleName)) throw new ArgumentException("Argument cannot be null, empty, or whitespace: role.");
var u = _unitOfWork.UserRepository.FindById(user.Id);
if(u == null) throw new ArgumentException("IdentityUser does not correspond to a User entity.", nameof(user));
return Task.FromResult(u.Role.Any(x => x.Name == roleName));
}
public Task RemoveFromRoleAsync(IdentityUser user, string roleName) {
if(user == null) throw new ArgumentNullException(nameof(user));
if(string.IsNullOrWhiteSpace(roleName)) throw new ArgumentException("Argument cannot be null, empty, or whitespace: role.");
var u = _unitOfWork.UserRepository.FindById(user.Id);
if(u == null) throw new ArgumentException("IdentityUser does not correspond to a User entity.", nameof(user));
var r = u.Role.FirstOrDefault(x => x.Name == roleName);
u.Role.Remove(r);
_unitOfWork.UserRepository.Update(u);
return _unitOfWork.SaveChangesAsync();
}
#endregion
#region IUserPasswordStore<IdentityUser, Guid> Members
public Task<string> GetPasswordHashAsync(IdentityUser user) {
if(user == null) throw new ArgumentNullException(nameof(user));
return Task.FromResult(user.PasswordHash);
}
public Task<bool> HasPasswordAsync(IdentityUser user) {
if(user == null) throw new ArgumentNullException(nameof(user));
return Task.FromResult(!string.IsNullOrWhiteSpace(user.PasswordHash));
}
public Task SetPasswordHashAsync(IdentityUser user, string passwordHash) {
user.PasswordHash = passwordHash;
return Task.FromResult(0);
}
#endregion
#region IUserSecurityStampStore<IdentityUser, Guid> Members
public Task<string> GetSecurityStampAsync(IdentityUser user) {
if(user == null) throw new ArgumentNullException(nameof(user));
return Task.FromResult(user.SecurityStamp);
}
public Task SetSecurityStampAsync(IdentityUser user, string stamp) {
user.SecurityStamp = stamp;
return Task.FromResult(0);
}
#endregion
#region Private Methods
private User getUser(IdentityUser identityUser) {
if(identityUser == null) return null;
var user = new User();
PopulateUser(user, identityUser);
return user;
}
private static void PopulateUser(User user, IdentityUser identityUser) {
user.UserId = identityUser.Id;
user.UserName = identityUser.UserName;
user.PasswordHash = identityUser.PasswordHash;
user.SecurityStamp = identityUser.SecurityStamp;
}
private IdentityUser getIdentityUser(User user) {
if(user == null) return null;
var identityUser = new IdentityUser();
PopulateIdentityUser(identityUser, user);
return identityUser;
}
private static void PopulateIdentityUser(IdentityUser identityUser, User user) {
identityUser.Id = user.UserId;
identityUser.UserName = user.UserName;
identityUser.PasswordHash = user.PasswordHash;
identityUser.SecurityStamp = user.SecurityStamp;
}
#endregion
}
}
感谢 tdragon,我能够 bash 将足够多的随机岩石组合在一起 assemble 我自己进行了函数依赖注入。
因为我使用了 ASP.NET MVC HTML5 样板,实际的依赖设置可能与默认的 AutoFac 设置不同,但关键是如果你使用相同的 repository pattern tutorial 和我一样,您需要在 StartupContainer.cs
中创建以下内容:
builder.RegisterType<UnitOfWork>().As<IUnitOfWork>().InstancePerRequest();
builder.RegisterType<UserManager<IdentityUser, Guid>>().InstancePerRequest();
builder.RegisterType<UserStore>().As<IUserStore<IdentityUser, Guid>>().InstancePerLifetimeScope();
builder.RegisterType<RoleManager<IdentityRole, Guid>>().InstancePerRequest();
builder.RegisterType<RoleStore>().As<IRoleStore<IdentityRole, Guid>>().InstancePerLifetimeScope();
从那里开始,无论您希望在哪个控制器中操纵用户,都需要添加以下内容:
private readonly IUnitOfWork _unitOfWork;
private readonly UserManager<IdentityUser, Guid> _userManager;
private readonly RoleManager<IdentityRole, Guid> _roleManager;
public ControllerName(
IUnitOfWork unitOfWork,
UserManager<IdentityUser, Guid> userManager,
RoleManager<IdentityRole, Guid> roleManager) {
_unitOfWork = unitOfWork;
_userManager = userManager;
_roleManager = roleManager;
}
当然,我使用该存储库模式的工作还没有完成——实际的 User
table 与默认值相比有很大的修改,而且由于验证问题,我的实际用户插入失败了(我我认为某些东西被 non-nullable 值绊倒了,我没有在我的用户插入中明确说明,即使数据库字段有默认值),但重要的是依赖注入不再是问题.
当前项目:
- ASP.NET 4.6.2
- MVC 5
- 存储库模式来自 Persistence-Ignorant ASP.NET Identity
- ASP.NET MVC HTML5 样板
我已尽最大努力遵循上面链接的文章中的存储库模式,因为我有 a distinct need for a repository pattern,并且希望将用户 ID 视为 GUID 而不仅仅是 nvarchar(128 ) 字符串。
本质上,我需要在多个站点之间共享 table 结构,因此我使用了一种存储库模式,该模式还允许我在这些多个站点之间共享登录信息。各个站点使用 ASP.NET MVC HTML5 样板模板,因此 AutoFac 被嵌入其中。但是也请注意,一般来说,我在依赖注入方面存在相当大的概念困难,类似于尝试闻蓝色的气味,或尝试品尝温度的味道。我只是不了解它。
我还设法扩展了该存储库模式以允许我启用迁移。如:我能够将数据库模型迁移到数据库。不幸的是,因为我正在处理基础设施层(我称我的存储库模式层为数据库模型的核心和其他一切的基础设施),我无法将主要管理员用户注入数据库,因为没有 Entity Framework 也没有App_Start 在基础设施层。因此,我无法在 Infrastructure:
中使用ApplicationDbInitializer
namespace Company.Infrastructure.Context {
using Identity;
using Microsoft.AspNet.Identity;
using System;
using Store;
using System.Data.Entity;
public class ApplicationDbInitializer : DropCreateDatabaseAlways<ApplicationDbContext> {
private readonly UserManager<IdentityUser, Guid> _userManager;
private readonly RoleManager<IdentityRole, Guid> _roleManager;
public ApplicationDbInitializer() { }
public ApplicationDbInitializer(UserManager<IdentityUser, Guid> userManager, RoleManager<IdentityRole, Guid> roleManager) {
_userManager = userManager;
_roleManager = roleManager;
}
// This example shows you how to create a new database if the Model changes
//public class ApplicationDbInitializer : DropCreateDatabaseIfModelChanges<ApplicationDbContext>
protected override void Seed(ApplicationDbContext context) {
InitializeIdentityForEf(context);
base.Seed(context);
}
//Create User=Admin@Admin.com with password=Admin@123456 in the Admin role
public void InitializeIdentityForEf(ApplicationDbContext db) {
const string username = "CompanyName";
const string email = "info@companyname.com";
const string password = "password";
const string roleName = "Administrator";
//Create Role Admin if it does not exist
var role = _roleManager.FindByName(roleName);
if(role == null) {
role = new IdentityRole(roleName);
var roleresult = _roleManager.Create(role);
}
var user = _userManager.FindByName(username);
if(user == null) {
user = new IdentityUser { UserName = username, Email = email };
var result = _userManager.Create(user, password);
result = _userManager.SetLockoutEnabled(user.Id, false);
}
// Add user admin to Role Admin if not already added
var rolesForUser = _userManager.GetRoles(user.Id);
if(!rolesForUser.Contains(role.Name)) {
var result = _userManager.AddToRole(user.Id, role.Name);
}
}
}
}
注入主管理员。
因此,我试图通过其中一个网站注入主要管理员用户,但是当我尝试注册修改后的 UserManager
和 UserStore
时,AutoFac 似乎无法接收这些注册,即使它可以很好地接收模板附带的所有其他注册。
AutoFac 是通过 App_Start\Startup.Container.cs
:
namespace Company.Website {
using System.Reflection;
using System.Web.Mvc;
using Autofac;
using Autofac.Integration.Mvc;
using Services;
using Owin;
using System;
using Microsoft.AspNet.Identity;
using Infrastructure.Identity;
using Infrastructure.Store;
using Core.Repositories;
using Infrastructure.Context;
/// <summary>
/// Register types into the Autofac Inversion of Control (IOC) container. Autofac makes it easy to register common
/// MVC types like the <see cref="UrlHelper"/> using the <see cref="AutofacWebTypesModule"/>. Feel free to change
/// this to another IoC container of your choice but ensure that common MVC types like <see cref="UrlHelper"/> are
/// registered. See http://autofac.readthedocs.org/en/latest/integration/aspnet.html.
/// </summary>
public partial class Startup {
public static void ConfigureContainer(IAppBuilder app) {
var container = CreateContainer();
app.UseAutofacMiddleware(container);
// Register MVC Types
app.UseAutofacMvc();
}
private static IContainer CreateContainer() {
var builder = new ContainerBuilder();
var assembly = Assembly.GetExecutingAssembly();
RegisterServices(builder);
RegisterMvc(builder, assembly);
var container = builder.Build();
SetMvcDependencyResolver(container);
return container;
}
private static void RegisterServices(ContainerBuilder builder) {
builder.RegisterType<BrowserConfigService>().As<IBrowserConfigService>().InstancePerRequest();
builder.RegisterType<CacheService>().As<ICacheService>().SingleInstance();
builder.RegisterType<FeedService>().As<IFeedService>().InstancePerRequest();
builder.RegisterType<LoggingService>().As<ILoggingService>().SingleInstance();
builder.RegisterType<ManifestService>().As<IManifestService>().InstancePerRequest();
builder.RegisterType<OpenSearchService>().As<IOpenSearchService>().InstancePerRequest();
builder.RegisterType<RobotsService>().As<IRobotsService>().InstancePerRequest();
builder.RegisterType<SitemapService>().As<ISitemapService>().InstancePerRequest();
builder.RegisterType<SitemapPingerService>().As<ISitemapPingerService>().InstancePerRequest();
// My additions:
builder.RegisterType<UnitOfWork>().As<IUnitOfWork>().InstancePerRequest();
builder.RegisterType<UserManager<IdentityUser, Guid>>().As<UserManager<IdentityUser, Guid>>().InstancePerRequest();
builder.RegisterType<UserStore>().As<IUserStore<IdentityUser, Guid>>().InstancePerLifetimeScope();
}
private static void RegisterMvc(ContainerBuilder builder, Assembly assembly) {
// Register Common MVC Types
builder.RegisterModule<AutofacWebTypesModule>();
// Register MVC Filters
builder.RegisterFilterProvider();
// Register MVC Controllers
builder.RegisterControllers(assembly);
}
/// <summary>
/// Sets the ASP.NET MVC dependency resolver.
/// </summary>
/// <param name="container">The container.</param>
private static void SetMvcDependencyResolver(IContainer container) {
DependencyResolver.SetResolver(new AutofacDependencyResolver(container));
}
}
}
我的家庭控制器的开头是这样的:
namespace Company.Website.Controllers {
using Boilerplate.Web.Mvc;
using Boilerplate.Web.Mvc.Filters;
using Constants;
using Identity;
using Infrastructure.Models;
using Infrastructure.Context;
using Infrastructure.Store;
using Microsoft.AspNet.Identity;
using Microsoft.Owin.Security;
using Recaptcha.Web;
using Recaptcha.Web.Mvc;
using Services;
using System;
using System.Diagnostics;
using System.Linq;
using System.Net;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Web.Mvc;
public class HomeController : Controller {
private readonly IBrowserConfigService _browserConfigService;
private readonly IFeedService _feedService;
private readonly IManifestService _manifestService;
private readonly IOpenSearchService _openSearchService;
private readonly IRobotsService _robotsService;
private readonly ISitemapService _sitemapService;
private ApplicationDbContext _db;
private readonly UserManager<IdentityUser, Guid> _userManager;
public HomeController(
IBrowserConfigService browserConfigService,
IFeedService feedService,
IManifestService manifestService,
IOpenSearchService openSearchService,
IRobotsService robotsService,
ISitemapService sitemapService,
UserManager<IdentityUser, Guid> userManager) {
_browserConfigService = browserConfigService;
_feedService = feedService;
_manifestService = manifestService;
_openSearchService = openSearchService;
_robotsService = robotsService;
_sitemapService = sitemapService;
_userManager = userManager;
}
重点是我打算使用 Index 方法的初始 运行 将管理员用户注入到数据库中:
var user = _userManager.FindByName(username);
if(user == null) {
user = new IdentityUser { UserName = username, Email = email };
var result = _userManager.Create(user, password);
result = _userManager.SetLockoutEnabled(user.Id, false);
}
这可以从任何页面完成,我只是选择了索引。一旦注入了管理员用户,代码将被停用并删除,因为……好吧,到那时工作就完成了。已创建管理员用户。
问题是,由于默认构造函数中存在 UserManager 条目,整个系统崩溃了。全部归锅:
Autofac.Core.DependencyResolutionException: None of the constructors found with 'Autofac.Core.Activators.Reflection.DefaultConstructorFinder' on type 'Company.Website.Controllers.HomeController' can be invoked with the available services and parameters: Cannot resolve parameter 'Microsoft.AspNet.Identity.UserManager
2[Company.Website.Identity.IdentityUser,System.Guid] userManager' of constructor 'Void .ctor(Company.Website.Services.IBrowserConfigService, Company.Website.Services.IFeedService, Company.Website.Services.IManifestService, Company.Website.Services.IOpenSearchService, Company.Website.Services.IRobotsService, Company.Website.Services.ISitemapService, Microsoft.AspNet.Identity.UserManager
2[Company.Website.Identity.IdentityUser,System.Guid])'.
从默认构造函数中删除该条目,一切正常(当然,Index 中的用户创建代码被注释掉了)。把它放回去,DotNet 惊慌失措。
一段时间以来,我一直在为这个问题苦苦思索,现在已经束手无策了。任何帮助将不胜感激。我有
编辑 1: UserStore.cs
的请求,在 Company.Infrastructure.Store
:
namespace Company.Infrastructure.Store {
using Identity;
using Core.Repositories;
using Core.Models;
using Microsoft.AspNet.Identity;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Claims;
using System.Threading.Tasks;
using Context;
public class UserStore : IUserStore<IdentityUser, Guid>, IUserLoginStore<IdentityUser, Guid>, IUserClaimStore<IdentityUser, Guid>, IUserRoleStore<IdentityUser, Guid>, IUserPasswordStore<IdentityUser, Guid>, IUserSecurityStampStore<IdentityUser, Guid>, IDisposable {
private readonly IUnitOfWork _unitOfWork;
public UserStore(IUnitOfWork unitOfWork) {
_unitOfWork = unitOfWork;
}
#region IUserStore<IdentityUser, Guid> Members
public Task CreateAsync(IdentityUser user) {
if(user == null) throw new ArgumentNullException(nameof(user));
var u = getUser(user);
_unitOfWork.UserRepository.Add(u);
return _unitOfWork.SaveChangesAsync();
}
public Task DeleteAsync(IdentityUser user) {
if(user == null) throw new ArgumentNullException(nameof(user));
var u = getUser(user);
_unitOfWork.UserRepository.Remove(u);
return _unitOfWork.SaveChangesAsync();
}
public Task<IdentityUser> FindByIdAsync(Guid userId) {
var user = _unitOfWork.UserRepository.FindById(userId);
return Task.FromResult(getIdentityUser(user));
}
public Task<IdentityUser> FindByNameAsync(string userName) {
var user = _unitOfWork.UserRepository.FindByUserName(userName);
return Task.FromResult(getIdentityUser(user));
}
public Task UpdateAsync(IdentityUser user) {
if(user == null) throw new ArgumentException("user");
var u = _unitOfWork.UserRepository.FindById(user.Id);
if(u == null) throw new ArgumentException("IdentityUser does not correspond to a User entity.", nameof(user));
PopulateUser(u, user);
_unitOfWork.UserRepository.Update(u);
return _unitOfWork.SaveChangesAsync();
}
#endregion
#region IDisposable Members
public void Dispose() {
// Dispose does nothing since we want Unity to manage the lifecycle of our Unit of Work
}
#endregion
#region IUserClaimStore<IdentityUser, Guid> Members
public Task AddClaimAsync(IdentityUser user, Claim claim) {
if(user == null) throw new ArgumentNullException(nameof(user));
if(claim == null) throw new ArgumentNullException(nameof(claim));
var u = _unitOfWork.UserRepository.FindById(user.Id);
if(u == null) throw new ArgumentException("IdentityUser does not correspond to a User entity.", nameof(user));
var c = new UserClaim {
ClaimType = claim.Type,
ClaimValue = claim.Value,
User = u
};
u.UserClaim.Add(c);
_unitOfWork.UserRepository.Update(u);
return _unitOfWork.SaveChangesAsync();
}
public Task<IList<Claim>> GetClaimsAsync(IdentityUser user) {
if(user == null) throw new ArgumentNullException(nameof(user));
var u = _unitOfWork.UserRepository.FindById(user.Id);
if(u == null) throw new ArgumentException("IdentityUser does not correspond to a User entity.", nameof(user));
return Task.FromResult<IList<Claim>>(u.UserClaim.Select(x => new Claim(x.ClaimType, x.ClaimValue)).ToList());
}
public Task RemoveClaimAsync(IdentityUser user, Claim claim) {
if(user == null) throw new ArgumentNullException(nameof(user));
if(claim == null) throw new ArgumentNullException(nameof(claim));
var u = _unitOfWork.UserRepository.FindById(user.Id);
if(u == null) throw new ArgumentException("IdentityUser does not correspond to a User entity.", nameof(user));
var c = u.UserClaim.FirstOrDefault(x => x.ClaimType == claim.Type && x.ClaimValue == claim.Value);
u.UserClaim.Remove(c);
_unitOfWork.UserRepository.Update(u);
return _unitOfWork.SaveChangesAsync();
}
#endregion
#region IUserLoginStore<IdentityUser, Guid> Members
public Task AddLoginAsync(IdentityUser user, UserLoginInfo login) {
if(user == null) throw new ArgumentNullException(nameof(user));
if(login == null) throw new ArgumentNullException(nameof(login));
var u = _unitOfWork.UserRepository.FindById(user.Id);
if(u == null) throw new ArgumentException("IdentityUser does not correspond to a User entity.", nameof(user));
var l = new UserLogin {
LoginProvider = login.LoginProvider,
ProviderKey = login.ProviderKey,
User = u
};
u.UserLogin.Add(l);
_unitOfWork.UserRepository.Update(u);
return _unitOfWork.SaveChangesAsync();
}
public Task<IdentityUser> FindAsync(UserLoginInfo login) {
if(login == null) throw new ArgumentNullException(nameof(login));
var identityUser = default(IdentityUser);
var l = _unitOfWork.LoginRepository.GetByProviderAndKey(login.LoginProvider, login.ProviderKey);
if(l != null) identityUser = getIdentityUser(l.User);
return Task.FromResult(identityUser);
}
public Task<IList<UserLoginInfo>> GetLoginsAsync(IdentityUser user) {
if(user == null) throw new ArgumentNullException(nameof(user));
var u = _unitOfWork.UserRepository.FindById(user.Id);
if(u == null) throw new ArgumentException("IdentityUser does not correspond to a User entity.", nameof(user));
return Task.FromResult<IList<UserLoginInfo>>(u.UserLogin.Select(x => new UserLoginInfo(x.LoginProvider, x.ProviderKey)).ToList());
}
public Task RemoveLoginAsync(IdentityUser user, UserLoginInfo login) {
if(user == null) throw new ArgumentNullException(nameof(user));
if(login == null) throw new ArgumentNullException(nameof(login));
var u = _unitOfWork.UserRepository.FindById(user.Id);
if(u == null) throw new ArgumentException("IdentityUser does not correspond to a User entity.", nameof(user));
var l = u.UserLogin.FirstOrDefault(x => x.LoginProvider == login.LoginProvider && x.ProviderKey == login.ProviderKey);
u.UserLogin.Remove(l);
_unitOfWork.UserRepository.Update(u);
return _unitOfWork.SaveChangesAsync();
}
#endregion
#region IUserRoleStore<IdentityUser, Guid> Members
public Task AddToRoleAsync(IdentityUser user, string roleName) {
if(user == null) throw new ArgumentNullException(nameof(user));
if(string.IsNullOrWhiteSpace(roleName)) throw new ArgumentException("Argument cannot be null, empty, or whitespace: roleName.");
var u = _unitOfWork.UserRepository.FindById(user.Id);
if(u == null) throw new ArgumentException("IdentityUser does not correspond to a User entity.", nameof(user));
var r = _unitOfWork.RoleRepository.FindByName(roleName);
if(r == null) throw new ArgumentException("roleName does not correspond to a Role entity.", nameof(roleName));
u.Role.Add(r);
_unitOfWork.UserRepository.Update(u);
return _unitOfWork.SaveChangesAsync();
}
public Task<IList<string>> GetRolesAsync(IdentityUser user) {
if(user == null) throw new ArgumentNullException(nameof(user));
var u = _unitOfWork.UserRepository.FindById(user.Id);
if(u == null) throw new ArgumentException("IdentityUser does not correspond to a User entity.", nameof(user));
return Task.FromResult<IList<string>>(u.Role.Select(x => x.Name).ToList());
}
public Task<bool> IsInRoleAsync(IdentityUser user, string roleName) {
if(user == null) throw new ArgumentNullException(nameof(user));
if(string.IsNullOrWhiteSpace(roleName)) throw new ArgumentException("Argument cannot be null, empty, or whitespace: role.");
var u = _unitOfWork.UserRepository.FindById(user.Id);
if(u == null) throw new ArgumentException("IdentityUser does not correspond to a User entity.", nameof(user));
return Task.FromResult(u.Role.Any(x => x.Name == roleName));
}
public Task RemoveFromRoleAsync(IdentityUser user, string roleName) {
if(user == null) throw new ArgumentNullException(nameof(user));
if(string.IsNullOrWhiteSpace(roleName)) throw new ArgumentException("Argument cannot be null, empty, or whitespace: role.");
var u = _unitOfWork.UserRepository.FindById(user.Id);
if(u == null) throw new ArgumentException("IdentityUser does not correspond to a User entity.", nameof(user));
var r = u.Role.FirstOrDefault(x => x.Name == roleName);
u.Role.Remove(r);
_unitOfWork.UserRepository.Update(u);
return _unitOfWork.SaveChangesAsync();
}
#endregion
#region IUserPasswordStore<IdentityUser, Guid> Members
public Task<string> GetPasswordHashAsync(IdentityUser user) {
if(user == null) throw new ArgumentNullException(nameof(user));
return Task.FromResult(user.PasswordHash);
}
public Task<bool> HasPasswordAsync(IdentityUser user) {
if(user == null) throw new ArgumentNullException(nameof(user));
return Task.FromResult(!string.IsNullOrWhiteSpace(user.PasswordHash));
}
public Task SetPasswordHashAsync(IdentityUser user, string passwordHash) {
user.PasswordHash = passwordHash;
return Task.FromResult(0);
}
#endregion
#region IUserSecurityStampStore<IdentityUser, Guid> Members
public Task<string> GetSecurityStampAsync(IdentityUser user) {
if(user == null) throw new ArgumentNullException(nameof(user));
return Task.FromResult(user.SecurityStamp);
}
public Task SetSecurityStampAsync(IdentityUser user, string stamp) {
user.SecurityStamp = stamp;
return Task.FromResult(0);
}
#endregion
#region Private Methods
private User getUser(IdentityUser identityUser) {
if(identityUser == null) return null;
var user = new User();
PopulateUser(user, identityUser);
return user;
}
private static void PopulateUser(User user, IdentityUser identityUser) {
user.UserId = identityUser.Id;
user.UserName = identityUser.UserName;
user.PasswordHash = identityUser.PasswordHash;
user.SecurityStamp = identityUser.SecurityStamp;
}
private IdentityUser getIdentityUser(User user) {
if(user == null) return null;
var identityUser = new IdentityUser();
PopulateIdentityUser(identityUser, user);
return identityUser;
}
private static void PopulateIdentityUser(IdentityUser identityUser, User user) {
identityUser.Id = user.UserId;
identityUser.UserName = user.UserName;
identityUser.PasswordHash = user.PasswordHash;
identityUser.SecurityStamp = user.SecurityStamp;
}
#endregion
}
}
感谢 tdragon,我能够 bash 将足够多的随机岩石组合在一起 assemble 我自己进行了函数依赖注入。
因为我使用了 ASP.NET MVC HTML5 样板,实际的依赖设置可能与默认的 AutoFac 设置不同,但关键是如果你使用相同的 repository pattern tutorial 和我一样,您需要在 StartupContainer.cs
中创建以下内容:
builder.RegisterType<UnitOfWork>().As<IUnitOfWork>().InstancePerRequest();
builder.RegisterType<UserManager<IdentityUser, Guid>>().InstancePerRequest();
builder.RegisterType<UserStore>().As<IUserStore<IdentityUser, Guid>>().InstancePerLifetimeScope();
builder.RegisterType<RoleManager<IdentityRole, Guid>>().InstancePerRequest();
builder.RegisterType<RoleStore>().As<IRoleStore<IdentityRole, Guid>>().InstancePerLifetimeScope();
从那里开始,无论您希望在哪个控制器中操纵用户,都需要添加以下内容:
private readonly IUnitOfWork _unitOfWork;
private readonly UserManager<IdentityUser, Guid> _userManager;
private readonly RoleManager<IdentityRole, Guid> _roleManager;
public ControllerName(
IUnitOfWork unitOfWork,
UserManager<IdentityUser, Guid> userManager,
RoleManager<IdentityRole, Guid> roleManager) {
_unitOfWork = unitOfWork;
_userManager = userManager;
_roleManager = roleManager;
}
当然,我使用该存储库模式的工作还没有完成——实际的 User
table 与默认值相比有很大的修改,而且由于验证问题,我的实际用户插入失败了(我我认为某些东西被 non-nullable 值绊倒了,我没有在我的用户插入中明确说明,即使数据库字段有默认值),但重要的是依赖注入不再是问题.