如何使用 c# 在数据库优先方法中拥有审计列?

Posted

技术标签:

【中文标题】如何使用 c# 在数据库优先方法中拥有审计列?【英文标题】:How can I have audit columns in a database-first approach using c#? 【发布时间】:2019-11-16 17:25:50 【问题描述】:

我需要为我的 MVC 项目(Web 应用程序)中的所有表添加审核列(创建者、创建者、修改者和修改者)。我想有一个包含这 4 列的基类并继承所有其他类。但我使用的是 Database-First 方法,因此 EF 生成了所有类。我不确定如何添加审计列和基类继承。

【问题讨论】:

这是切换到代码优先的绝佳机会(无论有无迁移)。在代码优先中,这要容易得多。使用数据库优先时,您会被 EF6.2 困住,并且很可能有一天 Visual Studio 将停止支持 EDMX 设计。 【参考方案1】:

数据库首先意味着您必须手动将所有字段添加到数据库中,AFAIK 没有继承。您可以编写一个添加这些列的 SQL 脚本。正如马哈尔所说。但是你仍然会缺少继承。

或者你先切换到代码。然后你可以定义一个接口并在代码中处理审计。如图here。 比如我用这个接口:

    /// <summary>
    /// Adds auditing properties to an entity.
    /// </summary>
    public interface IAuditedEntity
    
        /// <summary>
        /// Date and time of the entity's creation. Usually in UTC.
        /// </summary>
        /// <remarks>Best set via a <see cref="ITimeProvider"/>.</remarks>
        DateTime DateCreated  get; set; 

        /// <summary>
        /// Identification who created this instance.
        /// </summary>
        /// <remarks>Best set via a <see cref="ICurrentUserIdProvider"/>.</remarks>
        string CreatedBy  get; set; 

        /// <summary>
        /// Date and time of last modification. Usually in UTC.
        /// </summary>
        /// <remarks>Best set via a <see cref="ITimeProvider"/>.</remarks>
        DateTime? DateModified  get; set; 

        /// <summary>
        /// Last one modifiying this instance.
        /// </summary>
        /// <remarks>Best set via a <see cref="ICurrentUserIdProvider"/>.</remarks>
       string ModifiedBy  get; set; 

用户提供者接口定义如下:

    /// <summary>
    /// Interface for providing the current user's id.
    /// </summary>
    public interface ICurrentUserIdProvider
    
        /// <summary>
        /// Get the id of the curent user.
        /// </summary>
        /// <returns></returns>
        string GetCurrentUserId();
    

出于测试目的,您现在可以使用ambient context/service 传递当前用户或您需要测试逻辑的任何用户。

【讨论】:

【参考方案2】:

我假设不可能先通过 EF 数据库执行您想要的操作。但是为了将特定字段添加到数据库的所有表中,您可以对 Sql Server 中的每个字段使用以下代码,然后更新您的 EF 模型。

DECLARE @TableName VARCHAR(100)
DECLARE @TableSchema VARCHAR(100)
DECLARE @COLUMN_NAME VARCHAR(50)
SET @COLUMN_NAME='CreatedOn' 
DECLARE @COLUMN_DATATYPE VARCHAR(50)
SET @COLUMN_DATATYPE='DateTime'

DECLARE CUR CURSOR FOR
  SELECT TABLE_SCHEMA,
         TABLE_NAME
  FROM   INFORMATION_SCHEMA.TABLES
  WHERE  TABLE_TYPE = 'BASE TABLE'
OPEN CUR
FETCH NEXT FROM CUR INTO @TableSchema,@TableName
WHILE @@FETCH_STATUS = 0
  BEGIN

  DECLARE @SQL NVARCHAR(MAX)
  SET @SQL=NULL
  IF NOT EXISTS ( SELECT 1 FROM INFORMATION_SCHEMA.COLUMNS 
       WHERE TABLE_NAME=@TableName AND COLUMN_NAME = @COLUMN_NAME  and Table_Schema=@TableSchema)
  BEGIN
  SET @SQL='ALTER TABLE '+ @TableSchema+'.'+ @TableName +' ADD '+ @COLUMN_NAME + ' '+ @COLUMN_DATATYPE
    PRINT @SQL
    EXEC(@SQL)
  END

  IF EXISTS ( SELECT 1 FROM INFORMATION_SCHEMA.COLUMNS 
              WHERE TABLE_NAME=@TableName AND COLUMN_NAME=@COLUMN_NAME and Table_Schema=@TableSchema)
  BEGIN
    PRINT 'Column Already exists in Table'
  END
      FETCH NEXT FROM CUR INTO @TableSchema,@TableName
  END
CLOSE CUR
DEALLOCATE CUR

【讨论】:

以上是关于如何使用 c# 在数据库优先方法中拥有审计列?的主要内容,如果未能解决你的问题,请参考以下文章

如何使用触发器将基表的所有更新列添加到审计表的多行?

如何强制实体框架插入标识列?

如何使用 C# 在 datagridview 控件中显示某些表架构列?

在 Mysql 中添加新列时如何轻松维护审计触发器

e语言代码如何审计

如何在 LINQ C# 中仅过滤 2 列分组中的最后一个值