54

I am a newbie in ASP.NET, and currently learning ASP.NET Identity. I know it's built on top of OWIN implementation by Microsoft, and I am also still learning that too. So, I came across the extension method CreatePerOwinContext in the Owin startup code, and I don't see a clear purpose of using it. Is it some kind of dependency injection container? What is the real purpose of the method? In what case it should be applied?

Next Developer
  • 1,249
  • 1
  • 10
  • 20

3 Answers3

70

CreatePerOwinContext registers a static callback which your application will use to get back a new instance of a specified type.
This callback will be called once per request and will store the object/objects in OwinContext so that you will be able to use them throughout the application.

Let's say you have defined your own implementation of 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; }
}

and your implementation of 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);
        }
}

In your Owin Startup you will register the callback:

// IAppBuilder app

app.CreatePerOwinContext<ApplicationDatabaseContext>(ApplicationDatabaseContext.Create);
app.CreatePerOwinContext<ApplicationUserManager>(ApplicationUserManager.Create);

which will call the static method:

public static ApplicationDatabaseContext Create()
{
    return new ApplicationDatabaseContext();
}

and

public static ApplicationUserManager Create(IdentityFactoryOptions<ApplicationUserManager> options, IOwinContext context)
{
    ...
}

Now you will be able to access your database context and user manager in a simple straightforward way:

ApplicationDatabaseContext dbContext = context.OwinContext.Get<ApplicationDatabaseContext>();
ApplicationUserManager userManager = context.OwinContext.GetUserManager<ApplicationUserManager>();

In your ApiController (if you're using WebApi):

IAuthenticationManager authenticationManager = HttpContext.Current.GetOwinContext().Authentication;
ApplicationUserManager applicationUserManager = HttpContext.Current.GetOwinContext().GetUserManager<ApplicationUserManager>();
LeftyX
  • 35,328
  • 21
  • 132
  • 193
  • 3
    Thanks for the explanation. So it looks like some kind of system service locator. In startup code, one registers a service factory by providing a static method which creates the service when first time it is requested, and somewhere in the application the service can be retrieved from OwinContext – Next Developer Oct 29 '14 at 10:48
  • 2
    @NextDeveloper: That's right. Of course, if you're using your own IoC, you can skip that registration and use your own [implementation](https://aspnetidentity.codeplex.com/discussions/532294) – LeftyX Oct 29 '14 at 10:57
  • 2
    Is there a way to retrieve the key, which is used in the OWIN dictionary? If I'm using Nancy, I only have Context.GetOwinEnvironment to get the dictionary. There the objects are keyed with something like this ""AspNet.Identity.Owin:WebApplication2.ApplicationDbContext, WebApplication2, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null". The inital call was app.CreatePerOwinContext(ApplicationDbContext.Create); Who the hell shall know the name? – decades Mar 13 '15 at 13:13
  • @LeftyX , mind clarifying. You and MSDN state that this callback will be invoked *once* per request, however I'm only seeing this being called when the server initializes after the first request. – Aaron Hudon Sep 21 '15 at 02:07
  • @ajhuddy: I can confirm `ApplicationDatabaseContext.Create` is called for each http request. – LeftyX Sep 21 '15 at 10:03
  • Important to notice is that the `HttpContext.Current.GetOwinContext().GetUserManager()` will work only if `Microsoft.AspNet.Identity.Owin` is imported (via `using` statement) – Stoyan Dimov Mar 18 '16 at 09:20
8

What is the real purpose of the method? In what case it should be applied?

To answer your question more directly, this is useless.

  1. It's some sort of IoC factory, which some people like using.
  2. This one makes you use theirs (IoC) over your choice.
  3. (I don't like IoC, it feels like an anti-pattern for people who want to feel warm and fuzzy and use the term "architecture".)
  4. But seriously, this pattern doesn't IoC interfaces, it IoC static factory functions! Who's idea was that? Why not just use the Factory function yourself? Now you have to remember (Google) an extra API call, and when you press F12 on Get, it takes you nowhere helpful.

What should you do instead then?

Personally, I'm a fan of using OO for this, remember OO? Pepperidge farm remembers. With OO, you remain in control, you can debug, log, and you can extend.

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

}

All this could be a waste of time, if someone finds some documentation why Microsoft engineers put this in, they might have a good reason why, but I doubt it, so let's upvote this answer in the meantime.

UPDATE 1

Here's the why, why it's there for Microsoft: https://blogs.msdn.microsoft.com/webdev/2014/02/12/per-request-lifetime-management-for-usermanager-class-in-asp-net-identity/

Basically, the UserManager and all them are built for this kind of structure. The Security checks occur in the pipeline, so why not have a singleton linked to the request, to reduce waste? Because it's hidden.

I would still recommend creating your own instance of the db context on a baseclass, it makes it much cleaner to use. If you really want, you can have a property in your baseclass which retrieves the singleton from the OwinContext.

How much time do we waste trying to work out these fancy APIs, and Authorise attributes and the like, when all we want to do is:

public void DoSomething()
{
   DemandAuthenticated();
   DemandAuthorised(typeof(somethingClass), "DoSomething");
}

Clearly, I prefer verbose code you can see.

Update 2

EF contexts should not be held as singletons, and certainly not through any IoC or repository pattern.

Generally, yes IoC can be good in situations. But specifically for a dbContext? No.

1) EF DB contexts are a unit of work, they should be short-lived. If you keep them running for a long time, the object cache will slow down queries, and updates/inserts to the underlying database get slower. It's designed to have short lifetime. 2) Also, EF contexts are already loosely coupled. You can change the RDBMS behind a context in the connection string, you can even use memory-only. 3) EF has LINQ which is very flexible, expressive, and type safe. 4) Database is not a business-level service for IoC it's a tool that services use to communicate with the database. Perhaps, You might have some kind of service IEmail that is accessed via IoC. But it should access the internal database using a new EF context that is disposed promptly after completion of queries. 5) Given 1-4 above, we certainly don't want any intermediate Interface layers (Service or Repository) to spoil all the benefits of using EF in the first place.

Kind Contributor
  • 17,547
  • 6
  • 53
  • 70
  • 1
    IoC gives you far more control in various ways than directly creating objects that may need to be abstracted out and it's still OO. To the point of use here, it's ugly to me and feels like "poor man's IoC". IoC, when properly implemented, gives you specific types of malleability and utility (e.g. unit testing) which you cannot "easily" obtain by either "poor man's" or direct instantiation. – Hardrada May 03 '19 at 18:34
  • @Hardrada see Update 2 in my answer - it's important that people understand this. – Kind Contributor May 04 '19 at 06:53
  • 1
    1. Yes, 2. Yes, 3. Sure, 4. Fair enough, but the factory you have created on your object still obstructs abstraction and still requires you to have some NON-interfaced class available and coupled. As of 2019, ASP.NET EF Core, you can add dbcontext as a service to inject to controllers and they are short lived (scoped per request lifetime). More flexible and decoupled still. – Hardrada May 24 '19 at 15:18
2

you may use typeof to get the name like this:

HttpContext.GetOwinContext().Get<ApplicationDbContext>(typeof(ApplicationDbContext).ToString());
Andrew
  • 3,648
  • 1
  • 15
  • 29