繁体   English   中英

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

[英]How to filter DbContext in a multi-tenant application using Entity Framework with MVC4

我正在使用MVC4EF5开发一个多租户Web应用程序 我之前曾问过有关过滤我的DbContext的问题: 在存储库模式中按ID过滤是不好的做法

显然,我的方法很合理,但有人建议,不是在单个存储库中处理所有过滤,而是使用'存储库或DBContextWrapper类来提供已经过滤到租户级别上下文 ,它将为[my]普通存储库提供信息

不幸的是我不是MVC专家,所以我尽可能地开始实现这一点,但是在研究其他多租户应用程序的EF过滤时,我发现了一个非常类似案例的问题 多租户Web应用程序带有过滤的dbContext ,尽管完全失败了理解答案。

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

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

我当前的方法似乎确实有效,但是我很确定我已经以错误的方式解决了问题和/或根据我在其他有关做同样事情的问题中看到的内容做得不够。 在另一个问题中,使用实体框架反射的简单多租户Web应用程序的解决方案用于执行此操作,但我无法确定它是否适用于我的情况,甚至如何使用它。

如果有人能够解释这个问题的最佳方式,以及不同方式的利弊,我将非常感激。 谢谢 :)

我目前的实施如下:

D B

  • 一个数据库,多个租户。
  • 所有表都以这种或那种方式链接回公司表,尽管并非所有表都有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();
    }
}

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

如果您关注具有不具有CompanyID的过滤表的数据库性能,则可以故意对数据库进行非规范化以在该表中包含该字段。

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

编辑(评论后):

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

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM