Microsoft在OWIN实现中扩展方法CreatePerOwinContext的目的是什么

Nex*_*per 49 asp.net owin katana

我是ASP.NET的新手,目前正在学习ASP.NET身份.我知道它建立在微软的OWIN实现之上,而且我也在学习它.所以,我在Owin启动代码中遇到了扩展方法CreatePerOwinContext,我没有看到使用它的明确目的.它是某种依赖注入容器吗?这个方法的真正目的是什么?在什么情况下应该应用它?

Lef*_*tyX 66

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; }
}
Run Code Online (Sandbox Code Playgroud)

和你的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);
        }
}
Run Code Online (Sandbox Code Playgroud)

在您的Owin Startup中,您将注册回调:

// IAppBuilder app

app.CreatePerOwinContext<ApplicationDatabaseContext>(ApplicationDatabaseContext.Create);
app.CreatePerOwinContext<ApplicationUserManager>(ApplicationUserManager.Create);
Run Code Online (Sandbox Code Playgroud)

这将调用静态方法:

public static ApplicationDatabaseContext Create()
{
    return new ApplicationDatabaseContext();
}
Run Code Online (Sandbox Code Playgroud)

public static ApplicationUserManager Create(IdentityFactoryOptions<ApplicationUserManager> options, IOwinContext context)
{
    ...
}
Run Code Online (Sandbox Code Playgroud)

现在,您将能够以一种简单直接的方式访问数据库上下文和用户管理器:

ApplicationDatabaseContext dbContext = context.OwinContext.Get<ApplicationDatabaseContext>();
ApplicationUserManager userManager = context.OwinContext.GetUserManager<ApplicationUserManager>();
Run Code Online (Sandbox Code Playgroud)

在你的ApiController中(如果你使用的是WebApi):

IAuthenticationManager authenticationManager = HttpContext.Current.GetOwinContext().Authentication;
ApplicationUserManager applicationUserManager = HttpContext.Current.GetOwinContext().GetUserManager<ApplicationUserManager>();
Run Code Online (Sandbox Code Playgroud)

  • 感谢您的解释.所以它看起来像某种系统服务定位器.在启动代码中,通过提供静态方法来注册服务工厂,该方法在第一次请求时创建服务,并且在应用程序的某个位置可以从OwinContext检索服务. (3认同)
  • @NextDeveloper:没错.当然,如果您使用自己的IoC,可以跳过该注册并使用自己的[实现](https://aspnetidentity.codeplex.com/discussions/532294) (2认同)
  • 有没有办法检索在OWIN字典中使用的密钥?如果我正在使用Nancy,我只有Context.GetOwinEnvironment来获取字典.在那里,对象被键入类似"AspNet.Identity.Owin:WebApplication2.ApplicationDbContext,WebApplication2,Version = 1.0.0.0,Culture = neutral,PublicKeyToken = null".初始调用是app.CreatePerOwinContext <ApplicationDbContext>(ApplicationDbContext .创造);到底谁知道这个名字? (2认同)

Tod*_*odd 5

该方法的真正目的是什么?在什么情况下应该使用它?

为了更直接地回答您的问题,这是没有用的。

  1. 这是某些人喜欢使用的IoC工厂。
  2. 这使您可以选择使用他们的(IoC)。
  3. (我不喜欢IoC,对于想要感到温暖和模糊并使用“体系结构”一词的人来说,它就像是一种反模式。)
  4. 但认真的说,这种模式不是IoC接口,而是IoC静态工厂功能!那是谁的主意?为什么不自己使用Factory功能呢?现在,您必须记住(Google)一个额外的API调用,并且当您在上按F12时Get,对您毫无帮助。

那你该怎么办呢?

就个人而言,我是为此使用OO的粉丝,还记得OO吗?Pepperidge农场记得。使用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();
        }
    }

}
Run Code Online (Sandbox Code Playgroud)

所有这一切都可能是浪费时间,如果有人找到Microsoft工程师为何要使用此文档的话,他们可能有充分的理由,但我对此表示怀疑,因此,让我们在此同时提出一个答案。

更新1

这是Microsoft的原因,原因:https//blogs.msdn.microsoft.com/webdev/2014/02/12/per-request-lifetime-management-for-usermanager-class-in-asp-net-身份/

基本上,UserManager及其所有都是针对这种结构而构建的。安全检查发生在管道中,那么为什么不将单例链接到请求以减少浪费呢?因为它是隐藏的。

我仍然建议在基类上创建自己的db上下文实例,这样可以使使用起来更加简洁。如果确实需要,则可以在基类中有一个属性,该属性从OwinContext中检索单例。

当我们要做的全部事情是:时,我们花了多少时间来尝试开发这些奇特的API和Authorize属性等。

public void DoSomething()
{
   DemandAuthenticated();
   DemandAuthorised(typeof(somethingClass), "DoSomething");
}
Run Code Online (Sandbox Code Playgroud)

显然,我更喜欢您可以看到的详细代码。

更新2

EF上下文不应作为单例保存,当然也不应通过任何IoC或存储库模式保存。

通常,是的,IoC在某些情况下会很好。但是专门针对dbContext吗?没有。

1)EF DB上下文是一个工作单元,应该短暂存在。如果您让它们长时间运行,则对象缓存将减慢查询的速度,并且对基础数据库的更新/插入会变慢。它的设计寿命很短。2)此外,EF上下文已经松散耦合。您可以在连接字符串中的上下文后面更改RDBMS,甚至可以使用仅内存。3)EF具有LINQ,它非常灵活,易于表达且类型安全。4)数据库不是IoC的业务级服务,它是服务用来与数据库进行通信的工具。也许,您可能具有通过IoC访问的IEmail服务。但是它应该使用新的EF上下文访问内部数据库,该上下文在查询完成后会立即处理。5)鉴于以上1-4,我们当然不会