如何在 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 => 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=>x.IsActive).ToArray()
- 此代码将触发 ObjectMaterialized 事件。 2) dbContext.Users.Where(x=>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<DbDataReader> 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.1 中仅加载子对象的某些字段?
如何在 Entity Framework 6 DbContext.Database.BeginTransaction 中配置事务超时?