Какова цель метода расширения CreatePerOwinContext в реализации OWIN Microsoft?

Я новичок в ASP.NET и в настоящее время изучаю идентификатор ASP.NET. Я знаю, что он построен на вершине внедрения OWIN Microsoft, и я также все еще это изучаю. Итак, я встретил метод расширения CreatePerOwinContext в стартовом коде Owin, и я не вижу ясной цели его использования. Это какой-то контейнер для инъекций зависимости? Какова реальная цель метода? В каком случае это должно быть применено?

Ответ 1

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

Ответ 2

Какова реальная цель метода? В каком случае это следует применять?

Чтобы ответить на ваш вопрос более прямо, это бесполезно.

  1. Это своего рода фабрика IoC, которую некоторые люди любят использовать.
  2. Этот заставляет вас использовать их (IoC) по вашему выбору.
  3. (Мне не нравится IoC, это похоже на антишаблон для людей, которые хотят чувствовать себя тепло и нечетко и используют термин "архитектура".)
  4. А если серьезно, этот шаблон не взаимодействует с IoC, это статические фабричные функции IoC! Кто это был? Почему бы просто не использовать функцию Factory самостоятельно? Теперь вы должны запомнить (Google) дополнительный вызов API, и когда вы нажимаете F12 на Get, это не приносит вам никакой пользы.

Что вы должны делать вместо этого?

Лично я фанат использования ОО для этого, помнишь ОО? Хутор Пепперидж помнит. С 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- идентичность /

По сути, UserManager и все они созданы для такой структуры. Проверки безопасности выполняются в конвейере, так почему бы не связать однозначный запрос с запросом, чтобы уменьшить потери? Потому что это скрыто.

Я все еще рекомендовал бы создать свой собственный экземпляр контекста db на базовом классе, это делает его намного более чистым в использовании. Если вы действительно хотите, у вас может быть свойство в базовом классе, которое извлекает синглтон из OwinContext.

Сколько времени мы тратим на разработку этих причудливых API и атрибутов Authorize и т.п., Когда все, что мы хотим сделать, это:

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

Очевидно, я предпочитаю подробный код, который вы видите.

Обновление 2

Контексты EF не должны восприниматься как синглтоны и, конечно, не через IoC или шаблон репозитория.

В общем, да, IoC может быть хорош в ситуациях. Но специально для dbContext? Нет.

1) Контексты EF DB - это единица работы, они должны быть недолговечными. Если вы продолжите их работу в течение длительного времени, кэш объектов будет замедлять запросы, а обновления/вставки в базовую базу данных будут выполняться медленнее. Это разработано, чтобы иметь короткий срок службы. 2) Кроме того, контексты EF уже слабо связаны. Вы можете изменить СУБД за контекстом в строке соединения, вы даже можете использовать только память. 3) EF имеет LINQ, который очень гибок, выразителен и безопасен для типов. 4) База данных не является сервисом бизнес-уровня для IoC, это инструмент, который сервисы используют для связи с базой данных. Возможно, у вас может быть какой-то сервис IEmail, доступ к которому осуществляется через IoC. Но он должен обращаться к внутренней базе данных, используя новый контекст EF, который удаляется сразу после завершения запросов. 5) Учитывая 1-4 выше, мы, конечно же, не хотим, чтобы какие-либо промежуточные уровни интерфейса (Service или Repository) испортили все преимущества использования EF.

Ответ 3

вы можете использовать typeof, чтобы получить такое имя:

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