У меня есть ASP .Net Core 2.2 Web API с концентратором SignalR.
Когда API получает сообщение от клиента, ему необходимо сохранить это сообщение в базе данных. Это делается следующим образом:
Концентратор SignalR:
public class ChatHub : Hub
{
public async Task SendMessageToGroup(int clientId, int groupName, string message)
{
await SaveMessage(clientId, groupName, message);
await Clients.Group(groupName).SendAsync("ReceiveMessage", message);
}
private async Task<bool> SaveMessage(int clientId, string groupName, string message)
{
using (var scope = _serviceProvider.CreateScope())
{
var dbContext = scope.ServiceProvider.GetRequiredService<TenantContext>();
Message newMessage = new Message()
{
Message = message,
GroupName = groupName,
Timestamp = DateTime.Now
};
dbContext.Messages.Add(pwMessage);
dbContext.SaveChanges();
}
return true;
}
}
Все будет хорошо, за исключением того факта, что это мультитенантное приложение. Обычно, когда клиент вызывает методы контроллера API с использованием HTTP-запросов, клиент отправляет через заголовок «TenantId» каждый запрос. Затем у меня есть промежуточное программное обеспечение, которое перехватывает этот запрос, извлекает TenantId из заголовка, вызывает службу для извлечения этого Tenant с использованием tenantId и сохраняет объект Tenant в HttpContext. Затем в методе OnConfiguring () объекта DbContext я использую этот объект арендатора (хранящийся в HttpContext), чтобы установить connectionString dbContext для любой базы данных, которую использует этот арендатор. Итак:
Middleware:
public class TenantIdentifier
{
private readonly RequestDelegate _next;
public TenantIdentifier(RequestDelegate next)
{
_next = next;
}
public async Task Invoke(HttpContext httpContext)
{
string tenantId = httpContext.Request.Headers["tenantId"].FirstOrDefault();
Tenant tenant = await GetTenant(tenantId);
httpContext.Items["Tenant"] = tenant;
await _next.Invoke(httpContext);
}
}
DbContext.cs:
public TenantContext(DbContextOptions<TenantContext> options) : base(options)
{
}
public TenantContext(DbContextOptions<TenantContext> options, IHttpContextAccessor httpContextAccessor) : base(options)
{
_httpContextAccessor = httpContextAccessor;
}
protected override async void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
Tenant tenant = (Tenant)_httpContextAccessor.HttpContext.Items["Tenant"];
string connectionString = $"server={tenant.DbUrl};user id={tenant.DbUserName};Pwd={tenant.DbPassword};database={tenant.DbName};persistsecurityinfo=True;TreatTinyAsBoolean=false";
optionsBuilder.UseMySql(connectionString);
}
Теперь, когда клиент вызывает концентратор SignalR, и я создаю новую область в концентраторе и запрашиваю DbContext, строка подключения имеет значение null. Это происходит потому, что, в отличие от HTTP-запроса, вызов концентратора SignalR не вызывает промежуточное программное обеспечение (которое отвечает за идентификацию арендатора)
Как я могу, при запросе DbContext из области, вручную передать ему строку подключения, вместо того, чтобы полагаться на нее, чтобы попытаться сгенерировать connectionString в событии OnConfiguring () (которое не будет работать)
Надеюсь, это имеет смысл: / Спасибо