如何使用带有MVC4的实体框架在多租户应用程序中过滤DbContext

本文关键字:应用程序 DbContext 过滤 框架 何使用 MVC4 实体 | 更新日期: 2023-09-27 18:18:58

我正在开发一个多租户web应用程序使用MVC4EF5。我以前问过关于过滤DbContext的问题:在存储库模式中按ID过滤是不好的做法吗?

显然,我的方法是合理的,但有人建议,而不是处理单一存储库中的所有过滤,我提供一个上下文已经过滤到租户级别使用'一个存储库或DBContextWrapper类,将提供您的[我的]正常存储库'

不幸的是,我不是MVC专家,所以我开始尽我所能实现这一点,但在研究EF中过滤其他多租户应用程序时,我发现了一个非常类似的情况过滤dbContext的多租户web应用程序的问题,尽管完全未能理解它的答案。

在我的应用程序中,CompanyID是User类的属性,因此应该直接从经过身份验证的用户获取。如:

int CompanyID = db.Users.Single(u => u.Email == User.Identity.Name).CompanyID;

我目前的方法似乎是有效的,但是我很确定我的方法是错误的,或者是低效的,这是基于我在其他问题中看到的关于做同样的事情。在另一个问题的解决方案,一个简单的多租户web应用程序与实体框架反射是用来做到这一点,但我不能确定它是否会适用于我的情况下,甚至如何使用它。

如果有人能解释一下最好的方法,以及不同方法的利弊,我将非常感激。谢谢:)

我目前的实现如下:

DB

  • 一个数据库,多个租户
  • 所有表都以某种方式链接回Company表,尽管不是所有表都有CompanyID字段。

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));
    }
}

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();
    }
}

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();
    }
}

如何使用带有MVC4的实体框架在多租户应用程序中过滤DbContext

与您提供的其他一些示例相比,我更喜欢您的方法。假设每个租户运行相同的代码库和域,基于登录用户进行过滤应该是确保正确过滤数据的最有效方法。(如果没有,您也可以使用它们来过滤。)

如果你担心没有CompanyID的过滤表的数据库性能,你可以故意将数据库反规范化,在这些表中包含该字段。

您引用的反射方法虽然优雅,但似乎过于复杂,并且比在db调用中包含CompanyID要多很多开销(特别是因为db调用在两个实例中都发生)。

EDIT (after comment):

至于其余部分,您似乎编写了许多不必要的多余代码(至少在上面引用的示例中不是这样)。我不一定理解为什么要区分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);
    }
}

从这里开始,如果你担心性能,你真的应该在DB中做所有的过滤,然后只调用那些过程来返回你的数据。