2

我正在尝试实现一个多租户应用程序,在该应用程序中我通过租户对象查询数据库,而不是直接脱离上下文。在我有这个之前:

public User GetUserByEmail(string email)
    {
        using (var db = CreateContext())
        {
            return db.Users.FirstOrDefault(u => u.Email.Equals(email, StringComparison.OrdinalIgnoreCase));
        }
    }

现在我有这个:

public User GetUserByEmail(string email)
    {
        using (var db = CreateContext())
        {
            return _tenant.Users.FirstOrDefault(u => u.Email.Equals(email, StringComparison.OrdinalIgnoreCase));
        }
    }

租户如下:

public class Tenant
{
    public Tenant()
    {
    }

    [Key]
    [Required]
    public int TenantId { get; set; }

    public virtual DbSet<User> Users { get; set; }
    // etc
}

我的用户模型具有以下内容:

public virtual List<Tenant> Tenants { get; set; }

在我的上下文配置中,我有以下内容:

modelBuilder.Entity<Tenant>()
        .HasMany(e => e.Users)
        .WithMany()
        .Map(m =>
        {
            m.ToTable("UserTenantJoin");
            m.MapLeftKey("TenantId");
            m.MapRightKey("UserId");
        });

但是我遇到了一个问题,即 DbSet 与上面的 ModelBuilder 不兼容 - 它让 HasMany 窒息,说不能从使用中推断出 DbSet 的使用。

我改用 ICollection 玩,但是在我的服务层中,所有对_tenant.Users.Include(stuff)、 orFind()和其他数据库查询的调用都中断了。

如果我使用 ICollection 会中断的服务方法示例:

   public User GetUserWithInterestsAndAptitudes(string username)
    {
        using (var db = CreateContext())
        {
            return _tenant.Users.  // can't use .Include on ICollection
                Include(u => u.Relationships).
                Include(u => u.Interests).
                Include(u => u.Interests.Select(s => s.Subject)).
                Include(u => u.Interests.Select(s => s.Aptitude)).
                FirstOrDefault(s => s.Username.Equals(username, StringComparison.OrdinalIgnoreCase));
        }
    }

我希望有一个解决方案可以让我保持导航属性可查询,而无需重新构建我的服务层。

一种选择是我通过 将所有内容恢复为使用上下文db.Users,然后为每个查询添加另一个条件.Where(u => u.TenantId == _tenant.TenantId)- 但我试图避免这种情况。

在这里的任何帮助将不胜感激。

4

1 回答 1

3

我有一个类似于您试图避免的解决方案。

我有一个真正的 DbContext,只能通过 TenantContext 访问。

public class RealContext
{
     public DbSet<User> Users { get; set; }
     [...]
}

public class TenantContext 
{
    private RealContext realContext;
    private int tenantId;
    public TenantContext(int tenantId)
    {
        realContext = new RealContext();
        this.tenantId= tenantId;
    }
    public IQueryable<User> Users { get { FilterTenant(realContext.Users); }     }

    private IQueryable<T> FilterTenant<T>(IQueryable<T> values) where T : class, ITenantData
    {
         return values.Where(x => x.TenantId == tenantId);
    }
    public int SaveChanges()
    {
        ApplyTenantIds();
        return realContext.SaveChanges();
    }
}

使用这种方法,我确信在没有获得正确租户的情况下不会发送查询。为了在上下文中添加和删除项目,我使用这两种通用方法。

public void Remove<T>(params T[] items) where T : class, ITenantData
{
    var set = realContext.Set<T>();
    foreach(var item in items)
        set.Remove(item);
}

public void Add<T>(params T[] items) where T : class, ITenantData
{
    var set = realContext.Set<T>();
    foreach (var item in items)
        set.Add(item);
}
于 2013-11-03T22:36:56.150 回答