简体   繁体   English

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

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

I am developing a multi-tenant web application using MVC4 and EF5 . 我正在使用MVC4EF5开发一个多租户Web应用程序 I previously asked this question regarding filtering my DbContext: Is it bad practice to filter by ID within the repository pattern . 我之前曾问过有关过滤我的DbContext的问题: 在存储库模式中按ID过滤是不好的做法

Apparently, my approach was sound, but it was suggested that rather than handling all filtering in that single repository, I provide a context already filtered to tenant level using 'a repository or DBContextWrapper class that would feed your [my] normal repository' . 显然,我的方法很合理,但有人建议,不是在单个存储库中处理所有过滤,而是使用'存储库或DBContextWrapper类来提供已经过滤到租户级别上下文 ,它将为[my]普通存储库提供信息

Unfortunately I am no MVC expert, so I started implementing this as best I could, but upon researching filtering in EF for other multi-tenant applications I found a question for a very similar case Multi-tenancy web application with filtered dbContext , though completely failed to understand the answer to it. 不幸的是我不是MVC专家,所以我尽可能地开始实现这一点,但是在研究其他多租户应用程序的EF过滤时,我发现了一个非常类似案例的问题 多租户Web应用程序带有过滤的dbContext ,尽管完全失败了理解答案。

In my application, the CompanyID is a property of the User class, so should be taken directly from the authenticated user. 在我的应用程序中,CompanyID是User类的属性,因此应直接从经过身份验证的用户获取。 Eg: 例如:

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

My current approach does appear to work, however I'm pretty sure I have gone about it the wrong way and/or have done it inefficiently based on what I've seen in other Questions about doing the same sort of thing. 我当前的方法似乎确实有效,但是我很确定我已经以错误的方式解决了问题和/或根据我在其他有关做同样事情的问题中看到的内容做得不够。 In another question Solutions for a simple multi tenant web application with entity framework reflection is used to do this, but I'm not able to work out whether it would apply in my case, or even how to use it. 在另一个问题中,使用实体框架反射的简单多租户Web应用程序的解决方案用于执行此操作,但我无法确定它是否适用于我的情况,甚至如何使用它。

I would be extremely appreciative if anyone can explain the best way of going about this, and the pros/cons of differing ways. 如果有人能够解释这个问题的最佳方式,以及不同方式的利弊,我将非常感激。 Thanks :) 谢谢 :)

My current implementation is as follows: 我目前的实施如下:

DB D B

  • One database, multiple tenants. 一个数据库,多个租户。
  • All tables link back to Company table one way or another, although not all have a CompanyID field. 所有表都以这种或那种方式链接回公司表,尽管并非所有表都有CompanyID字段。

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

I like your approach better than some of the other examples you've provided. 我比你提供的其他一些例子更喜欢你的方法。 Filtering based on the logged-in user should be the most effective way to make sure you're filtering your data appropriately assuming each tenant runs off the same codebase and domain. 基于登录用户进行过滤应该是确保您正确过滤数据的最有效方法,假设每个租户都运行相同的代码库和域。 (If not, you could utilize those to filter as well.) (如果没有,你也可以利用它们进行过滤。)

If you're concerned about database performance with filtering tables that don't have a CompanyID you can purposely denormalize your database to include that field in those tables. 如果您关注具有不具有CompanyID的过滤表的数据库性能,则可以故意对数据库进行非规范化以在该表中包含该字段。

The reflection approach you cited, although elegant, seems overly complicated and like a lot more overhead than including the CompanyID in your db call (especially since the db call is happening in both instances). 您引用的反射方法虽然优雅,但似乎过于复杂,并且比在数据库调用中包含CompanyID要多得多(特别是因为在两个实例中都发生了db调用)。

EDIT (after comment): 编辑(评论后):

As for the rest of it, you seem to have written a lot of excess code that's not really necessary (at least not within the example cited above). 至于其余部分,你似乎写了很多不必要的超额代码(至少不在上面引用的例子中)。 I don't necessarily understand why you're distinguishing between a BookingSystemRepository and a CompanyBookingSystemRepository since from your code it seems like the former exists only to pass calls through to the latter, which just filters results using the UserID (is there ever a situation where you wouldn't filter those results?). 我不一定理解为什么你要区分BookingSystemRepository和CompanyBookingSystemRepository,因为从你的代码看,前者似乎只存在将调用传递给后者,后者只是使用UserID过滤结果(是否存在这样的情况:你不会过滤那些结果?)。

You could eliminate both of those classes (and the issue you cite in your comment) entirely by just changing your method to: 您可以完全通过将方法更改为以下内容来消除这两个类(以及您在评论中引用的问题):

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

From there, if you're worried about performance, you really should be doing all your filtering within the DB and then only call those procedures to return your data. 从那里,如果你担心性能,你真的应该在数据库中进行所有过滤,然后只调用这些过程来返回你的数据。

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

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