如何在 Entity Framework 6 中使用 DbDataReader 获取表名?

Posted

技术标签:

【中文标题】如何在 Entity Framework 6 中使用 DbDataReader 获取表名?【英文标题】:How to get table name using DbDataReader in Entity Framework 6? 【发布时间】:2021-11-16 02:22:02 【问题描述】:

我的应用程序中有一个自定义 DbDataReader,它会覆盖 GetDateTime 方法来更改 DateTimeKind。

public class MyDbDataReader : BaseDbDataReader

    private string _dbName;

    public MyDbDataReader(string dbName, DbDataReader source) : base(source)
    
        _dbName = dbName;
    

    public override DateTime GetDateTime(int ordinal)
    
        var tableName = base.GetSchemaTable().TableName; //this doesn't work
        return DateTime.SpecifyKind(base.GetDateTime(ordinal), base.GetName(ordinal).EndsWith("UTC", StringComparison.OrdinalIgnoreCase) ? DateTimeKind.Utc : DateTimeKind.Local);
    

这是我的 BaseDbDataReader:

public abstract class BaseDbDataReader : DbDataReader

    readonly DbDataReader source;
    public BaseDbDataReader(DbDataReader source)
    
        this.source = source;
    

    ...
    public override DataTable GetSchemaTable()  return source.GetSchemaTable(); 

这个 dbReader 用在我的自定义拦截器中:

public class MyInterceptor : DbCommandInterceptor


    public override void ReaderExecuted(DbCommand command, DbCommandInterceptionContext<DbDataReader> interceptionContext)
               
        base.ReaderExecuted(command, interceptionContext);
        if (!command.Connection.Database.Equals("...") &&
            !(interceptionContext.Result is MyDbDataReader) &&
            interceptionContext.Result != null &&
            interceptionContext.Exception == null)
                       
            interceptionContext.Result = new MyDbDataReader(command.Connection.Database, interceptionContext.Result);
        
    

我想要的只是在 GetDateTime 方法中获取 TableName。但是 GetSchemaTable 方法返回一个难以理解的结果,其中 TableName 属性等于“SchemaTable”。我在这里做错了什么以及如何获得正确的表名(如“用户”)。

注意:我不使用 SqlCommand 和 SqlCommand.ExecuteReader 来执行查询。我只是使用 dbSet。即dbContext.Users.Where(x =&gt; x.Id = 1).Single();

【问题讨论】:

EF Core 使用 DbDataReader,而不是相反。你想做什么?也许更改加载日期的DateTimeKind? There are far simpler solutions。该问题终于在 EF Core 2.1 及更高版本中得到解决 @PanagiotisKanavos,我无法迁移到 EF 核心。并且提供的解决方案不适用于我的情况。因为((IObjectContextAdapter)this).ObjectContext.ObjectMaterialized 事件仅在创建 EF 模型(来自 dbContext 的模型)时有效。但在将 IQueryable 映射到匿名类型或任何其他不被 EF 跟踪的模型时,它不起作用。示例:1) dbContext.Users.Where(x=&gt;x.IsActive).ToArray() - 此代码将触发 ObjectMaterialized 事件。 2) dbContext.Users.Where(x=&gt;x.IsActive).Select(ToCustomModel).ToArray() - 此代码不会触发 ObjectMaterialized 事件 EF 也使用 DbDataReader。版本无所谓。所有更高级别的数据库都使用 ADO.NET 的 DbDataReader。您无需创建自定义来更改 DateTimeKind。无论如何,ORM不会使用您的自定义 DbDataReader,它们将使用数据库提供程序的类。转换类型是类型转换器的工作 但无论如何,在我的情况下,具有 DateTimeKind 属性的解决方案将不起作用。我的目标是更改加载日期的 DateTimeKind。但不适用于所有日期时间。我只想为特定的 dateTime 属性更改它。 【参考方案1】:

TableName 属性将始终返回 "SchemaTable",因为它没有其他有意义的名称可以返回。

根据the link you found,每列的表名应在模式表的BaseTableName 列中返回。但是只有在指定了CommandBehavior.KeyInfo 标志时才会返回。

挖掘源代码,您似乎需要使用ReaderExecuting 方法并接管执行命令的责任才能做到这一点:

public override void ReaderExecuting(
    DbCommand command, 
    DbCommandInterceptionContext<DbDataReader> interceptionContext)

    // Skip processing for a specific database:
    if (command.Connection.Database.Equals("...")) return;
    
    var commandBehavior = interceptionContext.CommandBehavior | CommandBehavior.KeyInfo;
    
    try
    
        var reader = command.ExecuteReader(commandBehavior);
        var result = new MyDbDataReader(command.Connection.Database, reader);
        interceptionContext.Result = result;
    
    catch (Exception ex)
    
        interceptionContext.Exception = ex;
    

理论上,您应该能够从架构表中提取BaseTableName 列:

public override DateTime GetDateTime(int ordinal)

    var schemaTable = base.GetSchemaTable();
    var tableName = schemaTable.Rows[ordinal].Field<string>("BaseTableName");
    ...

【讨论】:

好点。但我发现了一些使用 GetSchemaTable 获取表名的示例 - link。看来我需要将 commandBehaviour 更改为 KeyInfo。但我不知道在哪里使用它,因为我不使用 ExecuteReader 来执行 sql 查询。 有趣。我没有在模式表中发现该列。看来您需要通过DbCommandInterceptionContext 参数更改CommandBehavior 我尝试将此代码 public override void ReaderExecuting(DbCommand command, DbCommandInterceptionContext&lt;DbDataReader&gt; interceptionContext) interceptionContext = interceptionContext.WithCommandBehavior(System.Data.CommandBehavior.KeyInfo); base.ReaderExecuting(command, interceptionContext); 放在我的自定义拦截器中,但没有帮助。 TableName 仍然是“SchemaTable” 这个方法会在执行查询之前被调用 很遗憾,您的解决方案不起作用。

以上是关于如何在 Entity Framework 6 中使用 DbDataReader 获取表名?的主要内容,如果未能解决你的问题,请参考以下文章

如何在 Entity Framework 6 中使用 DbDataReader 获取表名?

如何扩展 Entity Framework 6 模型

如何在 Entity Framework 6.1 中仅加载子对象的某些字段?

如何在 Entity Framework 6 DbContext.Database.BeginTransaction 中配置事务超时?

在 Entity Framework 6.1(非核心)中,如何使用 IndexAttribute 来定义聚集索引?

如何在 .Net Core 6.0 中结合 CosmosDB、Entity Framework 和 OData?