【发布时间】:2014-10-29 08:56:22
【问题描述】:
我是 ASP.NET 的新手,目前正在学习 ASP.NET Identity。我知道它是建立在 Microsoft 的 OWIN 实现之上的,我也在学习这一点。所以,我在 Owin 启动代码中遇到了扩展方法 CreatePerOwinContext,我没有看到使用它的明确目的。它是某种依赖注入容器吗?该方法的真正目的是什么?应该在什么情况下应用?
【问题讨论】:
我是 ASP.NET 的新手,目前正在学习 ASP.NET Identity。我知道它是建立在 Microsoft 的 OWIN 实现之上的,我也在学习这一点。所以,我在 Owin 启动代码中遇到了扩展方法 CreatePerOwinContext,我没有看到使用它的明确目的。它是某种依赖注入容器吗?该方法的真正目的是什么?应该在什么情况下应用?
【问题讨论】:
CreatePerOwinContext 注册一个静态回调,您的应用程序将使用该回调来取回指定类型的新实例。
此回调将在每个请求中调用一次,并将对象/对象存储在 OwinContext 中,以便您可以在整个应用程序中使用它们。
假设您已经定义了自己的 IdentityDbContext 实现:
public class ApplicationDatabaseContext : IdentityDbContext<MyApplicationUser, MyRole, Guid, MyUserLogin, MyUserRole, MyUserClaim>
{
public ApplicationDatabaseContext() : base("<connection string>")
{
}
public static ApplicationDatabaseContext Create()
{
return new ApplicationDatabaseContext();
}
protected override void OnModelCreating(System.Data.Entity.DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
// Customize your table creation here.
#region USERS - INFOS
modelBuilder.Entity<UserInfo>()
.Property(p => p.FirstName)
.HasColumnType("varchar")
.HasMaxLength(70);
modelBuilder.Entity<UserInfo>()
.Property(p => p.LastName)
.HasColumnType("varchar")
.HasMaxLength(70);
modelBuilder.Entity<UserInfo>()
.Property(p => p.Address)
.HasColumnType("varchar")
.HasMaxLength(100);
modelBuilder.Entity<UserInfo>()
.Property(p => p.City)
.HasColumnType("varchar")
.HasMaxLength(100);
modelBuilder.Entity<UserInfo>()
.ToTable("UsersInfo");
#endregion
}
public DbSet<UserInfo> UsersInfo { get; set; }
}
以及你对UserManager的实现:
public class ApplicationUserManager : UserManager<MyApplicationUser, Guid>
{
public ApplicationUserManager(IUserStore<MyApplicationUser, Guid> store) : base(store)
{
}
public static ApplicationUserManager Create(IdentityFactoryOptions<ApplicationUserManager> options, IOwinContext context)
{
var manager = new ApplicationUserManager(new MyUserStore(context.Get<ApplicationDatabaseContext>()));
manager.UserValidator = new UserValidator<MyApplicationUser, Guid>(manager)
{
AllowOnlyAlphanumericUserNames = false,
RequireUniqueEmail = true
};
manager.PasswordValidator = new PasswordValidator()
{
RequiredLength = 6,
RequireNonLetterOrDigit = false,
// RequireDigit = true,
RequireLowercase = false,
RequireUppercase = false,
};
var dataProtectionProvider = options.DataProtectionProvider;
if (dataProtectionProvider != null)
{
manager.UserTokenProvider = new DataProtectorTokenProvider<MyApplicationUser, Guid>(dataProtectionProvider.Create("PasswordReset"));
}
return (manager);
}
}
在您的 Owin Startup 中,您将注册回调:
// IAppBuilder app
app.CreatePerOwinContext<ApplicationDatabaseContext>(ApplicationDatabaseContext.Create);
app.CreatePerOwinContext<ApplicationUserManager>(ApplicationUserManager.Create);
将调用静态方法:
public static ApplicationDatabaseContext Create()
{
return new ApplicationDatabaseContext();
}
和
public static ApplicationUserManager Create(IdentityFactoryOptions<ApplicationUserManager> options, IOwinContext context)
{
...
}
现在您将能够以简单直接的方式访问您的数据库上下文和用户管理器:
ApplicationDatabaseContext dbContext = context.OwinContext.Get<ApplicationDatabaseContext>();
ApplicationUserManager userManager = context.OwinContext.GetUserManager<ApplicationUserManager>();
在您的 ApiController 中(如果您使用 WebApi):
IAuthenticationManager authenticationManager = HttpContext.Current.GetOwinContext().Authentication;
ApplicationUserManager applicationUserManager = HttpContext.Current.GetOwinContext().GetUserManager<ApplicationUserManager>();
【讨论】:
ApplicationDatabaseContext.Create。
该方法的真正目的是什么?在什么情况下应该 申请了吗?
要更直接地回答您的问题,这是没有用的。
Get 上按 F12 时,它对您毫无帮助。那你应该怎么做呢?
就我个人而言,我非常喜欢为此使用 OO,还记得 OO 吗?佩珀里奇农场记得。使用 OO,您可以保持控制,可以调试、记录和扩展。
public class BaseApiController : ApiController
{
private AppDbContext _db = null;
protected AppDbContext db
{
get
{
if (_db == null)
{
_db = AppDbContext.Create(); //Hey look a proper factory that you can extend with other overloads! And I can debug this line - neat!
}
return _db;
}
}
protected override void Dispose(bool disposing)
{
if (disposing)
{
if (_db != null)
_db.Dispose();
}
}
}
所有这一切都可能是浪费时间,如果有人找到一些文档为什么 Microsoft 工程师将其放入,他们可能有充分的理由,但我对此表示怀疑,所以同时让我们支持这个答案。
更新 1
这就是为什么它存在于 Microsoft 的原因:https://blogs.msdn.microsoft.com/webdev/2014/02/12/per-request-lifetime-management-for-usermanager-class-in-asp-net-identity/
基本上,UserManager 和所有这些都是为这种结构而构建的。安全检查发生在管道中,那么为什么不将单例链接到请求以减少浪费呢?因为它被隐藏了。
我仍然建议在基类上创建自己的 db 上下文实例,这样使用起来更加简洁。如果你真的想要,你可以在你的基类中有一个属性,它从 OwinContext 中检索单例。
当我们想做的只是:
public void DoSomething()
{
DemandAuthenticated();
DemandAuthorised(typeof(somethingClass), "DoSomething");
}
显然,我更喜欢您可以看到的详细代码。
更新 2
EF 上下文不应作为单例保存,当然也不应通过任何 IoC 或存储库模式。
一般来说,是的,IoC 在某些情况下可能会很好。但专门针对 dbContext?没有。
1) EF DB 上下文是一个工作单元,它们应该是短暂的。如果让它们长时间运行,对象缓存会减慢查询速度,并且对底层数据库的更新/插入会变慢。它的设计寿命很短。 2) 此外,EF 上下文已经松散耦合。您可以更改连接字符串中上下文后面的 RDBMS,甚至可以仅使用内存。 3) EF 具有非常灵活、富有表现力和类型安全的 LINQ。 4) 数据库不是 IoC 的业务级服务,它是服务用来与数据库通信的工具。也许,您可能有某种通过 IoC 访问的服务 IEmail。但它应该使用一个新的 EF 上下文访问内部数据库,该上下文在完成查询后会立即处理。 5) 鉴于上述 1-4,我们当然不希望任何中间接口层(服务或存储库)首先破坏使用 EF 的所有好处。
【讨论】:
您可以使用typeof 来获取这样的名称:
HttpContext.GetOwinContext().Get<ApplicationDbContext>(typeof(ApplicationDbContext).ToString());
【讨论】: