在我们的系统中,我们正在实施系统范围的时区选择,无论用户从何处查看网站,所有日期和时间都将被视为。
数据库将存储UTC时间。我试图做的是使用
DbCommandInterceptor
来捕获所有日期时间属性,并在通过实体框架从数据库读取数据的过程中进行时区转换。但是,我遇到了一个异常,该异常发生在具有几何类型空间字段的表上。
System.Data.Entity.Core.ProviderInknownException:'空间 读取器只能从 SqlDataReader 类型的读取器生成。 A HomesteadRepository.DataInterception.EFDataReader 类型的阅读器是 提供。'
EFDataReader
数据读取器类继承自DbDataReader
,通过构造函数将原始数据读取器带入。 99% 的内部函数使用原始数据读取器返回其函数,唯一的变化是执行日期和时间调整的 GetDateTime
函数。
public class EFDataReader : DbDataReader
{
private readonly DbDataReader originalReader;
public override bool HasRows => originalReader.HasRows;
public override bool IsClosed => originalReader.IsClosed;
public override int FieldCount => originalReader.FieldCount;
public override int RecordsAffected => originalReader.RecordsAffected;
public override Int32 Depth => originalReader.Depth;
public override Object this[Int32 ordinal] => originalReader[ordinal];
public override Object this[String name] => originalReader[name];
public EFDataReader(DbDataReader originalReader)
{
this.originalReader = originalReader;
}
public override bool GetBoolean(int ordinal)
{
return originalReader.GetBoolean(ordinal);
}
public override byte GetByte(int ordinal)
{
return originalReader.GetByte(ordinal);
}
public override long GetBytes(int ordinal, long dataOffset, byte[] buffer, int bufferOffset, int length)
{
return originalReader.GetBytes(ordinal, dataOffset, buffer, bufferOffset, length);
}
public override char GetChar(int ordinal)
{
return originalReader.GetChar(ordinal);
}
public override long GetChars(int ordinal, long dataOffset, char[] buffer, int bufferOffset, int length)
{
return originalReader.GetChars(ordinal, dataOffset, buffer, bufferOffset, length);
}
public override string GetDataTypeName(int ordinal)
{
return originalReader.GetDataTypeName(ordinal);
}
public override DateTime GetDateTime(int ordinal)
{
try
{
DateTime datetime = originalReader.GetDateTime(ordinal);
datetime = DL.AdjustDateTime(datetime).Value;
return datetime;
}
catch (Exception ex)
{
throw;
}
}
public override decimal GetDecimal(int ordinal)
{
return originalReader.GetDecimal(ordinal);
}
public override double GetDouble(int ordinal)
{
return originalReader.GetDouble(ordinal);
}
public override IEnumerator GetEnumerator()
{
return originalReader.GetEnumerator();
}
public override Type GetFieldType(int ordinal)
{
return originalReader.GetFieldType(ordinal);
}
public override float GetFloat(int ordinal)
{
return originalReader.GetFloat(ordinal);
}
public override Guid GetGuid(int ordinal)
{
return originalReader.GetGuid(ordinal);
}
public override short GetInt16(int ordinal)
{
return originalReader.GetInt16(ordinal);
}
public override int GetInt32(int ordinal)
{
return originalReader.GetInt32(ordinal);
}
public override long GetInt64(int ordinal)
{
return originalReader.GetInt64(ordinal);
}
public override string GetName(int ordinal)
{
return originalReader.GetName(ordinal);
}
public override int GetOrdinal(string name)
{
return originalReader.GetOrdinal(name);
}
public override string GetString(int ordinal)
{
return originalReader.GetString(ordinal);
}
public override object GetValue(int i)
{
return originalReader.GetValue(i);
}
public override int GetValues(object[] values)
{
return originalReader.GetValues(values);
}
public override bool IsDBNull(int ordinal)
{
return originalReader.IsDBNull(ordinal);
}
public override bool NextResult()
{
return originalReader.NextResult();
}
public override bool Read()
{
if (originalReader != null)
{
return originalReader.Read();
}
return false;
}
}
EFInterceptor
命令拦截器类继承自DbCommandInterceptor
。唯一被覆盖的函数是 ReaderExecuted
函数。
public class EFInterceptor : DbCommandInterceptor
{
public EFInterceptor()
{
}
public override void ReaderExecuted(DbCommand command, DbCommandInterceptionContext<DbDataReader> interceptionContext)
{
interceptionContext.Result = new EFDataReader(interceptionContext.Result);
base.ReaderExecuted(command, interceptionContext);
}
}
实体框架数据库上下文在其构造函数中添加拦截器。
DbInterception.Add(new EFInterceptor());
是否有一种方法可以创建和使用使用
SqlDataReader
的拦截器,或者可以在实体框架从数据库读取数据后立即执行此转换而无需手动转到每个字段并执行调整的任何方式?
EFDataReader
类继承自DbDataReader
,用于拦截数据库命令并调整时区转换的日期时间属性。然而,它缺乏处理空间数据类型的功能,这就是抛出异常的原因。
要解决此问题,可以修改
EFDataReader
,将空间数据类型的读取委托给原始 SqlDataReader
。
public override object GetValue(int ordinal)
{
// Check if the column data type is spatial
if (IsSpatialDataType(ordinal))
{
// Delegate to the original SqlDataReader for spatial data
return originalReader.GetValue(ordinal);
}
else
{
// Continue using EFDataReader for non-spatial data
return base.GetValue(ordinal);
}
}
private bool IsSpatialDataType(int ordinal)
{
// Logic to determine if the column data type is spatial
// This could be based on the column name, data type, etc.
// Implement this method based on your specific requirements
}
确保实施
IsSpatialDataType
方法以准确识别空间数据列。该解决方案应该允许您适当地处理空间数据,同时仍然拦截其他数据类型以进行时区调整。