ASP.NET Boilerplate, тестовые примеры с реальными базами данных

Я использую шаблон asp.net для проекта для ASP.NET 5.x. Я пытаюсь создать тестовые примеры для этого проекта, которые будут вызывать существующие тестовые базы данных (одна база данных для хоста, а другая база данных для клиента). Мои шаги до сих пор заключались в следующем:

  1. В конструкторе класса TestBase я вызываю метод, который будет работать так же, как MultiTenantMigrateExecuter.Run(), который будет заполнять данные для базы данных Test Host и базы данных Test Tenant (я буду использовать базу данных хоста и одну базу данных Tenant для тестирования). Посев будет таким же, как и для реальной БД, только с другими именами для тестовых БД.
  2. Также из конструктора класса TestBase я получаю TenantId из базы данных хоста.
  3. Затем я пытаюсь получить любого заполненного пользователя из базы данных клиента следующим образом: var user= UsingDbContext(context => context.Users.FirstOrDefault(t => t.UserName== "johndoe")); но, конечно, это будет вызывать HostDb, а не TenantDb.

Я нашел способ сделать вызов TenantDb, заключив код в оператор using, подобный этому, избегая context и используя репозиторий, чтобы иметь возможность получить нужного мне пользователя из TenantDb:

using (this.AbpSession.Use(tenant.Id, null))
{
    // get the TenantDb.User here by using the User repository
}

... а затем так в каждом тестовом примере, который я пишу:

using (this.AbpSession.Use(AbpSession.TenantId, AbpSession.UserId))
{
    // make calls to the Tenant database here by using Tenant repository
}

но это не самое чистое решение, и оно имеет свои ограничения.

Возникает вопрос: есть ли лучший способ в моем случае установить в классе TestBase контекст для выполнения вызовов по умолчанию в базе данных Tenant, а не в базе данных Host?

Я тоже пробовал, но не работает ...

protected T UsingTenantDbContext<T>(Func<TestAppDbContext, T> func)
{
    T result;

    using (this.AbpSession.Use(AbpSession.TenantId, AbpSession.UserId))
    {
        using (var context = LocalIocManager.Resolve<TestAppDbContext>())
        {
            context.DisableAllFilters();
            result = func(context);
            context.SaveChanges();
        }
    }

    return result;
}

person John    schedule 03.07.2017    source источник


Ответы (1)


Поигравшись с кодом, я нашел ответ на свой вопрос ... В классе TestBase я создал новое статическое свойство:

internal static MyAppDbContext tenantContext;

Статический, потому что этот класс будет унаследован несколько раз, но tenantContext должен быть установлен только один раз.

Далее я создал следующий метод:

protected void CreateTenantDbContext()
{
    if (tenantContext == null)
    {
        using (var context = LocalIocManager.Resolve<MyAppDbContext>())
        {
            // AbpSession.TenantId is set in a previous method.
            // Usin the host context, get the Connection String for the necessary tenant
            var encryptedDbConnString = context.Tenants.FirstOrDefault(x => x.Id == AbpSession.TenantId)?.ConnectionString;

            // Decrypt the string
            var decryptedDbConnString = SimpleStringCipher.Instance.Decrypt(encryptedDbConnString);

            // Create the context for the tenant db and assign it to the static property tenantContext
            tenantContext = LocalIocManager.Resolve<MyAppDbContext>(new { nameOrConnectionString = decryptedDbConnString });
        }
    }
}

После создания вы можете использовать его в своих тестовых примерах.

person John    schedule 03.07.2017