Mat*_*son 9 c# asp.net entity-framework multi-tenant asp.net-mvc-4
我正在使用MVC4和EF5开发一个多租户Web应用程序.我之前曾问过有关过滤我的DbContext的问题:在存储库模式中按ID过滤是不好的做法.
显然,我的方法很合理,但有人建议,不是在单个存储库中处理所有过滤,而是使用'存储库或DBContextWrapper类来提供已经过滤到租户级别的上下文,它将为[my]普通存储库提供信息.
不幸的是我不是MVC专家,所以我尽可能地开始实现这一点,但是在研究其他多租户应用程序的EF过滤时,我发现了一个非常类似案例的问题 多租户Web应用程序带有过滤的dbContext,尽管完全失败了理解答案.
在我的应用程序中,CompanyID是User类的属性,因此应直接从经过身份验证的用户获取.例如:
int CompanyID = db.Users.Single(u => u.Email == User.Identity.Name).CompanyID;
Run Code Online (Sandbox Code Playgroud)
我当前的方法似乎确实有效,但是我很确定我已经以错误的方式解决了问题和/或根据我在其他问题中看到的关于做同样事情的事情而做得不够好.在另一个问题中,使用实体框架反射的简单多租户Web应用程序的解决方案用于执行此操作,但我无法确定它是否适用于我的情况,甚至如何使用它.
如果有人能够解释这个问题的最佳方式,以及不同方式的利弊,我将非常感激.谢谢 :)
我目前的实施如下:
D B
TestController.cs
public class TestController : Controller
{
private BookingSystemEntities db = new BookingSystemEntities();
public ActionResult Index()
{
var user = db.Users.Single(u => u.Email == User.Identity.Name);
IBookingSystemRepository rep = new BookingSystemRepository(db, user);
return View(rep.GetAppointments(false));
}
}
Run Code Online (Sandbox Code Playgroud)
BookingSystemRepository.cs
public class BookingSystemRepository : IBookingSystemRepository
{
private CompanyBookingSystemRepository db;
public BookingSystemRepository(BookingSystemEntities context, User user)
{
this.db = new CompanyBookingSystemRepository(context, user);
}
public IEnumerable<Appointment> GetAppointments()
{ return GetAppointments(false); }
public IEnumerable<Appointment> GetAppointments(bool includeDeleted)
{
return includeDeleted
? db.Appointments
: db.Appointments.Where(a => a.Deleted.HasValue);
}
public IEnumerable<Client> GetClients()
{ return GetClients(false); }
public IEnumerable<Client> GetClients(bool includeDeleted)
{
return includeDeleted
? db.Clients
: db.Clients.Where(c => c.Deleted.HasValue);
}
public void Save()
{
db.SaveChanges();
}
public void Dispose()
{
if (db != null)
db.Dispose();
}
}
Run Code Online (Sandbox Code Playgroud)
CompanyBookingSystemRepository.cs
public class CompanyBookingSystemRepository
{
private BookingSystemEntities db;
private User User;
public IEnumerable<Appointment> Appointments { get { return db.Appointments.Where(a => a.User.CompanyID == User.CompanyID).AsEnumerable<Appointment>(); } }
public IEnumerable<Client> Clients { get { return db.Clients.Where(a => a.CompanyID == User.CompanyID).AsEnumerable<Client>(); } }
public CompanyBookingSystemRepository(BookingSystemEntities context, User user)
{
db = context;
this.User = user;
}
public void SaveChanges()
{
db.SaveChanges();
}
public void Dispose()
{
if (db != null)
db.Dispose();
}
}
Run Code Online (Sandbox Code Playgroud)
与您提供的其他一些示例相比,我更喜欢您的方法。假设每个租户运行相同的代码库和域,基于登录用户的过滤应该是确保正确过滤数据的最有效方法。(如果没有,您也可以利用它们进行过滤。)
如果您担心过滤没有 CompanyID 的表的数据库性能,您可以有意对数据库进行非规范化以在这些表中包含该字段。
您引用的反射方法虽然很优雅,但似乎过于复杂,并且比在数据库调用中包含 CompanyID 的开销要大得多(特别是因为数据库调用在两个实例中都发生)。
编辑(评论后):
至于其余部分,您似乎编写了很多不必要的多余代码(至少在上面引用的示例中)。我不一定明白为什么您要区分 BookingSystemRepository 和 CompanyBookingSystemRepository,因为从您的代码来看,前者的存在似乎只是为了将调用传递给后者,而后者只是使用 UserID 过滤结果(是否存在以下情况)您不会过滤这些结果吗?)。
您可以通过将方法更改为以下方式来完全消除这两个类(以及您在评论中引用的问题):
public class TestController : Controller
{
private BookingSystemEntities db = new BookingSystemEntities();
public ActionResult Index()
{
var user = db.Users.Single(u => u.Email == User.Identity.Name);
var appointments = db.Appointments.Where(a => a.User.CompanyID == user.CompanyID).AsEnumerable();
return View(appointments);
}
public override void Dispose(bool disposing)
{
db.Dispose();
base.Dispose(disposing);
}
}
Run Code Online (Sandbox Code Playgroud)
从那里开始,如果您担心性能,您确实应该在数据库中进行所有过滤,然后仅调用这些过程来返回数据。