【问题标题】:MVC3 - multi tenant applicationMVC3 - 多租户应用程序
【发布时间】:2012-03-03 19:10:23
【问题描述】:

我正在构建一个多租户 MVC3 应用程序。 何时 建立租户上下文的最佳做法是什么?

我最初考虑在应用程序启动时使用依赖注入,但这不起作用。在应用程序启动时,我知道我可以绑定“应用程序”上下文(或主查找数据库),因为这只会由服务器环境更改。但是租户上下文可以在逐个请求的基础上发生变化,并且应该通过加密的 cookie 或 http 会话进行持久化,我想。我不认为 TempData、ViewData、ViewBag 在这里对我有用。

所以我的问题是,对于每个请求,我都需要验证租户上下文是否存在。如果是这样,请从持久性机制中获取它。否则建立它。应该在 MVC 管道中的哪个点进行检查?

我是否应该创建一个默认控制器,一个提供租户检查/建立的操作过滤器,并用操作过滤器装饰控制器,然后让每个控制器都从默认控制器派生?

【问题讨论】:

  • @Dangerous 的回答非常深入。我看到了很多不同的多租户实现,对于非程序员来说,要么很难开始工作,要么很难理解。这似乎是一个受欢迎的要求,并且(如果我可以这么大胆地说)乞求直接在 ASP.net MVC 或开源项目中实现。我知道有多种方法可以做到这一点,但开源可以解决一种方法,只是一个简单的框架,允许用户连接他们的“应用程序”。并不是说我们应该有另一个开源 CMS(比如 Orchard - 这很棒)。
  • -基于 MVC 2 和 Spark 视图引擎的开源:github.com/zowens/Multi-tenancy-Samplegithub.com/andycwk-tp/Multi-tenancy-Sample(从上面分叉)-基于 MVC 3 和 Razor/Webforms 视图引擎的开源:code.google.com/p/multimvc跨度>

标签: asp.net-mvc-3 multi-tenant


【解决方案1】:

使用 Ninject,您可以逐个请求地使用依赖注入来解析请求所针对的租户。

我这样做的方法是使用 Nuget 将 NinjectMVC3 添加到我的项目中,然后添加一个 App_Start/NinjectMVC3 类。此类包含一个 RegisterServices(IKernel kernel) 例程,您可以在其中注册您的依赖项。

我指定在一个模块中而不是直接在这个例程中加载我的依赖项:

private static void RegisterServices(IKernel kernel)
{
    kernel.Load(new TenantConfigurationModule());
}

然后将模块指定为:

public class TenantConfigurationModule : NinjectModule
{
    public override void Load()
    {
        IEnumerable<ITenantConfiguration> configuration = //Instantiate a list of configuration classes from where they are stored.

        //Initialise a ninject provider to determine what configuration object to bind to on each request.
        TenantConfigurationProvider provider = new TenantConfigurationProvider(configuration);

        //And then bind to the provider specifying that it is on a per request basis.
        Bind<ITenantConfiguration>().ToProvider(provider).InRequestScope();
    }
}

基本配置类可以指定为:

public interface ITenantConfiguration
{
    string TenantName { get; }

    IEnumerable<string> UrlPaths { get; }

    //whatever else you need for the tenant configuration
}


public abstract class TenantConfiguration : ITenantConfiguration
{
    public string TenantName { get; protected set; }

    public IEnumerable<string> UrlPaths { get; protected set; }
}

然后指定实际配置:

public class TenantOneConfiguration : TenantConfiguration
{
    public MVTTenantConfiguration()
    {
        TenantName = "MVT";
        UrlPaths = new string[] { "http://localhost:50094" }; //or whatever the url may be
    }
}

public class TenantOneConfiguration : TenantConfiguration
{
    public MVTTenantConfiguration()
    {
        TenantName = "MVT";
        UrlPaths = new string[] { "http://localhost:50095" };
    }
}

然后可以编写提供程序:

public class TenantConfigurationProvider : Provider<ITenantConfiguration>
{
    private IEnumerable<ITenantConfiguration> configuration;

    public TenantConfigurationProvider(IEnumerable<ITenantConfiguration> configuration)
    {
        if (configuration == null || configuration.Count() == 0)
        {
            throw new ArgumentNullException("configuration");
        }

        this.configuration = configuration;
    }

    protected override ITenantConfiguration CreateInstance(IContext context)
    {
        //Determine the request base url.
        string baseUrl = string.Format("{0}://{1}", HttpContext.Current.Request.Url.Scheme, HttpContext.Current.Request.Url.Authority);

        //Find the tenant configuration for the given request url.
        ITenantConfiguration tenantConfiguration = configuration.Single(c => c.UrlPaths.Any(p => p.Trim().TrimEnd('/').Equals(baseUrl, StringComparison.OrdinalIgnoreCase)));

        if (tenantConfiguration == null)
        {
            throw new TenantNotFoundException(string.Format("A tenant was not found for baseUrl {0}", baseUrl));
        }

        return tenantConfiguration;
    }
}

然后您可以根据需要将配置注入到控制器、视图、属性等中。

以下是一些有用的链接:

通过继承视图类在视图中使用依赖注入:see link

要查看描述和多租户示例应用程序:see link

zowens 的示例有很多内容,但在 asp.net mvc 中实现多租户并不是一项简单的任务。这个例子提供了一些很好的想法,但我用它作为实现我自己的基础。此示例将每个租户配置存储在单独的 c# 项目中,然后在启动时搜索配置(您可以为此使用反射)以查找要使用的所有租户。然后,每个租户配置项目可以存储特定于该租户的设置、视图、覆盖控制器、css、图像、javascript,而无需更改主应用程序。此示例还使用 StructureMap 进行依赖注入。我选择了 Ninject,但你可以使用任何你喜欢的东西,只要它在请求的基础上解决。

此示例还使用 Spark View 引擎,以便可以轻松地将视图存储在其他项目中。我想坚持使用剃刀视图引擎,但这有点棘手,因为必须预编译视图。为此,我使用了 David Ebbos razor 生成器,它是一个出色的视图编译器,提供预编译的视图引擎:see link

注意:如果您确实尝试在单独的项目中实现视图,那么正确实现视图引擎可能会非常棘手。我必须实现自己的视图引擎和虚拟路径工厂才能使其正常工作,但这是值得的。

此外,如果您在单独的项目中实施资源,那么以下链接可能是有关如何从这些项目中检索信息的有用建议:see link

我希望这也有助于在您的 mvc3 应用程序中实现多租户。不幸的是,我自己没有可以上传的示例应用程序,因为我的实现包含在工作项目中的实现中。

【讨论】:

  • 嗨@Dangerous,如果您能分享有关如何为我的自定义视图引擎创建自定义虚拟路径工厂的知识,那将是非常棒的。
猜你喜欢
  • 1970-01-01
  • 2018-08-29
  • 2011-05-05
  • 2015-08-17
  • 1970-01-01
  • 2013-12-23
  • 1970-01-01
  • 2014-07-27
  • 2012-07-24
相关资源
最近更新 更多