2016-08-26 12 views
0

私は現在、ベン・フォスターズとSaaskitと仲良くしています。データをシードする際にAppTenantがnullになるのはなぜですか?

私はAppTenantIdプロパティでApplicationUserを拡張し、ユーザーを識別するためにAppTenantを使用するカスタムUSERSTOREを作成しました:

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はnullですされています。 AppTenantは、上記USERSTOREのコンストラクタにヌルとして渡されるので、私は、System.InvalidoperationExceptionに直面しています

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

コードが最終的に到達したとき。

私は間違っていますか?間違ったやり方をしているのですか?

更新:は 今の私はバール・アプローチをとっている、のUserManagerを回避し、モックAppTenantとUSERSTOREの私自身のインスタンスを作成:

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

これはうまくいかないでしょう。この例では、 'new'キーワードを使って手動で' ApplicationDbContext'をインスタンス化します。しかし、次の行では 'ApplicationDbContext'に依存する' UserManager 'を解決します。ユーザ管理は 'ApplicationDbContext'の別のインスタンスを受け取ります。' user'はまだ作成されていません( 'SaveChanges'メソッドは' CreateAsync'の後に呼び出されるため) – Tseng

+0

あなたは'ApplicationDbContext'をIoCコンテナから解決する必要がありますしかし、注意する必要があります。アプリ起動時にこれを行うとき。シングルトンを作成するApplicationServices(アプリケーションの起動時にリクエストはありません)。まずスコープを作成し、スコープから解決し、スコープを最後に配置する必要があります。 – Tseng

答えて

2

Saaskitを使用する場合、HttpContextに基づいてTenantContext<T>を設定する方法を決定するAppTenantResolverを設定します。次に、検索されたTenantContext<T>をのItemsプロパティに格納します。これはスコープレベルのキャッシュであるため、テナントは要求の期間だけそこに保存されます。

AppTenantをクラスに挿入すると、クラスをHttpContext.Itemsから解決しようとします。テナントが見つからない場合は、代わりにヌルを挿入します。

SeedData.Initialize(app.ApplicationServices)を呼び出すと、要求のコンテキストにないため、AppTenantResolverミドルウェアが実行されず、AppTenantが解決されません。

残念ながら、コードの詳細がわからないため、問題の解決方法を正確に伝えるのは難しいです。 SeedDataメソッドに新しいScopeを作成し、そのスコープ内のAppTenantを解決して、その後のIoCの呼び出しで挿入できるようにする必要があります。

+0

。私はこれのためのgitリポジトリを作成しました。これは、単にstackoverflow上の断片の代わりに、コードを頑丈にすることを容易にするはずです。 https://github.com/Servellia/Saas-Demo/tree/master/src/RecruitmentPortal.Web – Marco

+0

私はあなたのコードを見てきました。私は基本的にあなたが達成しようとしていることは、リクエストのコンテキスト内にない場合に可能です。 2つの異なるAppTenantのユーザーを挿入する場合はどうでしょうか?SeedDataクラスのDIを通じてAppTenantを解決することはできません。私はあなたが提供する編集がおそらく最も良い解決策だと思います。 – Sock

関連する問題