В настоящее время я работаю над мультитенантным приложением, использующим подход Shared DB / Shared Schema. Таким образом, мы обеспечиваем разделение данных клиента, определяя столбец TenantID во всех таблицах. По соглашению все операции чтения / записи SQL должны включать в себя предложение Где TenantID = '?' . Не идеальное решение, но задним числом это 20/20.
В любом случае, поскольку практически каждая страница / рабочий процесс в нашем приложении должна отображать данные, специфичные для арендатора, в самом начале проекта я принял (плохое) решение использовать Singleton для инкапсуляции учетных данных текущего пользователя (т. Е. TenantID и UserID). В то время я думал, что не хочу добавлять параметр TenantID к каждой сигнатуре метода в моем слое данных.
Вот как выглядит основной псевдокод:
public class UserIdentity
{
public UserIdentity(int tenantID, int userID)
{
TenantID = tenantID;
UserID = userID;
}
public int TenantID { get; private set; }
public int UserID { get; private set; }
}
public class AuthenticationModule : IHttpModule
{
public void Init(HttpApplication context)
{
context.AuthenticateRequest +=
new EventHandler(context_AuthenticateRequest);
}
private void context_AuthenticateRequest(object sender, EventArgs e)
{
var userIdentity = _authenticationService.AuthenticateUser(sender);
if (userIdentity == null)
{
//authentication failed, so redirect to login page, etc
}
else
{
//put the userIdentity into the HttpContext object so that
//its only valid for the lifetime of a single request
HttpContext.Current.Items["UserIdentity"] = userIdentity;
}
}
}
public static class CurrentUser
{
public static UserIdentity Instance
{
get { return HttpContext.Current.Items["UserIdentity"]; }
}
}
public class WidgetRepository: IWidgetRepository{
public IEnumerable<Widget> ListWidgets(){
var tenantId = CurrentUser.Instance.TenantID;
//call sproc with tenantId parameter
}
}
Как видите, здесь есть несколько запахов кода. Это синглтон, поэтому он уже не подходит для юнит-тестов. Кроме того, у вас есть очень тесная связь между CurrentUser и объектом HttpContext. По сути, это также означает, что у меня есть ссылка на System.Web в моем слое данных (дрожь).
Я хочу погасить некоторые технические долги в этом спринте, избавившись от этого синглтона по причинам, указанным выше. У меня есть несколько мыслей о том, какой может быть лучшая реализация, но если у кого-то есть какие-либо рекомендации или извлеченные уроки, которыми они могут поделиться, я был бы очень признателен.