2013-11-03 79 views
2

我試圖實現一個多租戶應用程序,其中我通過租戶對象查詢數據庫,而不是直接從上下文中查詢數據庫。我不得不在此之前:DbSet,ModelBuilder和EF導航屬性

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是與上面的模型構建器不兼容 - 它扼流圈的hasMany說,使用DbSet不能從使用推斷。

我使用ICollection來代替,但隨後在我的服務層調用_tenant.Users.Include(stuff)Find()和其他db查詢中斷。

打破,如果我使用的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)添加另一個條件 - 但我試圖避免這種情況。

任何幫助在這裏將不勝感激。

回答

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

謝謝,這很有趣。我很好奇 - 當你從服務/存儲庫層新建TenantContext時,你如何訪問tenantId?我發佈了另一個關於這個問題,以防萬一你有一分鐘​​:http://stackoverflow.com/questions/19756369/making-an-object-accessible-by-service-layer-without-passing-as-parameter-in- mvc – SB2055

+0

在我的情況下,tenantId存儲在usertable的一列中。 –

+0

對於將FilterTenant的輸出轉換爲DbSet,您有什麼想法,以便我不必爲Remove和Add編寫泛型? – SB2055