【问题标题】:EF Core - set field values automaticallyEF Core - 自动设置字段值
【发布时间】:2019-02-06 23:53:36
【问题描述】:

我正在使用 ASP.NET Core + Entity Framework Core 开发一个 Web 应用程序。

我想存储更多关于创建修改删除的信息。

我的 EF 实体实现了带有以下字段的接口:

  • DateTime CreationTime { get; set; }
  • long? CreatorUserId { get; set; }
  • DateTime? ModificationTime { get; set; }
  • long? ModifierUserId { get; set; }
  • DateTime? DeletionTime { get; set; }
  • public long? DeleterUserId { get; set; }
  • bool IsDeleted { get; set; }
  • int TenantId { get; set; }

我一直在尝试让我的应用自动设置所有字段。

现在我只能处理DateTime 字段:

AppDbContext.cs

public override async Task<int> SaveChangesAsync(CancellationToken cancellationToken = default(CancellationToken))
{
    ChangeTracker.DetectChanges();
    ChangeTracker.ProcessModifiedTime();
    ChangeTracker.ProcessSoftDelete();
    ChangeTracker.ProcessCreationTime();

    return (await base.SaveChangesAsync(true, cancellationToken));
}

ChangeTrackerExtensions.cs

public static class ChangeTrackerExtensions
{
    public static void ProcessCreationTime(this ChangeTracker changeTracker)
    {
        foreach (var item in changeTracker.Entries<ICreationTime>().Where(e => e.State == EntityState.Added))
        {
            item.CurrentValues[AppConsts.CreationTime] = DateTime.Now;
        }
    }

    public static void ProcessModifiedTime(this ChangeTracker changeTracker)
    {
        foreach (var item in changeTracker.Entries<IModificationTime>().Where(e => e.State == EntityState.Modified))
        {
            item.CurrentValues[AppConsts.ModificationTime] = DateTime.Now;
        }
    }

    public static void ProcessSoftDelete(this ChangeTracker changeTracker)
    {
        foreach (var item in changeTracker.Entries<ISoftDelete>().Where(e => e.State == EntityState.Deleted))
        {
            item.State = EntityState.Modified;
            item.CurrentValues[AppConsts.IsDeleted] = true;
            item.CurrentValues[AppConsts.DeletionTime] = DateTime.Now;
        }
    }
}

服务层控制器级别,每次都可以分配所需的值(CreatorUserIdMofidierUserIdDeleterUserId)但这变成例行任务,非常乏味

我见过AspNetBoilerplate implementation,但让我担心的是开发人员让他们的 DbContext 依赖于 Session 和其他东西。

public abstract class AbpDbContext : DbContext, ITransientDependency, IShouldInitialize
{ 
    public IAbpSession AbpSession { get; set; }
    public IEntityChangeEventHelper EntityChangeEventHelper { get; set; }
    public ILogger Logger { get; set; }
    public IEventBus EventBus { get; set; }
    public IGuidGenerator GuidGenerator { get; set; }
    public ICurrentUnitOfWorkProvider CurrentUnitOfWorkProvider { get; set; }
    public IMultiTenancyConfig MultiTenancyConfig { get; set; }
    ...

恕我直言,这是典型的违反单一职责原则

我尝试查看 interceptors,但它们似乎仅在 Entity Framework 的普通(非核心)版本中可用 - 仍然没有设法让我了解如何实现我上面提到的。

真的,我完全不知道如何使用 Entity Framework Core 处理它。谁能想到任何服务层级别的解决方案?或者也许有更好的方法?

【问题讨论】:

    标签: c# asp.net entity-framework refactoring entity-framework-core


    【解决方案1】:

    此处的 UserId 和 TenantId 只是您的存储库服务(此处为 DbContext)所需的数据片段。如果服务需要数据,则应将其注入服务中。

    如果没有 DI,您可能会在 DbContext 上创建 UserID 和 TenantID 构造函数参数。使用 DI,您可以注入对提供此数据的服务的依赖。

    【讨论】:

    • 只看下面的答案stackoverflow.com/a/51126357/5374333“两个注释”部分,第 2 条 - SRP 问题。对于我们是否真的可以将会话或任何其他内容注入DbContext,仍然非常自信。
    • 让您的 DbContext 开放访问会话状态或 Http 上下文显然是错误的。但是让你的 DbContext 依赖于少量的固定参数是可以的。要使用正确的身份连接到正确的数据库并记录审计数据,可能需要 UserId 和 TenantId。一切都在存储库的单一职责范围内。
    • 我介绍了注册为 Scoped/Per Request 的 Session 类,它属于服务层并在 MVC 过滤器中填充 - 我们从 Claims 中获取 UserID 和 TenantID,这样当我们进一步将其注入任何服务 - 我们有 UserId 和 TenandId 并且没有违反 SRP。但我仍然担心将这个 Session 实例注入 DbContext 可能在 SRP 方面是错误的(即使我们不注入 HttpContext)。你怎么看?发布了我的解决方法stackoverflow.com/a/52129616/5374333
    • 听起来不错,只要您不向 ISession 服务添加一堆其他成员。例如,需要能够轻松地创建一个可以在单元测试或其他没有 HTTP 请求的上下文中使用的实现,例如批处理。
    • 很抱歉插入我自己的问题,但我认为它也与 OP 有关。那么,您在这里描述的是一种实现与存储库无关的域模型的方法吗?您是说这样做的方法是让您的存储库实现依赖于某种“ID 提供者”(其实现将在同一架构层中)?如果是这样,您能否写几句话来说明在读取(和更新)数据时它是如何工作的?我想您必须以某种方式将 ID 映射到域对象,并且整个 DDD 实体与值的想法变得很重要。
    猜你喜欢
    • 2021-05-06
    • 1970-01-01
    • 2021-12-26
    • 1970-01-01
    • 2019-06-26
    • 2020-04-14
    • 2020-11-30
    • 2017-08-20
    • 2014-10-09
    相关资源
    最近更新 更多