Asp.net mvc 如何使用ASP.NET MVC设置多租户应用程序?

Asp.net mvc 如何使用ASP.NET MVC设置多租户应用程序?,asp.net-mvc,dns,multi-tenant,Asp.net Mvc,Dns,Multi Tenant,应用程序是由多个组织(医疗机构、律师事务所等)共享的应用程序,而每个组织都有自己的用户。它们都在一个集中的环境中登录 要在应用程序中标识,必须在URL中表示组织。有两种主要的URL形式。子域和文件夹: [tenancy\u name].appname.com/projects/view/123 www.appname.com/[tenancy\u name]/projects/view/123 起初,我尝试了第二种方法,因为该解决方案不涉及处理DNS。但问题是:每次开发人员需要表达url(@

应用程序是由多个组织(医疗机构、律师事务所等)共享的应用程序,而每个组织都有自己的用户。它们都在一个集中的环境中登录

要在应用程序中标识,必须在URL中表示组织。有两种主要的URL形式。子域和文件夹:

  • [tenancy\u name].appname.com/projects/view/123
  • www.appname.com/[tenancy\u name]/projects/view/123
起初,我尝试了第二种方法,因为该解决方案不涉及处理DNS。但问题是:每次开发人员需要表达url(
@Html.Action
@url.Action
)时,它都必须显式地传递
[tenancy\u name]
。这给开发增加了不必要的开销。一个可能的解决方法是实现这些HTML帮助程序的自定义版本,自动考虑租赁名称。我正在考虑这一选择,但寻找更为严格的未来。我还意识到ASP.NET MVC会自动传递传出URL的路由值,但仅当控制器和操作与当前URL相同时。如果总是传递路由值就好了

要实现第一个选项,子域,我想,我需要一些第三方DNS管理器。我听说了DynDNS,并看了一下,但我认为仅仅看一下他们的网站就不清楚他们是如何工作的。每次创建新租约时,我是否需要触发web服务来告诉他们创建另一个子域?他们支持DNS中的通配符吗?他们在Windows Azure或共享主机上工作吗


我在这里找方向。我应该走哪条路?

看看codeplex上的这个项目,“baseRoute”也许可以帮助你


尊敬。

以下内容使我们的应用程序中的视图分辨率变得微不足道:

如何使用: 对于需要为特定租户重载的视图,请以与自定义显示模式相同的方式对待它们: 以下将起作用:

Index.cshtml
Index.cust2.mobile.cshtml

据我所知,显示/编辑器模板也以同样的方式工作

已知问题: 1.您必须为主要+次要的所有组合创建布局(无论出于何种MVC原因) 2.不管resharper对显示模式的支持怎么说,它都不支持将“.”作为显示模式名称的一部分(这里有一个跟踪进度的问题)

//放入应用程序启动--------
DisplayModeProvider.Instance.Modes.Clear();
foreach(GetDisplayModes()中的var displayMode)
{
DisplayModeProvider.Instance.Modes.Add(displayMode);
}
私有IEnumerable GetDisplayModes()
{
返回新的CompoundDisplayModeBuilder()
.AddPrimaryFilter(=>dependencyResolver.GetService(typeof(IResolveCustomerFromUrl)).GetName(),
“cust1”,
“客户2”)
.AddSecondaryFilter(ctx=>ctx.Request.Browser.IsMobileDevice,“移动”)
.BuildDisplayModes();
}
//应用程序结束开始部分
//以及模式生成器实现:
公共类CompoundDisplayModeBuilder
{
private readonly IList_primaryDisplayModes=new List();
私有只读IList_secondaryDisplayModes=新列表();
//注意:这只是一个帮助器方法,可以更容易地在全局asax的一行中指定多个租户
//您还可以删除它并逐个添加所有租户,尤其是在解析委托不同的情况下
public CompoundDisplayModeBuilder AddPrimaryFilter(Func上下文,参数字符串[]值后缀)
{
foreach(VALUESASASSUFFIES中的var后缀)
{
var=后缀;
AddPrimaryFilter(ctx=>string.Equals(contextEval(ctx)、val、StringComparison.InvariantCultureIgnoreCase)、val);
}
归还这个;
}
public CompoundDisplayModeBuilder AddPrimaryFilter(Func contextCondition,字符串后缀)
{
_添加(新的DefaultDisplayMode(后缀){ContextCondition=ContextCondition});
归还这个;
}
public CompoundDisplayModeBuilder AddSecondaryFilter(Func contextCondition,字符串后缀)
{
_添加(新的DefaultDisplayMode(后缀){ContextCondition=ContextCondition});
归还这个;
}
公共IEnumerable BuildDisplayModes()
{
foreach(在_primaryDisplayModes中为var primaryMode)
{
var primaryCondition=primaryMode.ContextCondition;
foreach(在_secondary显示模式中的var secondary模式)
{
var secondaryCondition=secondaryMode.ContextCondition;
返回新的DefaultDisplayMode(primaryMode.DisplayModeId+“”+secondaryMode.DisplayModeId){
ContextCondition=ctx=>primaryCondition(ctx)和&secondaryCondition(ctx)
};
}
}
foreach(在_primaryDisplayModes中的var primaryFilter)
{
收益-收益初级滤波器;
}
foreach(var secondaryFilter在_secondaryDisplayModes中)
{
二次回油过滤器;
}
返回新的DefaultDisplayMode();
}
}

当你说“必须表达”时。。。我想你的意思是你想那样做,不是吗?因为你可能有一个多租户应用程序,而不会在路由或URL中反映它。好吧,软的。我认为这样比较好。假设软件是一个项目经理。如果URL中未表示租赁,则URL www.appname.com/projects将根据登录的用户列出不同的项目。如果您有两个帐户,您可能会感到困惑,很难为URL添加书签。事实上,这不是强制性的,但更可取。是的,你对多个帐户的书签有很好的看法。。。在这些情况下,它是有用的。你已经研究过MVC领域了吗?
Partials/CustomerAgreement.cust1.cshtml
Partials/CustomerAgreement.cust2.cshtml
//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();
    }
}