由于我对应用程序服务的实现进行了编码(目前),以及为依赖注入配置它们的方式,我对MVC应用程序有点困惑。
我希望通过遵循SOLID原则来分离应用程序的各个层。
问题是,在某些服务中,构造函数需要IUserContext的实例。IUserContext包含有关登录用户的各种信息,并将在几个不同的层之间传递。
public class ProjectDataLoader : DataLoaderBase, IProjectDataLoader
{
public ProjectDataLoader(IMyDbContext dbContext, IUserContext userContext)
: base (dbContext, userContext)
{
}
...
public IEnumerable<ProjectViewModel> Find(string filter = "")
{
...
}
}
以及IUserContext的实现:
public class AspNetUserContext : IUserContext
{
...
}
我可以在每个方法调用上传递IUserContext,但我觉得它属于构造函数。但这不是问题所在。
当我通过AccountController、MyAppSignInManager从登录页面登录时。SignInOrTwoFactor通过OWIN管道调用。此时,我正在会话中创建AspNetUserContext的新实例:
HttpContext.Current.Session["UserContext"] = aspNetUserContext;
现在我有了自定义SignInManager实现:
public class MyAppSignInManager : SignInManager<MyAppUser, string>
{
...
}
我有一个自定义IUserStore实现:
public class MyAppUserStore : IUserPasswordStore<MyAppUser>,
IUserStore<MyAppUser>
{
...
}
以上所有内容都与我选择的容器SimpleInjector进行了依赖注入。
public static class DependencyConfig
{
public static Container Initialize(IAppBuilder app)
{
Container container = GetInitializeContainer(app);
container.Verify();
DependencyResolver.SetResolver(
new SimpleInjectorDependencyResolver(container));
return container;
}
private static Container GetInitializeContainer(IAppBuilder app)
{
var container = new Container();
RegisterCommon(container);
RegisterRepositories(container);
RegisterDataLoaders(container);
RegisterAppServices(container);
RegisterMvc(app, container);
return container;
}
private static void RegisterCommon(Container container)
{
container.Register<IUserContext>(() =>
{
IUserContext context = null;
if (HttpContext.Current.Session == null)
context = new AspNetUserContext(Guid.Empty, Guid.Empty);
else
context = (IUserContext)HttpContext.Current.Session["UserContext"];
return context;
}, Lifestyle.Transient);
}
private static void RegisterRepositories(Container container)
{
container.RegisterPerWebRequest<IUserRepository>(() =>
new UserRepository(container.GetInstance<IMyApp4Context>()));
container.Register<IMyApp4Context>(() => new MyApp4Context(),
Lifestyle.Transient);
}
private static void RegisterDataLoaders(Container container)
{
container.Register<IProjectDataLoader, ProjectDataLoader>();
container.Register<ContractDataLoader>();
container.Register<DrawingDataLoader>();
container.Register<WeldDataLoader>();
}
private static void RegisterAppServices(Container container)
{
}
private static void RegisterMvc(IAppBuilder app, Container container)
{
container.RegisterSingle(app);
container.RegisterPerWebRequest<MyAppUserManager>();
container.RegisterPerWebRequest<SignInManager<MyAppUser, string>,
MyAppAppSignInManager>();
container.RegisterPerWebRequest(() =>
{
if (HttpContext.Current != null &&
HttpContext.Current.Items["owin.Environment"] == null &&
container.IsVerifying())
{
return new OwinContext().Authentication;
}
return HttpContext.Current.GetOwinContext().Authentication;
});
container.RegisterPerWebRequest<IUserStore<MyAppUser>>(() =>
new MyAppUserStore(container.GetInstance<IUserRepository>()));
app.UseOwinContextInjector(container);
container.RegisterMvcControllers(
Assembly.GetExecutingAssembly());
}
private static void InitializeUserManager(MyAppUserManager manager, IAppBuilder app)
{
manager.UserValidator =
new UserValidator<MyAppUser>(manager)
{
AllowOnlyAlphanumericUserNames = false,
RequireUniqueEmail = true
};
manager.PasswordValidator = new PasswordValidator()
{
RequiredLength = 6,
RequireNonLetterOrDigit = false,
RequireDigit = true,
RequireLowercase = true,
RequireUppercase = true,
};
IDataProtectionProvider dataProtectionProvider =
app.GetDataProtectionProvider();
if (dataProtectionProvider != null)
{
manager.UserTokenProvider =
new DataProtectorTokenProvider<MyAppUser>(
dataProtectionProvider.Create(purposes: new string[] { "ASP.NET Identity" }));
}
}
}
此外:
public partial class Startup
{
public void ConfigureAuth(IAppBuilder app, Container container)
{
app.CreatePerOwinContext(() => container.GetInstance<MyAppUserManager>());
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
LoginPath = new PathString(value: "/Account/Login"),
Provider = new CookieAuthenticationProvider
{
OnValidateIdentity = SecurityStampValidator.OnValidateIdentity<MyAppUserManager, MyAppUser>(
validateInterval: TimeSpan.FromMinutes(value: 30),
regenerateIdentity: (manager, user) =>
{
return user.GenerateUserIdentityAsync(manager);
})
}
});
app.UseExternalSignInCookie(DefaultAuthenticationTypes.ExternalCookie);
app.UseTwoFactorSignInCookie(DefaultAuthenticationTypes.TwoFactorCookie, TimeSpan.FromMinutes(5));
app.UseTwoFactorRememberBrowserCookie(DefaultAuthenticationTypes.TwoFactorRememberBrowserCookie);
}
}
然后这些将用于控制器:
public class ProjectController : MyBaseContextController
{
public ProjectController(IProjectDataLoader loader)
: base(context)
{
...
}
}
我最初的问题是,在cookie认证完成后,如何获取MyAppUser。也许这样问仍然有效。
更好的问题是问我想做什么。本质上,我想要的是将IUserContext注入到我的服务中。这需要注入到DI容器中注册的各种服务实现的构造函数中。但是,在用户登录/验证之前,此实例将不可用。
注:
所有用户信息都存储在SQL中,我使用实体框架访问所有这些信息。
因此,一旦用户通过MyAppSignInManager通过登录页面登录进行了身份验证。SignInOrTwoFactor方法和cookie,我如何使AspNetUserContext(IUserContext)实例可用于DI容器?
注释
:我只想从数据库中获取一次用户信息,而不是每次调用需要的控制器。