Я новичок в ASP.NET и в настоящее время изучаю идентификатор ASP.NET. Я знаю, что он построен на вершине внедрения OWIN Microsoft, и я также все еще это изучаю. Итак, я встретил метод расширения CreatePerOwinContext в стартовом коде Owin, и я не вижу ясной цели его использования. Это какой-то контейнер для инъекций зависимости? Какова реальная цель метода? В каком случае это должно быть применено?
Какова цель метода расширения CreatePerOwinContext в реализации OWIN Microsoft?
Ответ 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
Какова реальная цель метода? В каком случае это следует применять?
Чтобы ответить на ваш вопрос более прямо, это бесполезно.
- Это своего рода фабрика IoC, которую некоторые люди любят использовать.
- Этот заставляет вас использовать их (IoC) по вашему выбору.
- (Мне не нравится IoC, это похоже на антишаблон для людей, которые хотят чувствовать себя тепло и нечетко и используют термин "архитектура".)
- А если серьезно, этот шаблон не взаимодействует с 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());