我一直在设计一个使用多租户(SaaS)服务器 - 客户端架构设计应用程序的相当艰巨的任务。我已经考虑过服务器的内部架构了一段时间了,这就是我所拥有的:
我想让服务器在WCF或MVC4中实现WebAPI。
服务器将具有基于插件的架构,使用MEF作为协调器。由于应用程序有一些要求根据租户要求更改业务逻辑,我正在考虑这样做。更具体地说,我将有一个类/功能,我用MEF导出,在类中,我将能够为其托管它的WebAPI路由添加元数据属性。显然,插件可能需要在某些情况下扩展,甚至可能被其他插件使用,在这种情况下,他们没有路由。
此时服务器的主要问题是,我在某种程度上考虑了插件架构,插件应该能够处理插件所需的所有内容。所以在持久性方面,如果开发了一个新的插件,我应该能够在插件中编写一些代码,然后由DAL执行,以便为插件的数据提供持久性。我不希望插件在任何情况下都经过DAL并触摸数据库,因为多租户和访问的约束需要来自某个地方。从本质上讲,我失去了如何在我的插件中定义一些持久性的能力,如果说DAL在EF上运行的话。我想过将“CreatePersistentStorage(tenant)”这样的东西放到插件的界面中,通过创建表来完全控制插件。我相信Orchard会做这样的事情,但会以更加强大的方式。
我需要帮助我如何实现可以在插件中扩展的DAL。 DAL必须控制最终创建,因此插件的数据表是在正确的租户下创建的。
也许EF不是要走的路?关于您认为值得赞赏的评论/经验。你也会在这个时候去WCF WebAPI或ASP.NET WebAPI吗?
祝你好运, 拉里
答案 0 :(得分:0)
好的,这就是我目前所拥有的,它提供了多租户,它提供了插件 - 清洁/优雅而不是那么多。
public class Context : DbContext
{
private static ConcurrentDictionary<string, DbCompiledModel> modelCache =
new ConcurrentDictionary<string, DbCompiledModel>();
private Context(DbCompiledModel model) : base(model)
{
}
public static Context Create(string tenantSchema)
{
var compiledModel = modelCache.GetOrAdd(
tenantSchema,
t =>
{
var providerInfo =
new DbProviderInfo("System.Data.SqlClient", "2008");
var modelBuilder = new DbModelBuilder();
modelBuilder.Conventions.Remove<IncludeMetadataConvention>();
foreach (var plugin in ServiceLocator
.Current
.GetAllInstances<IPlugin>().ToList())
{
plugin.SetupPersistence(modelBuilder,tenantSchema);
}
var model = modelBuilder.Build(providerInfo);
return model.Compile();
});
return new Context(compiledModel);
}
/// <summary>
/// Creates the database and/or tables for a new tenant
/// </summary>
public static void ProvisionTenant(string tenantSchema)
{
using (var ctx = Create(tenantSchema))
{
if (!ctx.Database.Exists())
{
ctx.Database.Create();
}
else
{
try
{
var createScript = ((IObjectContextAdapter)ctx)
.ObjectContext
.CreateDatabaseScript();
ctx.Database.ExecuteSqlCommand(createScript);
}
catch (Exception ex)
{
Console.WriteLine("Tenant already exists.");
}
}
}
}
}
以下是插件的外观:
[Export(typeof(IPlugin))]
public class Class2 : IPlugin
{
public int Id { get; set; }
public string Name { get; set; }
public string Lol { get; set; }
public void SetupPersistence(DbModelBuilder modelBuilder, string tenantSchema)
{
modelBuilder.Entity<Class2>().ToTable("Keke", tenantSchema);
modelBuilder.Entity<Class2>().HasKey(i => i.Id);
}
}
以下是您可以使用这些内容的方法:
Database.SetInitializer<Context>(null);
Context.ProvisionTenant("personal");
Context.ProvisionTenant("work");
using (var db = Context.Create("personal"))
{
db.Set<Class2>().Add(new Class2 { Name = "Keke" });
db.SaveChanges();
foreach (var d in db.Set<Class2>())
{
Console.WriteLine(d.Name);
}
Console.Read();
}
我正在使用CommonServiceLocator来保留我的MEF参考。
现在,这有几个问题:
如何处理迁移,或者如何从上下文中显式定义插件的表名,我们对任何输入都表示赞赏。