C# 每个"organization"的不同实例/SaaS 的自定义代码



我正在写一个c# ASP. net程序。. NET API)程序,打算用作SaaS。该程序具有标准功能,但旧的(非saas)程序也为每个客户提供了一些用于连接其系统的自定义代码。导致每个客户的代码版本不同。

我对此的一个解决方案是使用松耦合。但是我怎样才能使它在运行时调用接口的不同实例呢?最好不要使用switch-case/if-tree。

对这个解决方案的任何帮助都会很好,但我也想知道这是要走的路吗?

在其他编程语言中,例如javascript,您可以加载并运行JIT代码。另一个连接的解决方案是为需要连接到客户系统的部分使用微服务。通过这样做,我可以独立于主程序更新连接器。

您可以在依赖注入容器中连接实现,并在运行时选择实现。

假设您有一个IDiscountCalculator接口,每个客户需求有几个不同的实现。

您将从请求中获得当前租户/客户信息,并决定需要运行哪个实现,然后返回适当的实现。

以这个例子为例(我将抽象级别保持到最低)

services.AddScoped<IDiscountCalculator>(provider =>
{
// find the tenant
var user = provider.GetRequiredService<IHttpContextAccessor>().HttpContext!.User;
var tenantId = user.FindFirstValue("tenant_id");
var tenant = provider.GetRequiredService<AppDbContext>().Tenants.Single(t => t.Id == tenantId);

// return the implementation based on tenant's preferences
return tenant.DiscountType switch
{
typeof(SpecialDiscountCalculator).Name => provider.GetRequiredService<SpecialDiscountCalculator>(),
// ... other implementations
// default to standard discount
_ => provider.GetRequiredService<StandardDiscountCalculator>();
};
});

为了更好地测量,您可能会将其抽象为工厂DiscountCalculatorFactory:

class DiscountCalculatorFactory
{
private IPrincipalAccessor _principalAccessor;
private AppDbContext _dbContext;
public DiscountCalculatorFactory(AppDbContext dbContext, IPrincipalAccessor principalAccessor)
{
_dbContext = dbContext;
_principalAccessor = principalAccessor;
}
public async Task<IDiscountCalculator> CreateAsync()
{
var user = _principalAccessor.Principal;
var tenantId = user.FindFirstValue("tenant_id");
var tenant = await _dbContext.Tenants.SingleAsync(t => t.Id == tenantId);

return tenant.DiscountType switch
{
typeof(SpecialDiscountCalculator).FullName => provider.GetRequiredService<SpecialDiscountCalculator>(),
// ... other implementations
// default
_ => provider.GetRequiredService<DefaultDiscountCalculator>();
};
}
}
public interface IPrincipalAccessor {
ClaimsPrincipal? Principal { get; }
}
public class HttpPrincipalAccessor : IPrincipalAccessor
{
private IHttpContextAccessor _httpContextAccessor;
public HttpPrincipalAccessor(IHttpContextAccessor httpContextAccessor)
{
_httpContextAccessor = httpContextAccessor;
}
public ClaimsPrincipal? Principal => _httpContextAccessor?.HttpContext?.User;
}

最新更新