随着宽带和网络技术的进步,我们正在看到从传统的桌面应用转向基于网络的系统。云是这几天的所有范围。Sage和Quickbooks等会计软件包正在被诸如Kashflow和Wave Apps等在线软件取代。
每个客户并没有创建一个独特的软件实例,而是像Kashflow和Wave Apps一样,将他们的系统开发成多租户应用程序 - 所有用户都使用该软件的一个实例。在每种情况下,它们的数据都是通过系统的体系结构与其他客户的数据分离的。
引用:多租户是指软件架构中的一个原则,其中软件的单个实例在服务器上运行,为多个租户提供服务。租户是一组用户在他们使用的软件上共享相同的观点。 - 维基百科
这可以通过两种方式来实现:
单个数据库 - 创建单个数据库,所有数据都存储在此处。每个记录都分配有一个租户密钥,只有属于该租户的数据才可访问。访问受应用软件的限制。
多个数据库 - 或者,可以使用单独的数据库来存储每个客户数据。然后可以通过使用SQL登录凭据来限制对数据库的访问。
虽然我多次使用单一数据库方法,但在开始最近的一个项目时,很明显多种数据库方法可能更合适。
多数据库方法的主要优点之一是可以备份和恢复单个用户的数据。使用单一的数据库方法,恢复数据库将消除所有客户的变化,并且在单个客户出错时不可能提供回滚功能。
另外,如果网站变得非常成功,那么多数据库系统就可以非常容易地在各个服务器之间移动数据。
然而在我的情况下,主要的卖点是预期一些客户可能需要定制系统,超出了多租户设计所能达到的水平。通过使用单独的数据库,可以将这些数据库移动到新的服务器上,并在需要时进行完全自定义。虽然这可能会首先打破多租户系统的优势,但它确实提供了单一数据库系统不能提供的灵活性和面向未来的优势。
在多数据库多租户系统中,每个用户数据都存储在自己的数据库中。因此需要单独的数据库来保存登录细节,并提供用户数据存储位置的详细信息。这可能指向同一台服务器上的数据库或远程数据位置。
在Visual Studio中,创建一个新的ASP.NET Web应用程序。
选择MVC作为模板类型,在“更改认证”中,确保选择“个人用户帐户”。这个例子我们将使用表单认证。
首先,创建一个名为AccountDAL的文件夹- 我们将使用它来存储访问Account数据存储的所有代码。
创建一个新类,并将其命名为DataContext.cs。添加下面的代码:
public class DataContext : DbContext { public DataContext() : base("accountContext") { } public DbSetAccounts { get; set; } public DbSet Users { get; set; } }
我们将首先使用实体框架生成一个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中,我们也将检查auth模式是否设置为表单身份验证:
<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 ICollectionUsers { 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 RedirectToLocal(returnUrl); } else { ModelState.AddModelError("", "Invalid username or password."); } } // If we got this far, something failed, redisplay form return View(model); }
上面的代码将创建我们的新实例Account DataContext
,并检查与现有用户匹配的用户和密码。如果是这样,我们将设置一个auth cookie,它将登录到用户。
和logout
:
public ActionResult LogOff()
{
FormsAuthentication.SignOut();
Session.Abandon();
}
上面的代码将清除我们之前设置的auth cookie。这将具有将用户登出系统的效果。
如果我们现在运行该项目,我们现在可以以我们在测试数据中创建的两家公司之一登录。所有非常简单。
现在是多数据库方法。
我们创建两个新的数据库,每个公司都有一个。像我们在测试数据的表格中指定的那样,调用它们“ Company1
”和“ Company2
” Account
。在每一个中,创建一个新的表格Jobs
,如下所示:
在每个数据库中添加几个测试作业:
公司1测试数据:
公司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 DbSetJobs { 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() { // get the current user: var accountContext = new AccountDAL.DataContext(); var user = accountContext.Users.FirstOrDefault(x => x.Email == User.Identity.Name); if (user != null) { // now we have the current user, we can use their Account // to create a new DataContext to access system data: var systemContext = new SystemDAL.DataContext(user.Account.Database); return View(systemContext.Jobs); } return View(); }
上面的代码首先创建我们的一个实例Account DataContext
,并获取一个代表当前登录用户的对象。由此,我们可以创建一个System DataContext
实例,传入我们希望连接的数据库的名称。
一旦连接起来,我们就可以把所有公司的工作清单都传递给了View
。
修改Index
视图如下,替换现有的代码:
@model IQueryable<MultiTenancy.SystemDAL.Job> @{ ViewBag.Title = "Home Page"; } <br/> <ul> @if (Model != null) { foreach (var job in Model) { <li>@job.JobName</li> } } </ul>
我们有它 - 一个多租户Web应用程序,它将每个用户的数据存储在一个单独的数据库中!