Warning: file_get_contents(/data/phpspider/zhask/data//catemap/0/asp.net-core/3.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 如何在数据库优先方法中配置全局查询筛选器?_C#_Asp.net Core_.net Core_Entity Framework Core - Fatal编程技术网

C# 如何在数据库优先方法中配置全局查询筛选器?

C# 如何在数据库优先方法中配置全局查询筛选器?,c#,asp.net-core,.net-core,entity-framework-core,C#,Asp.net Core,.net Core,Entity Framework Core,我尝试使用全局查询过滤器在ASP.NET核心web应用程序中实现多租户。目前,我为每个租户都有一个单独的数据库,并在startup.cs中配置上下文,如下所示: services.AddDbContext<dbcontext>((service, options) => options.UseSqlServer(Configuration[$"Tenant:{service.GetService<ITenantProvider>().

我尝试使用全局查询过滤器在ASP.NET核心web应用程序中实现多租户。目前,我为每个租户都有一个单独的数据库,并在startup.cs中配置上下文,如下所示:

services.AddDbContext<dbcontext>((service, options) =>
                options.UseSqlServer(Configuration[$"Tenant:{service.GetService<ITenantProvider>().Current}:Database"])
                    .ConfigureWarnings(warnings => warnings.Throw(RelationalEventId.QueryClientEvaluationWarning)),
            contextLifetime: ServiceLifetime.Scoped, optionsLifetime: ServiceLifetime.Scoped);
但是我使用的是数据库优先的方法,所以每次生成模型时,我都会丢失配置是否有其他方法来配置全局查询筛选器,如使用
DbContextOptionsBuilder


我使用的是EF Core 2.1.2。

我最终使用了一个覆盖OnModelCreating方法的分部类:

public partial class MyContext : DbContext
{
    public MyContext(DbContextOptions<MyContext> options)
        : base(options)
    {
    }

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        OnModelCreatingInternal(modelBuilder);
        modelBuilder.Entity<Blog>().Property<string>("TenantId").HasField("
        modelBuilder.Entity<Blog>().HasQueryFilter(b => EF.Property<string>(b, "TenantId") == _tenantId);
    }
}
公共部分类MyContext:DbContext { 公共MyContext(DbContextOptions) :基本(选项) { } 模型创建时受保护的覆盖无效(ModelBuilder ModelBuilder) { OnModelCreatingInternal(modelBuilder); modelBuilder.Entity().Property(“TenantId”).HasField(“ modelBuilder.Entity(); } }
我仍然需要修改生成的代码(将生成的OnModelCreating签名更改为OnModelCreatingInternal并删除覆盖).但至少我遇到了一个编译器错误,所以我不能忘记它。

这是搜索这个主题时在谷歌上弹出的第一个东西,所以我发布了一个更全面、更易于使用的解决方案,这是我经过一段时间的反复思考后想到的

我希望能够自动筛选表中有一列名为TenantID的所有生成实体,并在保存时自动插入登录用户的TenantID

部分类示例:

public partial class Filtered_Db_Context : MyDbContext
{
    private int _tenant;

    public Filtered_Db_Context(IHttpContextAccessor context) : base()
    {
  _tenant = AuthenticationMethods.GetTenantId(context?.HttpContext);
}

public Filtered_Db_Context(HttpContext context) : base()
{
  _tenant = AuthenticationMethods.GetTenantId(context);
}

public void AddTenantFilter<T>(ModelBuilder mb) where T : class
{
  mb.Entity<T>().HasQueryFilter(t => EF.Property<int>(t, "TenantId") == _tenant);
}

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
  base.OnModelCreating(modelBuilder);

  //For any entity that has a TenantId it will only allow logged in user to see data from their own Tenant
  foreach (var entityType in modelBuilder.Model.GetEntityTypes())
  {
    var prop = entityType.FindProperty("TenantId");
    if (prop != null && prop.ClrType == typeof(int))
    {
      GetType()
        .GetMethod(nameof(AddTenantFilter))
        .MakeGenericMethod(entityType.ClrType)
        .Invoke(this, new object[] { modelBuilder });
    }
  }
}

public override int SaveChanges(bool acceptAllChangesOnSuccess)
{
  InsertTenantId();
  return base.SaveChanges(acceptAllChangesOnSuccess);
}

public override int SaveChanges()
{
  InsertTenantId();
  return base.SaveChanges();
}


public override Task<int> SaveChangesAsync(CancellationToken cancellationToken = default)
{
  InsertTenantId();
  return base.SaveChangesAsync(cancellationToken);
}

public override Task<int> SaveChangesAsync(bool acceptAllChangesOnSuccess, CancellationToken cancellationToken = default(CancellationToken))
{
  InsertTenantId();
  return base.SaveChangesAsync(acceptAllChangesOnSuccess, cancellationToken);
}

private void InsertTenantId()
{
  if (_tenant != 0)
  {
    var insertedOrUpdated = ChangeTracker.Entries().Where(e => e.State == EntityState.Added || e.State == EntityState.Modified).ToList();

    insertedOrUpdated.ForEach(e => {

      var prop = e.Property("TenantId");
      int propIntVal;
      bool isIntVal = int.TryParse(prop.CurrentValue.ToString(), out propIntVal);
      if (prop != null && prop.Metadata.IsForeignKey() && isIntVal && propIntVal != _tenant)
      {
        prop.CurrentValue = _tenant;
      }
    });
  }
}

  }
public分部类过滤的\u Db\u上下文:MyDbContext
{
私人国际租户;
公共筛选的_Db_上下文(IHttpContextAccessor上下文):base()
{
_租户=AuthenticationMethods.GetTenantId(context?.HttpContext);
}
公共筛选的\u Db\u上下文(HttpContext上下文):base()
{
_租户=AuthenticationMethods.GetTenantId(上下文);
}
public void AddTenantFilter(ModelBuilder mb),其中T:class
{
mb.Entity().HasQueryFilter(t=>EF.Property(t,“TenantId”)=\u tenant);
}
模型创建时受保护的覆盖无效(ModelBuilder ModelBuilder)
{
基于模型创建(modelBuilder);
//对于任何拥有TenantId的实体,它将只允许登录用户查看其自身租户的数据
foreach(modelBuilder.Model.GetEntityTypes()中的var entityType)
{
var prop=entityType.FindProperty(“租户”);
if(prop!=null&&prop.ClrType==typeof(int))
{
GetType()
.GetMethod(名称(AddTenantFilter))
.MakeGenericMethod(entityType.ClrType)
.Invoke(这个新对象[]{modelBuilder});
}
}
}
公共覆盖int SaveChanges(bool acceptAllChangesOnSuccess)
{
inserttenatid();
返回base.SaveChanges(AcceptalChangesOnSuccess);
}
公共覆盖int SaveChanges()
{
inserttenatid();
返回base.SaveChanges();
}
公共覆盖任务SaveChangesSync(CancellationToken CancellationToken=默认值)
{
inserttenatid();
返回base.saveChangesSync(cancellationToken);
}
public override Task savechangesync(bool acceptillchangesonsuccess,CancellationToken CancellationToken=default(CancellationToken))
{
inserttenatid();
return base.saveChangesSync(acceptAllChangesOnSuccess,cancellationToken);
}
私有void InsertTenantId()
{
如果(_租户!=0)
{
var insertedOrUpdated=ChangeTracker.Entries();
insertedOrUpdated.ForEach(e=>{
var prop=房地产(“承租人”);
int-propantval;
bool isIntVal=int.TryParse(prop.CurrentValue.ToString(),out-propIntVal);
if(prop!=null&&prop.Metadata.IsForeignKey()&&isIntVal&&propIntVal!=\u租户)
{
prop.CurrentValue=\u租户;
}
});
}
}
}
现在,您可以使用
Filtered\u Db\u Context
类执行所有实体框架操作,而无需在查询和保存时考虑租户函数

只需在启动时将其添加到依赖项注入,而不是EF生成的上下文:
serivces.AddDbContext()


重新构建scaffold时,无需进入并编辑任何生成的类。

您是否尝试过使用DbContext的分部类来覆盖OnModelCreating方法?@H.Herzl这是个好主意,但我仍然需要不断修改生成的代码:(@MartinBrandl)你有什么进展吗?除了像@Simon_Weaver这样修改代码之外,我使用了H.Herzl提到的解决方法。我回答了我的问题。
public partial class Filtered_Db_Context : MyDbContext
{
    private int _tenant;

    public Filtered_Db_Context(IHttpContextAccessor context) : base()
    {
  _tenant = AuthenticationMethods.GetTenantId(context?.HttpContext);
}

public Filtered_Db_Context(HttpContext context) : base()
{
  _tenant = AuthenticationMethods.GetTenantId(context);
}

public void AddTenantFilter<T>(ModelBuilder mb) where T : class
{
  mb.Entity<T>().HasQueryFilter(t => EF.Property<int>(t, "TenantId") == _tenant);
}

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
  base.OnModelCreating(modelBuilder);

  //For any entity that has a TenantId it will only allow logged in user to see data from their own Tenant
  foreach (var entityType in modelBuilder.Model.GetEntityTypes())
  {
    var prop = entityType.FindProperty("TenantId");
    if (prop != null && prop.ClrType == typeof(int))
    {
      GetType()
        .GetMethod(nameof(AddTenantFilter))
        .MakeGenericMethod(entityType.ClrType)
        .Invoke(this, new object[] { modelBuilder });
    }
  }
}

public override int SaveChanges(bool acceptAllChangesOnSuccess)
{
  InsertTenantId();
  return base.SaveChanges(acceptAllChangesOnSuccess);
}

public override int SaveChanges()
{
  InsertTenantId();
  return base.SaveChanges();
}


public override Task<int> SaveChangesAsync(CancellationToken cancellationToken = default)
{
  InsertTenantId();
  return base.SaveChangesAsync(cancellationToken);
}

public override Task<int> SaveChangesAsync(bool acceptAllChangesOnSuccess, CancellationToken cancellationToken = default(CancellationToken))
{
  InsertTenantId();
  return base.SaveChangesAsync(acceptAllChangesOnSuccess, cancellationToken);
}

private void InsertTenantId()
{
  if (_tenant != 0)
  {
    var insertedOrUpdated = ChangeTracker.Entries().Where(e => e.State == EntityState.Added || e.State == EntityState.Modified).ToList();

    insertedOrUpdated.ForEach(e => {

      var prop = e.Property("TenantId");
      int propIntVal;
      bool isIntVal = int.TryParse(prop.CurrentValue.ToString(), out propIntVal);
      if (prop != null && prop.Metadata.IsForeignKey() && isIntVal && propIntVal != _tenant)
      {
        prop.CurrentValue = _tenant;
      }
    });
  }
}

  }