2016-08-26 5 views
0

Я в настоящее время ходит вокруг с Беном Фостерсом Saaskit.Почему AppTenant null только при посеве данных?

я продлил ApplicationUser со свойством AppTenantId и создал пользовательское UserStore, который использует AppTenant для идентификации пользователя:

public class TenantEnabledUserStore : IUserStore<ApplicationUser>, IUserLoginStore<ApplicationUser>, 
    IUserPasswordStore<ApplicationUser>, IUserSecurityStampStore<ApplicationUser> 
{ 
    private bool _disposed; 
    private AppTenant _tenant; 
    private readonly ApplicationDbContext _context; 

    public TenantEnabledUserStore(ApplicationDbContext context, AppTenant tenant) 
    { 
     _context = context; 
     _tenant = tenant; 
    } 
    /*... implementation omitted for brevity*/ 
} 

Если пользователь регистрируется или входит в, это работает отлично. Правильно установлен AppTenant. Проблема возникает, когда SeedData.Initialize(app.ApplicationServices); вызывается в конце моего Statup.Configure() метода:

public static class SeedData 
{ 
    public async static void Initialize(IServiceProvider provider) 
    { 
     using (var context = new ApplicationDbContext(
      provider.GetRequiredService<DbContextOptions<ApplicationDbContext>>())) 
     { 
      var admin = new ApplicationUser 
      { 
       AppTenantId = 1, 
       Email = "[email protected]", 
       UserName = "Administrator", 
       EmailConfirmed = true 
      }; 

      if(!context.Users.Any(u => u.Email == admin.Email)) 
      { 
       var userManager = provider.GetRequiredService<UserManager<ApplicationUser>>(); 
       await userManager.CreateAsync(admin, "Penis123#"); 
      } 
      context.SaveChanges(); 
     } 
    } 
} 

usermanager это звонит пользовательский userstore, но теперь AppTenant равна нулю. Когда код, наконец, достигает

public Task<ApplicationUser> FindByNameAsync(string normalizedUserName, CancellationToken cancellationToken) 
{ 
    return _context.Users.FirstOrDefaultAsync(u => u.NormalizedUserName == normalizedUserName && u.AppTenantId == _tenant.AppTenantId, cancellationToken); 
} 

Я столкнулся с System.InvalidoperationException, потому что AppTenant передается как нуль в конструкторе упомянутой выше userstore.

Что я делаю неправильно? Я посеял неправильный путь или я забыл что-то фундаментальное здесь?

Update: Сейчас я взял лом-подход, избегал usermanager и создал свой собственный экземпляр userstore с макетом AppTenant:

if (!context.Users.Any(u => u.Email == admin.Email)) 
{ 
    var userStore = new TenantEnabledUserStore(context, new AppTenant 
    { 
     AppTenantId = 1 
    }); 
    await userStore.SetPasswordHashAsync(admin, new PasswordHasher<ApplicationUser>().HashPassword(admin, "VeryStrongPassword123#"), default(CancellationToken)); 
    await userStore.SetSecurityStampAsync(admin, Guid.NewGuid().ToString("D"), default(CancellationToken)); 
    await userStore.CreateAsync(admin, default(CancellationToken)); 
} 

Nontheless, я по-прежнему заинтересован в более чистый подход, который не чувствует себя взломанным.

+0

является частью арендатора данных для посева? т. е. произойдет ли посев для первого арендатора? –

+0

Это не сработает. в этом примере вы создаете экземпляр 'ApplicationDbContext' вручную, используя ключевое слово' new'. Но в следующей строке вы разрешаете 'UserManager ', который зависит от 'ApplicationDbContext'. Управляемый пользователь получит другой экземпляр 'ApplicationDbContext', где' user' еще не создан (поскольку метод 'SaveChanges' вызывается после' CreateAsync' – Tseng

+0

Вам нужно также разрешить ваш''ApplicationDbContext' из контейнера IoC , но будьте осторожны. Когда вы делаете это во время запуска с помощью приложения.ApplicationServices вы создаете singleton (во время загрузки приложения нет запроса). Сначала вам нужно создать область действия, затем устранить ее из области действия и ограничить область действия в конце. – Tseng

ответ

2

При использовании Saaskit вы настраиваете AppTenantResolver, который определяет, как установить TenantContext<T> на основе предоставленного HttpContext. Затем он сохраняет извлеченный TenantContext<T> в свойстве ItemsHttpContext. Это кеш уровня уровня, поэтому арендатор хранится там только на время запроса.

Когда вы вводите AppTenant в класс, он пытается его решить с HttpContext.Items. Если арендатор не найден, тогда вместо него вводится нуль.

Когда вы вызываете SeedData.Initialize(app.ApplicationServices), вы не находитесь в контексте запроса, и поэтому промежуточное программное обеспечение AppTenantResolver не запускается и не разрешит AppTenant.

К сожалению, не имея полной информации о вашем коде, трудно точно сказать, как исправить вашу проблему. Вам нужно будет убедиться, что вы создали новый Scope в своем методе SeedData и разрешите AppTenant в пределах этой области, чтобы последующие вызовы IoC позволяли вставлять его.

+0

Привет, Андрей. Для этого я создал репозиторий git. Это должно упростить прочность кода, а не просто фрагменты здесь в stackoverflow. https://github.com/Servellia/Saas-Demo/tree/master/src/RecruitmentPortal.Web – Marco

+0

Я взглянул на ваш код, и я думаю, что в принципе то, чего вы пытаетесь достичь, не будет возможно, если не в контексте запроса. Вы просто не сможете разрешить AppTenant через DI в своем классе SeedData, поскольку это не имеет никакого смысла - что, если вы хотите вставить пользователей для 2 разных AppTenants? Я думаю, что редактирование, которое вы предоставляете, вероятно, является лучшим решением. – Sock

Смежные вопросы