随着云计算的普及,越来越多的桌面应用程序转向基于Web的系统。会计软件如Sage和QuickBooks正在被Kashflow和Wave Apps等在线替代品所取代。Kashflow和Wave Apps等系统不是为每个客户创建一个独特的软件实例,而是开发了多租户应用程序——所有用户使用单一的软件实例。在每种情况下,他们的数据通过系统架构与其他客户的数据分离。
多租户是指软件架构中的原则,其中单一的软件实例在服务器上运行,服务于多个租户。租户是一组共享相同软件视图的用户。这可以通过两种方式实现:
单一数据库:创建一个单一的数据库,所有数据都存储在这里。每条记录都被分配一个租户密钥,只有属于该租户的数据才能被访问。访问权限由应用程序软件限制。
多个数据库:或者,可以使用单独的数据库来存储每个客户的数据。然后可以通过使用SQL登录凭据来限制对数据库的访问。
虽然曾经多次使用单一数据库方法,但在最近的一个项目开始时,很明显多数据库方法可能更适合。
多数据库方法的优势:多数据库方法的一个主要优势是它使得备份和恢复单个用户的数据成为可能。在单一数据库方法中,恢复数据库会抹去所有客户的变化,并且如果单个客户犯了错误,就不可能提供回滚功能。此外,如果网站变得非常成功,多数据库系统允许在单独的基础上非常容易地在服务器之间移动数据。
然而,在情况下,主要的卖点是预期一些客户可能需要对系统进行超出多租户设计所能实现的定制。通过使用单独的数据库,如果需要的话,这些数据库可以移动到新服务器,并且可以完全定制。虽然这可能会破坏多租户系统的优势,但它确实提供了灵活性和未来保障,这是单一数据库系统无法提供的。
多数据库系统的架构:在多数据库多租户系统中,每个用户的数据都存储在自己的数据库中。因此,需要一个单独的数据库来保存登录详细信息,并提供用户数据存储位置的详细信息。这可以指向同一服务器上的数据库,也可以指向远程数据位置。
如何使用MVC 6创建多数据库系统:在Visual Studio中,创建一个新的ASP.NET Web应用程序。选择MVC作为模板类型,在“更改身份验证”中确保选择了“个人用户帐户”。将使用表单身份验证作为此示例。
首先,创建一个名为AccountDAL的文件夹,将在这里存储所有访问Account数据存储的代码。
创建一个名为DataContext.cs的新类,并添加以下代码:
public class DataContext : DbContext
{
public DataContext() : base("accountContext")
{
}
public DbSet Accounts { get; set; }
public DbSet Users { get; set; }
}
将使用Entity Framework,代码优先,来生成一个DataContext,它代表存储在Account数据库中的数据。将有两个表:
Accounts:一个帐户代表一个单一的租户。这些数据将包含租户数据存储的位置。每个帐户可以有多个用户。
Users:包含系统所有用户的登录用户名和密码。每个用户都与一个帐户相关联。
在web.config中添加一个连接字符串,以连接到帐户数据库:
<connectionStrings>
<add name="accountContext" providerName="System.Data.SqlClient" connectionString="Server=desktop\SERVER2012; Database=Accounts; Integrated Security=SSPI" />
</connectionStrings>
在web.config中,还将检查身份验证模式是否设置为表单身份验证:
<authentication mode="Forms">
<forms loginUrl="/Account/Login" cookieless="UseCookies" />
</authentication>
接下来,让创建两个类来表示数据库中的表,User.cs和Account.cs:
public class User
{
public int Id { get; set; }
public string Email { get; set; }
public string Password { get; set; }
public string Name { get; set; }
public int AccountId { get; set; }
public virtual Account Account { get; set; }
}
public class Account
{
public int Id { get; set; }
public string Name { get; set; }
public string Database { get; set; }
public virtual ICollection Users { get; set; }
}
现在需要创建数据库。创建一个名为Accounts的新数据库,并添加两个名为Users和Accounts的表,如下所示:
Accounts:
Users:
最后,让对Login和Logout函数进行一些更改,以使用FormsAuthentication:
public ActionResult Login(LoginViewModel model, string returnUrl)
{
if (ModelState.IsValid)
{
var dataContext = new AccountDAL.DataContext();
var user = dataContext.Users.FirstOrDefault(x => x.Email == model.UserName && x.Password == model.Password);
if (user != null)
{
FormsAuthentication.SetAuthCookie(model.UserName, false);
return RedirectToAction(returnUrl);
}
else
{
ModelState.AddModelError("", "Invalid username or password.");
}
}
// 如果走到这一步,说明有东西失败了,重新显示表单
return View(model);
}
上面的代码将创建Account DataContext的新实例,并检查用户和密码是否与现有用户匹配。如果是这样,将设置一个auth cookie,这将登录用户。
和Logout:
public ActionResult LogOff()
{
FormsAuthentication.SignOut();
Session.Abandon();
}
上面的代码将清除之前设置的auth cookie。这将有将用户从系统中注销的效果。
如果现在运行项目,现在可以作为在测试数据中创建的两个公司中的任何一个登录。非常简单。
现在来到多数据库方法。让为每个公司创建两个新数据库。按照在测试数据中的“Account”表中指定的,将它们命名为“Company1”和“Company2”。在每个数据库中,创建一个名为Jobs的新表,如下所示:
Company 1测试数据:
Company 2测试数据:
现在,在Visual Studio中,创建一个名为SystemDAL的文件夹,用于存储所有与实际系统相关的数据对象。
首先,创建一个名为DataContext.cs的新类:
public class DataContext : DbContext
{
public DataContext(string database) : base("Data Source=desktop\\Server2012;Initial Catalog=" + database + ";Integrated Security=True")
{
}
public DbSet Jobs { get; set; }
}
这是实现多数据库逻辑的地方。不会将连接字符串的名称传递给DataContext基础构造函数,而是将使用数据库名称构建自己的,该数据库名称传递给DataContext构造函数。这将从数据库中的Account表中获取。
创建第二个类来表示一个job对象:
public class Job
{
public string JobName { get; set; }
public int Id { get; set; }
}
现在将修改Home\Index()函数以加载当前用户的数据:
[Authorize]
public ActionResult Index()
{
// 获取当前用户:
var accountContext = new AccountDAL.DataContext();
var user = accountContext.Users.FirstOrDefault(x => x.Email == User.Identity.Name);
if (user != null)
{
// 现在有了当前用户,可以使用他们的Account
// 来创建一个新的DataContext以访问系统数据:
var systemContext = new SystemDAL.DataContext(user.Account.Database);
return View(systemContext.Jobs);
}
return View();
}
上面的代码首先创建Account DataContext的实例,并获取代表当前登录用户的对象。从这个,然后可以创建一个System DataContext实例,传入想要连接的数据库的名称。
一旦连接,就可以然后将公司的所有工作传递给View。
@model IQueryable<MultiTenancy.SystemDAL.Job>
@{
ViewBag.Title = "Home Page";
}
<br/>
<ul>
@if (Model != null)
{
foreach (var job in Model)
{
<li>
@job.JobName
</li>
}
}
</ul>