【问题标题】:How to get table name using DbDataReader in Entity Framework 6?如何在 Entity Framework 6 中使用 DbDataReader 获取表名?
【发布时间】: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,而不是相反。你想做什么?也许更改加载日期的DateTimeKindThere 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 属性更改它。

标签: c# asp.net entity-framework entity-framework-6


【解决方案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”
  • 这个方法会在执行查询之前被调用
  • 很遗憾,您的解决方案不起作用。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-01-23
  • 2018-01-21
相关资源
最近更新 更多