假设您有一个多租户应用。 Tenant
具有各种属性:
public class Tenant{
public string TenantName {get; set;}
public string TenantUrl {get; set;}
}
这样,当我的服务层发送电子邮件时,我可以执行以下操作:
SendEmail(Tenant.FromEmailAddress, recipientEmailAddress)
这适用于属性。在我的业务逻辑中的许多地方,我遇到了必须考虑特定于租户的行为的情况。一个例子是检索主页的照片:
public List<string> GetPhotoUrls(){
if(currentTenant == TenantA){
// logic to go off to retrieve from one third party
} else if (currentTenant == TenantB){
// totally different logic
} else... // one for each tenant
// do some stuff
// return stuff
}
GetPhotoUrls
是一个简单的例子 - 但在我的业务逻辑中的许多地方都存在类似的情况。我正在寻找一个简单模式,我可以在其中定义和实现特定于租户的逻辑。总体目标是将所有特定于租户的逻辑放在一个位置,这样租户的创建和定义就很容易。
我希望开发人员的经验可以阅读:
public List<string> GetPhotoUrls(){
currentTenant.GetPhotoUrls(); // define this logic on the tenant object somehow
// do some stuff
// return stuff
}
有哪些模式/结构可用于实现此目的?
答案 0 :(得分:0)
在您的案例中使用策略模式。当您看到switch
语句或多个if
语句以简化客户端以便将自定义实现委派给依赖接口时,最好应用该模式。您也可以结合工厂模式使用。为了说明这一点:
public interface ITenant{
List<string> GetPhotoUrls();
}
public class TenantA:ITenant{
public string TenantName {get; set;}
public string TenantUrl {get; set;}
public List<string> GetPhotoUrls(){
//A implementation
}
}
public class TenantB:ITenant{
public string TenantName {get; set;}
public string TenantUrl {get; set;}
public List<string> GetPhotoUrls(){
//B implementation
}
}
public class SomeTenantApp{
public SomeTenantApp(ITenant tenant){
_tenant = tenant;
}
public void DoSomething(){
var urls = _tenant.GetPhotoUrls();
//do something
}
}
public static class TenantFactory{
public static ITenant Create(string id)
{
//logic to get concrete tenant
return concreteTenant;
}
}
class Program
{
static void Main(string[] args)
{
var tenant = TenantFactory.Create("A");
var app = var SomeTenantApp(tenant);
app.DoSomething();
}
}
客户端(SomeTenantApp)不必更改。您将实现委托给拥有逻辑的具体类。
答案 1 :(得分:0)
如果您想构建SaaS,我强烈建议您使用ASP.NET Core和依赖注入来克服多租户问题。
您可以定义租户类:
public class AppTenant
{
public string Name { get; set; }
public string[] Hostnames { get; set; }
}
接下来,您可以从当前请求中解析租户
public class AppTenantResolver : ITenantResolver<AppTenant>
{
IEnumerable<AppTenant> tenants = new List<AppTenant>(new[]
{
new AppTenant {
Name = "Tenant 1",
Hostnames = new[] { "localhost:6000", "localhost:6001" }
},
new AppTenant {
Name = "Tenant 2",
Hostnames = new[] { "localhost:6002" }
}
});
public async Task<TenantContext<AppTenant>> ResolveAsync(HttpContext context)
{
TenantContext<AppTenant> tenantContext = null;
// it's just a sample...
var tenant = tenants.FirstOrDefault(t =>
t.Hostnames.Any(h => h.Equals(context.Request.Host.Value.ToLower())));
if (tenant != null)
{
tenantContext = new TenantContext<AppTenant>(tenant);
}
return tenantContext;
}
}
接线:
public void ConfigureServices(IServiceCollection services)
{
services.AddMultitenancy<AppTenant, AppTenantResolver>();
}
获取当前租户(无论何时需要):
public class HomeController : Controller
{
private AppTenant tenant;
public HomeController(AppTenant tenant)
{
this.tenant = tenant;
}
.
.
.
}
有关详细信息,请查看SaasKit
Building multi-tenant applications with ASP.NET Core (ASP.NET 5)