我正在构建一个多租户MVC3应用程序。 建立租户上下文时的最佳做法是什么?
我最初考虑在应用程序启动时使用依赖注入,但这不起作用。在应用程序启动时,我知道我可以绑定“应用程序”上下文(或主查找数据库),因为它只会根据服务器环境进行更改。但是租户上下文可以按请求进行更改,并且应该通过加密cookie或http会话来保留,我想。我不认为TempData,ViewData,ViewBag会在这里为我工作。
所以我的问题是,如果租户上下文存在,我需要验证每个请求。如果是这样,请从持久性机制中获取它。否则建立它。在MVC管道中的哪一点应该检查?
我应该创建一个默认控制器,一个提供租户检查/建立的动作过滤器,并使用动作过滤器装饰控制器,然后让每个控制器都来自默认控制器吗?
答案 0 :(得分:4)
使用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应用程序中实现多租户。不幸的是,我自己没有自己的示例应用程序,因为我的实现包含在工作项目中的实现中。