保存前设定值

时间:2018-10-23 17:24:44

标签: c# entity-framework-core

我想知道是否有任何方法可以为实体保存设置值?
因为我正在处理多租户Web应用程序,所以我想设置当前的租户ID(通过简单的DI服务)。

我尝试在Fluent API中使用HasDefaultValue(),但是这将尝试转换为SQL函数。所以这对我不起作用。

builder.Entity<Order>( )
    .HasQueryFilter(p => p.TenantId == _tenantProvider.GetTenantId())
    .Property(p => p.TenantId)
    .HasDefaultValue(_tenantProvider.GetTenantId());

任何建议都将不胜感激。

3 个答案:

答案 0 :(得分:2)

您可以覆盖DbContext.SaveChanges()方法并迭代ChangeTracker条目:

public override int SaveChanges()
{
    foreach (var entityEntry in ChangeTracker.Entries()) // Iterate all made changes
    {
        if (entityEntry.Entity is Order order)
        {
            if (entityEntry.State == EntityState.Added) // If you want to update TenantId when Order is added
            {
                order.TenantId = _tenantProvider.GetTenantId();
            }
            else if (entityEntry.State == EntityState.Modified) // If you want to update TenantId when Order is modified
            {
                order.TenantId = _tenantProvider.GetTenantId();
            }
        }
    }
    return base.SaveChanges();
}

当然,这需要将租户提供者注入您的上下文中。

答案 1 :(得分:2)

如果您使用的是 EF Core 5+,则可以选择使用 SavingChanges 事件。这将允许您为 SaveChangesSaveChangesAsync 设置自定义逻辑,而无需覆盖这两个方法。

示例:

public MyDbContext(DbContextOptions<MyDbContext> dbContextOptions, ITenantProvider tenantProvider) : base(dbContextOptions)
{
    SavingChanges += (sender, args) =>
    {
        foreach (var orderEntity in ChangeTracker.Entries<Order>())
        {
            if (orderEntity.State == EntityState.Added)
            {
                orderEntity.Entity.TenantId = tenantProvider.GetTenantId();
            }
        }
    };
}

答案 2 :(得分:1)

具有自定义value generation on add的EF Core ValueGenerator

  

在将实体添加到上下文中时为属性生成值。

可用于将TenantId分配给新实体。在Next方法内部,您可以从上下文(或某些服务)中获取TenantId

以您的样本为例,值生成器可以是DbContext中的嵌套类,如下所示:

class TenantIdValueGenerator : ValueGenerator<int>
{
    public override bool GeneratesTemporaryValues => false;
    public override int Next(EntityEntry entry) => GetTenantId(entry.Context);
    int GetTenantId(DbContext context) => ((YourDbContext)context)._tenantProvider.GetTenantId();
}

您需要做的就是使用一些HasValueGenerator流利的API将生成器分配给TenantId属性。

唯一的问题是,根据设计,只有在属性没有显式设置值的情况下才调用值生成器(对于int属性-如果值是0)。

因此,更好的方法是通过从实体模型中删除TenantId属性并将其替换为shadow property来抽象(并完全控制)TenantId属性。

因此,我的建议是,从实体类中删除OnModelCreating并在TenantId内部为需要void ConfigureTenant<TEntity>(ModelBuilder modelBuilder) where TEntity : class { modelBuilder.Entity<TEntity>(builder => { builder.Property<int>("TenantId") .HasValueGenerator<TenantIdValueGenerator>(); builder.HasQueryFilter(e => EF.Property<int>(e, "TenantId") == _tenantProvider.GetTenantId()); }); } 列的每个实体调用以下方法:

32