gpt4 book ai didi

c# - 当列可为空时,应如何处理使实体属性不可为空的问题? -EF核心

转载 作者:行者123 更新时间:2023-11-30 18:12:01 24 4
gpt4 key购买 nike

该数据库具有nullabble列,有人告诉我可以安全地假设其中永远不会包含null数据。实体框架代码是在这种假设下建模的。但是,我发现某些客户数据库实际上在这些列中确实具有偶尔的空值。在使用SqlReader读取这些列时,例如,通过对null值调用getDateTime,实体框架会在内部爆炸。

数据库模式是很久以前设计的,目前无法更新。简而言之,不应为空的列有时为空。软件强制这些列存储适当的值,而不是数据库。

我们的实体框架代码是在假定软件已正确完成其工作的情况下进行建模的,而这些原本不应该为null的列也不为空。

在测试中,我发现两个客户的数据库在某种程度上在不应该包含的列中具有空值,并且实体框架在内部试图解析错误时开始崩溃。这些都是历史性的数据库,仍然可以加载,但是可能是由于该软件在10年前更加漏洞百出。例如,这些解析错误全部是SqlReader错误,原因是无法对不可为null的DateTime对象的null值调用getDateTime。

我可以尝试炸毁它,然后手动将每个属性更改为可为空(然后更改代码以现在处理它为可为空的可能性),但是我不想使每个属性都为可为空。到目前为止,我见过的每个空值都可以默认为一个适当的值。

我尝试将一个属性重构为“ PropertySafe”(不破坏现有代码),然后连接一个新的“ Property”,然后让“ PropertySafe”的getter调用Property并在null上返回适当的默认值。这样做更好,因为其余的代码不必知道此数据库架构缺陷,但是一遍又一遍地做起来很麻烦,我觉得这有点丑陋。我还担心某些获取了这些“安全”属性之一的EFContext查询会混淆EF并迫使其在客户端运行它们,这会降低性能。

我潜入了RelationalTypeMapping和ValueConverter,认为可能可以处理从sql的“ datetime not null”列到CLR DateTime属性的问题,这在我需要时默认为默认值,但是这不起作用,因为此时数据库已被解析。我将需要覆盖它专门在SqlReader上调用getDateTime的任何地方,并在那里拦截它。

失败的表/列的实体/属性示例实际上与以下示例相同。

public class Entity {
public int Id { get; set; }
public DateTime DueDate { get; set; }

public class Configuration : BaseConfiguation<Entity> {
protected override string TableName => "ENTITY_DUE_DATE";
protected override void DoConfigure( EntityTypeBuilder<Entity> builder ) {
builder.HasKey( x => new { x.Id } );
builder.Property( x => x.Id ).HasColumnName( "ID" );
builder.Property( x => x.DueDate ).HasColumnName( "DUEDATE" );
}
}
}


与SQL模式

CREATE TABLE ENTITY_DUE_DATE  (
ID INT NOT NULL,
DUEDATE DATETIME NULL
);



我们的DateTime映射看起来像

public class SqlServerDateTimeMapping : DateTimeTypeMapping {

public SqlServerDateTimeMapping() : base( "DateTime", System.Data.DbType.DateTime ) { }

protected SqlServerDateTimeMapping( RelationalTypeMappingParameters parameters ) : base( parameters ) { }

public override string GenerateSqlLiteral( object value ) {
if ( value is DateTime date ) {
if ( date.Date != date ) {
System.Diagnostics.Debug.Fail( "Time portions are not supported" );
}
return date.ToString( "yyyy-MM-dd" );
}
return base.GenerateSqlLiteral( value );
}

public override RelationalTypeMapping Clone( in RelationalTypeMappingInfo mappingInfo ) => new SqlServerDateTimeMapping();

public override RelationalTypeMapping Clone( string storeType, int? size ) => new SqlServerDateTimeMapping();

public override CoreTypeMapping Clone( ValueConverter converter ) => new SqlServerDateTimeMapping();
}


就像我说的那样,我试图制作一个转换器以插入SqlServerDateTimeMapping的Converter覆盖中,但是我不知道如何在Sql解析之前/期间对其进行处理。似乎转换器可以让您在两种CLR类型之间进行转换,因此可以进行后解析。

为了完整起见,这是我目前的转换器。这是完全错误的。

public class NullableDateTimeToDateTimeConverter : ValueConverter<DateTime?, DateTime> {
public NullableDateTimeToDateTimeConverter() : base(s => s ?? DateTime.MaxValue, x => x ) { }
}


我希望能够使我的Entity类的Property不可为空,使数据库列可为空,并让实体框架引擎的句柄在找到null时返回默认值,而当前在解析null时会炸毁(特别是在任何Context查询内的SqlReader.GetDateTime()调用的SqlBuffer.GetDateTime()中,无论其context.EntityDueDate.ToList()还是context.Set())。

