Мультитенантный .Net Core Web API - PullRequest
0 голосов
/ 04 июня 2018

У меня есть требование для создания веб-API для существующей системы.По всей стране существуют различные агентства, и у каждого агентства есть своя база данных.Все базы данных находятся на одном сервере.Все базы данных идентичны по структуре.Все базы данных имеют свои собственные имя пользователя и пароль.Агентство имеет одного или нескольких пользователей.Пользователь может принадлежать одному или нескольким агентствам.Существует также одна специальная база данных, которая содержит таблицу всех пользователей, таблицу всех агентств и таблицу мостов пользовательских агентств.

В настоящее время они используют традиционное настольное приложение Windows.Когда пользователь настраивает эту программу Windows, он входит в систему с именем пользователя и паролем.Затем система отображает для них список всех агентств, к которым они принадлежат (обычно только одно, но некоторые «опытные пользователи» могут принадлежать нескольким).Они выбирают агентство, а затем программа подключается к правильной базе данных.В течение оставшейся части сеанса все, что делает пользователь, будет выполнено в этой базе данных.

Клиент хочет создать веб-приложение, которое в конечном итоге заменит программу Windows (и эти два будут работать бок о бок длякакое-то время).Один разработчик создает интерфейс на Angular 5, а я разрабатываю API в ASP .Net Core 2.1.

, поэтому веб-приложение будет работать аналогично приложению Windows.Пользователь входит в веб-приложение.Веб-приложение, которое использует мой веб-API, сообщает API-интерфейсу, какой пользователь только что вошел в систему. Затем API-интерфейс проверяет, к какому агентству (-ям) принадлежит этот пользователь, из этой базы данных, в которой хранятся эти данные.API возвращает список агентств, к которым принадлежит пользователь веб-приложения.Там пользователь выбирает агентство.С этого момента веб-приложение будет включать этот идентификатор агентства в заголовок всех вызовов API.API, получая запрос от веб-приложения, будет знать, какую базу данных использовать, основываясь на идентификаторе агентства в заголовке запроса.

Надеюсь, что это имеет смысл ...

Очевидно, это означает, что мне придется менять строку подключения DbContext на лету, в зависимости от того, с какой базой данных должен общаться API.Я смотрел на это, во-первых, делая это на самом контроллере, который работал, но включал много анти-паттернов копирования и вставки во всех моих контроллерах.Поэтому я пытаюсь переместить это в событие OnConfiguring DbContext.Я думал, что было бы лучше создать DbContext Factory для создания DbContexts, используя соответствующую строку подключения.Я просто немного растерялся, хотя.Видите ли, когда веб-приложение вызывает конечную точку веб-API (скажем, HTTP GET-запрос для получения списка учетных записей), это вызовет обработчик HttpGet в контроллере учетных записей.Этот метод действия затем читает заголовок идентификатора агентства.Но все это происходит на контроллере .... Если я вызываю фабрику DbContext из события OnConfiguring () DbContext, она должна будет отправить идентификатор агентства (который был прочитан в контроллере) на фабрику, чтобы фабрика зналакакую строку подключения создать.Я пытаюсь не использовать глобальные переменные, чтобы мои классы были слабо связаны.

Если у меня в конвейере не запущена какая-либо служба, которая перехватывает все запросы, читает заголовок идентификатора агентства, и это каким-то образом вводится в DbContextконструктор?Не знаю, как бы я поступил так ...

В общем, я немного растерялся.Я даже не уверен, что это правильный подход.Я рассмотрел некоторые «мультитенантные» примеры, но, если честно, я нашел их немного сложными для понимания, и я надеялся, что смогу сделать что-то немного проще сейчас, и со временем, насколько мне известно.Net Core улучшается, я могу посмотреть на улучшение кода соответственно.

1 Ответ

0 голосов
/ 04 июня 2018

Я работаю над тем, что вы описали здесь.Так как я тоже в самом начале, у меня пока нет серебряной пули.Хотя есть одна вещь, которая может помочь вам с вашим подходом:

во-первых, выполняя это на самом контроллере, который работал, но включал в себя множество анти-паттернов копирования и вставки во всех моихконтроллеры.

Я использовал подход промежуточного программного обеспечения, отвечающий за обмен строкой dbconnection.Примерно так:

public class TenantIdentifier
{
    private readonly RequestDelegate _next;

    public TenantIdentifier(RequestDelegate next)
    {
        _next = next;
    }

    public async Task Invoke(HttpContext httpContext, GlobalDbContext dbContext)
    {
        var tenantGuid = httpContext.Request.Headers["X-Tenant-Guid"].FirstOrDefault();
        if (!string.IsNullOrEmpty(tenantGuid))
        {
            var tenant = dbContext.Tenants.FirstOrDefault(t => t.Guid.ToString() == tenantGuid);
            httpContext.Items["TENANT"] = tenant;
        }

        await _next.Invoke(httpContext);
    }
}


public static class TenantIdentifierExtension
{
    public static IApplicationBuilder UseTenantIdentifier(this IApplicationBuilder app)
    {
        app.UseMiddleware<TenantIdentifier>();
        return app;
    }
}

Здесь я использую самодельный http-заголовок с именем X-Tenant-Guid для идентификации GUID арендаторов.Затем я делаю запрос в глобальную базу данных, где я получаю строку подключения этого арендатора db.

Я опубликовал пример здесь.https://github.com/riscie/ASP.NET-Core-Multi-Tenant-multi-db-Example (он еще не обновлен до asp net core 2.1, но делать это быстро не должно)

...