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

时间:2014-10-29 08:56:23

标签: asp.net owin katana

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

3 个答案:

答案 0 :(得分:64)

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 启动中,您将注册回调:

// 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>();

答案 1 :(得分:4)

  

该方法的真正目的是什么?它应该是什么情况   施加?

更直接地回答你的问题,这是没用的。

  1. 它是某种人喜欢使用的某种IoC工厂。
  2. 这个让你使用他们的(IoC)而不是你的选择。
  3. (我不喜欢IoC,对于那些想要感到温暖和模糊并且使用术语&#34;架构&#34;的人来说,感觉就像是反模式。)
  4. 但严重的是,这种模式不是IoC接口,它是IoC静态工厂功能!谁的想法是这样的?为什么不自己使用Factory功能呢?现在你必须记住(谷歌)一个额外的API调用,当你在Get上按F12时,它将带你无处帮助。
  5. 那你应该做什么呢?

    就个人而言,我是使用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();
            }
        }
    
    }
    

    所有这些都可能是浪费时间,如果有人发现微软工程师为什么要提供这些文档,他们可能有充分的理由,但我对此表示怀疑,所以让我们在此期间提出这个答案。

    更新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中检索单例。

    当我们想要做的只是:

    时,我们浪费了多少时间来尝试制定这些花哨的API,授权属性等等。
    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的所有好处。

答案 2 :(得分:2)

您可以使用typeof获取如下名称:

HttpContext.GetOwinContext().Get<ApplicationDbContext>(typeof(ApplicationDbContext).ToString());