简体   繁体   English

有什么方法可以将owin startup.cs配置为在全局asax应用程序启动之前运行?

[英]Is there any way to configure owin startup.cs to be run before global asax application start?

I'm trying to implement the container per request and transaction per request patterns in MVC 5. I've written most of the code for it leveraging the global asax. 我正在尝试在MVC 5中实现每个请求的容器和每个请求的事务模式。我已经利用全局asax编写了大部分代码。 I am having trouble with multiple contexts being created though because owin is creating new DbContext classes as part of it's startup.cs. 我在创建多个上下文时遇到了麻烦,因为owin正在创建新的DbContext类作为startup.cs的一部分。 Is there any way I can have owin run before the global asax application start event so that I can retrieve the existing context that was created there? 有什么办法可以在全局asax应用程序启动事件之前运行owin,以便可以检索在那里创建的现有上下文? I'm new to these patterns so if that question doesn't sound right I'm open to alternate suggestions. 我是这些模式的新手,因此,如果这个问题听起来不正确,我欢迎您提出替代建议。

public IContainer Container
{
    get
    {
        return (IContainer)HttpContext.Current.Items["_Container"];
    }
    set
    {
        HttpContext.Current.Items["_Container"] = value;
    }
}

protected void Application_Start()
{
    AreaRegistration.RegisterAllAreas();
    FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
    RouteConfig.RegisterRoutes(RouteTable.Routes);
    BundleConfig.RegisterBundles(BundleTable.Bundles);

    ////Database.SetInitializer(new MigrateDatabaseToLatestVersion<ApplicationDbContext, Configuration>());


    DependencyResolver.SetResolver(
        new StructureMapDependencyResolver(() => Container ?? IoC.Container));

    IoC.Container.Configure(cfg =>
    {
        cfg.AddRegistry(new StandardRegistry());
        cfg.AddRegistry(new ControllerRegistry());
        cfg.AddRegistry(new ActionFilterRegistry(
            () => Container ?? IoC.Container));
        cfg.AddRegistry(new MvcRegistry());
        cfg.AddRegistry(new TaskRegistry());
        cfg.AddRegistry(new ModelMetadataRegistry());
    });

    using (var container = IoC.Container.GetNestedContainer())
    {

        foreach (var task in container.GetAllInstances<IRunAtInit>())
        {
            task.Execute();
        }

        foreach (var task in container.GetAllInstances<IRunAtStartup>())
        {
            task.Execute();
        }
    }
}

public void Application_BeginRequest()
{
    Container = IoC.Container.GetNestedContainer();

    foreach (var task in Container.GetAllInstances<IRunOnEachRequest>())
    {
        task.Execute();
    }
}

public void Application_Error()
{
    foreach (var task in Container.GetAllInstances<IRunOnError>())
    {
        task.Execute();
    }
}

public void Application_EndRequest()
{
    try
    {
        foreach (var task in
            Container.GetAllInstances<IRunAfterEachRequest>())
        {
            task.Execute();
        }
    }
    finally
    {
        Container.Dispose();
        Container = null;
    }
}

Structure Map Class 结构图类

public class StructureMapDependencyResolver : IDependencyResolver
{
    private readonly Func<IContainer> _factory;

    public StructureMapDependencyResolver(Func<IContainer> factory)
    {
        _factory = factory;
    }

    public object GetService(Type serviceType)
    {
        if (serviceType == null)
        {
            return null;
        }

        var factory = _factory();

        return serviceType.IsAbstract || serviceType.IsInterface
            ? factory.TryGetInstance(serviceType)
            : factory.GetInstance(serviceType);
    }

    public IEnumerable<object> GetServices(Type serviceType)
    {
        return _factory().GetAllInstances(serviceType).Cast<object>();
    }

Startup for Owin - this is being executed both after Global ASAX on start and on each request, but after the global ASAX code. Owin的启动-在启动和每个请求时在Global ASAX之后执行,但在全局ASAX代码之后执行。 I would like to be able to either set the OWIN context to an existing context instance or have this code execute first and get the context that was created in startup. 我希望能够将OWIN上下文设置为现有的上下文实例,或者先执行此代码并获取在启动时创建的上下文。

