multitenancy应用程序是由多个组织(医疗机构,律师事务所......)共享的应用程序,而每个组织又拥有自己的用户。他们都登录了一个集中的环境。
要在应用程序中进行标识,必须在URL中表示组织。有两种主要的URL形式。子域名和文件夹:
[tenancy_name].appname.com/projects/view/123
www.appname.com/[tenancy_name]/projects/view/123
起初我尝试了第二个,因为此解决方案不涉及处理DNS。但问题是:每次开发人员需要表达网址(@Html.Action
或@Url.Action
)时,都必须明确传递[tenancy_name]
。这为开发增加了不必要的开销。可能的解决方法是实现这些HTML帮助程序的自定义版本,这些版本会自动考虑租赁名称。我正在考虑这个选择,但寻找更加明确的东西。我还意识到ASP.NET MVC会自动传递传出URL的路由值,但前提是控制器和操作与当前路由值相同。如果总是传递路线值,那就太好了。
要实现第一个选项,子域名,我想,我需要一些第三方DNS管理器。我听说过DynDNS并看了一眼,但我还不清楚它们是如何工作只是看他们的网站。我是否需要触发网络服务,告诉他们每次创建新租约时都要创建另一个子域?他们是否支持DNS中的通配符?它们是否可以在Windows Azure或共享主机上运行?
我在这里寻找方向。我应该走哪条路?
答案 0 :(得分:1)
答案 1 :(得分:0)
在我们的应用程序中使View视图解析变得微不足道:
使用方法: 对于您需要为特定租户重载的视图 - 将它们视为与自定义显示模式相同的方式: 以下工作:
Index.cshtml
Index.cust2.mobile.cshtml
或
Partials/CustomerAgreement.cust1.cshtml
Partials/CustomerAgreement.cust2.cshtml
据我记得,显示/编辑器模板的工作方式也相同
已知问题: 1.您必须为主要+辅助的所有组合创建布局(无论MVC原因是什么) 2.无论重塑者对显示模式的支持是什么 - 它都不支持“。”作为显示模式名称的一部分(这是跟踪进度的问题http://youtrack.jetbrains.com/issue/RSRP-422413)
//put in application start --------
DisplayModeProvider.Instance.Modes.Clear();
foreach (var displayMode in GetDisplayModes())
{
DisplayModeProvider.Instance.Modes.Add(displayMode);
}
private IEnumerable<IDisplayMode> GetDisplayModes()
{
return new CompoundDisplayModeBuilder()
.AddPrimaryFilter(_ => dependencyResolver.GetService(typeof(IResolveCustomerFromUrl)).GetName(),
"cust1",
"cust2")
.AddSecondaryFilter(ctx => ctx.Request.Browser.IsMobileDevice, "mobile")
.BuildDisplayModes();
}
//end of application start part
//and the mode builder implementation:
public class CompoundDisplayModeBuilder
{
private readonly IList<DefaultDisplayMode> _primaryDisplayModes = new List<DefaultDisplayMode>();
private readonly IList<DefaultDisplayMode> _secondaryDisplayModes = new List<DefaultDisplayMode>();
//NOTE: this is just a helper method to make it easier to specify multiple tenants in 1 line in global asax
//You can as well remove it and add all tenants one by one, especially if resolution delegates are different
public CompoundDisplayModeBuilder AddPrimaryFilter(Func<HttpContextBase, string> contextEval, params string[] valuesAsSuffixes)
{
foreach (var suffix in valuesAsSuffixes)
{
var val = suffix;
AddPrimaryFilter(ctx => string.Equals(contextEval(ctx), val, StringComparison.InvariantCultureIgnoreCase), val);
}
return this;
}
public CompoundDisplayModeBuilder AddPrimaryFilter(Func<HttpContextBase, bool> contextCondition, string suffix)
{
_primaryDisplayModes.Add(new DefaultDisplayMode(suffix) { ContextCondition = contextCondition });
return this;
}
public CompoundDisplayModeBuilder AddSecondaryFilter(Func<HttpContextBase, bool> contextCondition, string suffix)
{
_secondaryDisplayModes.Add(new DefaultDisplayMode(suffix) { ContextCondition = contextCondition });
return this;
}
public IEnumerable<IDisplayMode> BuildDisplayModes()
{
foreach (var primaryMode in _primaryDisplayModes)
{
var primaryCondition = primaryMode.ContextCondition;
foreach (var secondaryMode in _secondaryDisplayModes)
{
var secondaryCondition = secondaryMode.ContextCondition;
yield return new DefaultDisplayMode(primaryMode.DisplayModeId + "." + secondaryMode.DisplayModeId){
ContextCondition = ctx => primaryCondition(ctx) && secondaryCondition(ctx)
};
}
}
foreach (var primaryFilter in _primaryDisplayModes)
{
yield return primaryFilter;
}
foreach (var secondaryFilter in _secondaryDisplayModes)
{
yield return secondaryFilter;
}
yield return new DefaultDisplayMode();
}
}