使用 Linq2Sql 的时态表

Temporal tables with Linq2Sql

我有一个通用存储库,可以通过 ID 获取实体或获取所有实体:

    internal class Repository<TEntity> : IRepository<TEntity>
        where TEntity : BaseEntity
    {
        protected SaiContext Context { get; }

        /// <summary>Gets the entity set.</summary>
        protected virtual DbSet<TEntity> Set => Context.Set<TEntity>();

        public Repository(SaiContext context)
        {
            Context = context;
        }

        public async Task<TEntity> GetAsync(int entityId, IEnumerable<string> includeProperties = null)
        {
            try
            {
                return await GetQueryableWithIncludes(includeProperties).SingleAsync(entity => entity.Id == entityId);
            }
            catch (InvalidOperationException)
            {
                throw new EntityNotFoundException(typeof(TEntity), entityId);
            }
        }

        public async Task<IEnumerable<TEntity>> GetAllAsync(IEnumerable<string> includeProperties = null)
        {
            return await GetQueryableWithIncludes(includeProperties).ToListAsync();
        }

        protected IQueryable<TEntity> GetQueryableWithIncludes(IEnumerable<string> includeProperties = null)
        {
            IQueryable<TEntity> queryable = Set;

            if (includeProperties == null)
            {
                return queryable;
            }

            foreach (var propertyName in includeProperties)
            {
                queryable = queryable.Include(propertyName);
            }

            return queryable;
        }
    }

为实体关系配置 DbContext 后,所有实体的导航属性以及所有其他属性都正确加载。

现在我被要求使用 temporal SQL tables 以便所有实体都有一个有效范围。

使用 SQL 我会在查询中包含 FOR SYSTEM_TIME AS OF @validityDate

为了尊重 @validityDate,调整现有实施的最简单方法(如果有的话)是什么?

我尝试过的:

  1. 寻找一种在执行 SQL 查询时配置所需系统时间的方法。问题:我找不到方法。
  2. 通过 table 值函数公开查询,允许将 @validityDate 作为参数传递。问题:我无法使用 Linq2Sql 传递参数(或者至少我不知道如何传递)。
  3. 创建一个执行连接的 table 值函数(而不是让 EF 执行连接),以便可以使用 context.FromSqlRaw(<query>) 调用它。问题:如何创建 C# 对象树? (返回多行,因为存在一对多关系)

我发现所有使用时间 tables 的示例都使用 FromSqlRaw。如果可能的话,我想避免它,因为这意味着整个数据库上下文配置变得无用,并且必须包含用于映射的附加代码。

我在 efcore-temporal-query (nuget) 库中找到了解决方案。

代码已调整为使用 README 中所述的临时表。

存储库方法现在接受可选参数 validityDate:

    public async Task<TEntity> GetAsync(int entityId, DateTime? validityDate = null, IEnumerable<string> includeProperties = null)
    {
        try
        {
            var query = GetQueryableWithIncludes(includeProperties);
            query = GetQueryableWithValidityDate(query, validityDate);
            return await query.SingleAsync(entity => entity.Id == entityId);
        }
        catch (InvalidOperationException)
        {
            throw new EntityNotFoundException(typeof(TEntity), entityId);
        }
    }

    protected IQueryable<TEntity> GetQueryableWithIncludes(IEnumerable<string> includeProperties = null)
    {
        IQueryable<TEntity> queryable = Set;

        if (includeProperties == null)
        {
            return queryable;
        }

        foreach (var propertyName in includeProperties)
        {
            queryable = queryable.Include(propertyName);
        }

        return queryable;
    }

    private static IQueryable<TEntity> GetQueryableWithValidityDate(IQueryable<TEntity> query, DateTime? validityDate)
    {
        return validityDate.HasValue ? query.AsOf(validityDate.Value) : query;
    }

历史查询的相关部分是 query.AsOf(validityDate.Value)