无论解决方案是什么,如果我的查询仍然可以处理数据库端而不是客户端端,那将是理想的选择。所以如果我做到了

context.EntityDueDates.Where(x => x.DueDate > DateTime.UtcNow).ToList()


最好能够在数据库上运行该查询,而不必返回客户端来执行

DueDate ?? DEFAULT_VALUE

逻辑,但是它适合在那里。

我应该澄清一下,该解决方案应该不仅适用于DateTime。我之所以选择DateTime,是因为我手动修复的是DateTime,但是检查数据库显示出这种相同的情况可能会发生几个int / int吗?差异也是如此。我希望给出的解决方案可以在任何可空/不可空数据类型之间应用。

更新:

我有新领导。在SqlServerDateTimeMapping类中,我可以重写该方法:

public override MethodInfo GetDataReaderMethod() {
var methodInfo = base.GetDataReaderMethod();
return methodInfo;
}


并检查methodInfo。果然,这就是方法“ GetDateTime”,与解析时崩溃的方法相同。我的想法是,也许我可以以某种方式返回我自己的方法,该方法可以在处理空检查的SqlDataReader上调用,也可以返回SqlDataReader的子类来重写此方法。我现在注意到,尽管GetDateTime将int作为参数,指的是哪一列。在内部,它在调用以确保它不为null之前调用IsDBNull。我希望传递一个字符串,我可以覆盖其解析,但是看起来这只不过是占用一列并使用SqlBuffer来进行get_dateTime,由谁来进行解析

最佳答案

我已经解决了答案,但是我愿意接受其他解决方案。这“解决了”我的答案,但是将我限制为每个类型只有一个通用的“默认”。有时“最佳”默认值可能是DateTime.MinValue,有时是DateTime.MaxValue,有时是DateTime.UtcNow。违约有其缺陷。我确实认为此答案不会对性能造成影响,并且我相信不会以任何可能使工作在查询的客户端完成的方式弄乱映射。

我的解决方案是制作一个类“ ContextDbDataReaderDecorator”,该类可以在DbDataReader之间来回隐式转换。它在创建时存储DbDataReader,并将其用于自己的GetDataTimeSafe替代方法。隐式转换允许我们从SqlServerDateTimeMapping的“ GetDataReaderMethod”重载中返回它,因为尽管它实际上并不属于DbDataReader,但仍可以在DbDataReader上对其进行调用。

我的关系类型映射对象如下所示:

public class SqlServerDateTimeMapping : DateTimeTypeMapping {
static MethodInfo s_getDateTimeSafeMethod = typeof( ContextDbDataReaderDecorator ).GetMethod( nameof( ContextDbDataReaderDecorator.GetDateTimeSafe ) );

public SqlServerDateTimeMapping() : base( "DateTime", System.Data.DbType.DateTime ) {}

protected SqlServerDateTimeMapping( RelationalTypeMappingParameters parameters ) : base( parameters ) {}

public override string GenerateSqlLiteral( object value ) {
if ( value is DateTime date ) {
if ( date.Date != date ) {
System.Diagnostics.Debug.Fail( "Time portions are not supported" );
}
return date.ToString( "yyyy-MM-dd" );
}
return base.GenerateSqlLiteral( value );
}


public override MethodInfo GetDataReaderMethod() {
return s_getDateTimeSafeMethod;
}

public override RelationalTypeMapping Clone( in RelationalTypeMappingInfo mappingInfo ) => new SqlServerDateTimeMapping();
public override RelationalTypeMapping Clone( string storeType, int? size ) => new SqlServerDateTimeMapping();
public override CoreTypeMapping Clone( ValueConverter converter ) => new SqlServerDateTimeMapping();
}


我创建的ContextDbDataReaderDecorator类如下所示:

public class ContextDbDataReaderDecorator {
public DbDataReader DbDataReader { get; }

public ContextDbDataReaderDecorator( DbDataReader inner ) {
DbDataReader = inner;
}

public static implicit operator DbDataReader( ContextDbDataReaderDecorator self ) => self.DbDataReader;
public static implicit operator ContextDbDataReaderDecorator( DbDataReader other ) => new ContextDbDataReaderDecorator( other );
public DateTime GetDateTimeSafe( int ordinal ) => (DbDataReader.GetValue( ordinal ) as DateTime?) ?? new DateTime( 3000, 1, 1 );
public int GetIntSafe(int ordinal) => (DbDataReader.GetValue( ordinal ) as int?) ?? 0;
public long GetLongSafe( int ordinal ) => (DbDataReader.GetValue( ordinal ) as long?) ?? 0;
public float GetFloatSafe(int ordinal) => (DbDataReader.GetValue( ordinal ) as float?) ?? 0.0f;
}

关于c# - 当列可为空时,应如何处理使实体属性不可为空的问题? -EF核心,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56193020/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com