简体   繁体   English

DbSet,ModelBuilder和EF导航属性

[英]DbSet, ModelBuilder, and EF Navigation Properties

I'm attempting to implement a multi-tenant application where I query the db via the tenant object, instead of directly off the context. 我正在尝试实现一个多租户应用程序,在这里我通过租户对象而不是直接从上下文查询数据库。 Before I had this: 在此之前:

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

Now I have this: 现在我有这个:

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

Where Tenant is the following: 其中“租户”为:

public class Tenant
{
    public Tenant()
    {
    }

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

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

Where my User model has the following: 我的用户模型具有以下内容:

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

And in my Context configuration, I have the following: 在我的上下文配置中,我具有以下内容:

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

But I'm running into a problem with the fact that DbSet is incompatible with the ModelBuilder above - it chokes on HasMany saying that the use of DbSet cannot be inferred from usage. 但是我遇到了一个问题,那就是DbSet与上面的ModelBuilder不兼容-它使HasMany感到不安,他说不能从用法中推断出DbSet的使用。

I played with using ICollection instead, but then in my service layer all calls to _tenant.Users.Include(stuff) , or Find() , and other db queries break. 我改用了ICollection,但是在我的服务层中,对_tenant.Users.Include(stuff)Find()所有调用都_tenant.Users.Include(stuff) ,其他数据库查询也中断了。

Example of a service method that breaks if I use ICollection: 如果使用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));
        }
    }

I'm hoping there's a solution that will allow me to keep the navigation properties queryable without re-architecting my service layer. 我希望有一个解决方案,使我可以保持导航属性的可查询性,而无需重新构建服务层。

One option is that I revert everything back to using the context via db.Users , and then add another condition to every single query .Where(u => u.TenantId == _tenant.TenantId) - but I'm trying to avoid this. 一种选择是,我通过db.Users将所有内容恢复为使用上下文,然后向每个查询添加另一个条件.Where(u => u.TenantId == _tenant.TenantId) -但我正尝试避免这种情况。

Any help here would be much appreciated. 在这里的任何帮助将不胜感激。

I have a solution similar to what you are trying to avoid. 我有一个类似于您要避免的解决方案。

I have a real DbContext that is only accessed via a TenantContext. 我有一个真正的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();
    }
}

Using this method I'm sure that there is no was a query can be sent without getting the correct tenants. 使用这种方法,我确定没有正确的租户就无法发送查询。 For adding and removing items from the context I' using those two generic methods. 为了从上下文中添加和删除项目,我使用这两种通用方法。

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

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

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