    // For more information on configuring authentication, please visit https://go.microsoft.com/fwlink/?LinkId=301864
    public void ConfigureAuth(IAppBuilder app)
    {

        // Configure the db context, user manager and signin manager to use a single instance per request
        app.CreatePerOwinContext(ApplicationDbContext.Create);
        app.CreatePerOwinContext<ApplicationUserManager>(ApplicationUserManager.Create);
        app.CreatePerOwinContext<ApplicationSignInManager>(ApplicationSignInManager.Create);

        // Enable the application to use a cookie to store information for the signed in user
        // and to use a cookie to temporarily store information about a user logging in with a third party login provider
        // Configure the sign in cookie
        app.UseCookieAuthentication(new CookieAuthenticationOptions
        {
            AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
            LoginPath = new PathString("/Account/Login"),
            Provider = new CookieAuthenticationProvider
            {
                // Enables the application to validate the security stamp when the user logs in.
                // This is a security feature which is used when you change a password or add an external login to your account.  
                OnValidateIdentity = SecurityStampValidator.OnValidateIdentity<ApplicationUserManager, ApplicationUser>(
                    validateInterval: TimeSpan.FromMinutes(30),
                    regenerateIdentity: (manager, user) => user.GenerateUserIdentityAsync(manager))
            }
        });            
        app.UseExternalSignInCookie(DefaultAuthenticationTypes.ExternalCookie);

        // Enables the application to temporarily store user information when they are verifying the second factor in the two-factor authentication process.
        app.UseTwoFactorSignInCookie(DefaultAuthenticationTypes.TwoFactorCookie, TimeSpan.FromMinutes(5));

        // Enables the application to remember the second login verification factor such as phone or email.
        // Once you check this option, your second step of verification during the login process will be remembered on the device where you logged in from.
        // This is similar to the RememberMe option when you log in.
        app.UseTwoFactorRememberBrowserCookie(DefaultAuthenticationTypes.TwoFactorRememberBrowserCookie);

        // Uncomment the following lines to enable logging in with third party login providers
        //app.UseMicrosoftAccountAuthentication(
        //    clientId: "",
        //    clientSecret: "");

        //app.UseTwitterAuthentication(
        //   consumerKey: "",
        //   consumerSecret: "");

        //app.UseFacebookAuthentication(
        //   appId: "",
        //   appSecret: "");

        //app.UseGoogleAuthentication(new GoogleOAuth2AuthenticationOptions()
        //{
        //    ClientId = "",
        //    ClientSecret = ""
        //});
    }
}

With MVC 5, OWIN will always run after global.asax Application_Start() . 使用MVC 5,OWIN将始终在global.asax Application_Start()之后运行。 That's because the MVC application hosts OWIN. 这是因为MVC应用程序托管OWIN。 It's far easier to let OWIN handle configurations and startups, with App_Start just for MVC registrations. 仅使用MVC注册的App_Start,让OWIN处理配置和启动要容易得多。 So I recommend moving your container registrations and startups to Startup . 因此,我建议将您的容器注册和Startup移动到Startup Something like this: 像这样:

public partial class Startup
{
    public void Configuration(IAppBuilder app)
    {
        DependencyResolver.SetResolver(
            new StructureMapDependencyResolver(IoC.Container));

        IoC.Container.Configure(cfg =>
        {
           ...
        });

        using (var container = IoC.Container.GetNestedContainer())
        {
            foreach (var task in container.GetAllInstances<IRunAtInit>())
            {
                task.Execute();
            }

            foreach (var task in container.GetAllInstances<IRunAtStartup>())
            {
                task.Execute();
            }
        }
    }
}

Then, in the Application_BeginRequest , Application_EndRequest , and Application_Error , you access the container via the DependencyResolver. 然后,在Application_BeginRequestApplication_EndRequestApplication_Error ,您可以通过DependencyResolver访问容器。 eg: 例如:

public void Application_BeginRequest()
{
    foreach (var task in DependencyResolver.Current.GetServices<IRunOnEachRequest>())
    {
        task.Execute();
    }
}

Notice there is no more Container property and subsequently no nested containers (except that one in startup). 请注意,这里没有更多的Container属性,并且随后没有嵌套的容器(启动时除外)。 That's because your question is more nuanced than just pipeline timings. 那是因为您的问题比管道计时更细微。 The container and transaction per request patterns are really about lifecyles (specifically Transient lifecycles) which, in ASP.NET, the container already knows to resolve per request. 容器和按请求的事务模式实际上是关于生命周期 (特别是瞬态生命周期)的,在ASP.NET中,容器已经知道要解决每个请求。 So you don't need to duplicate that effort. 因此,您无需重复这项工作。 The StructureMap documentation explains it better than I can. StructureMap文档比我能更好地解释它。

Side note : There is a PreApplicationStart attribute you can put on an assembly which indicates what method to run before Application_start, but because it's a static method, it's really only good for static configurations - which an IoC container is not. 旁注 :您可以在程序集中放置一个PreApplicationStart属性,该属性指示在Application_start之前要运行哪种方法,但是由于它是静态方法,因此它实际上仅对静态配置有用-IoC容器不是。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM