调用实体的 IValidatableObject.Validate 方法时,EF6 代理的引用有时为 null
EF6 proxy's reference is sometimes null when entity's IValidatableObject.Validate method is called
当我实体的 IValidatableObject.Validate
方法被 DbContext 的 SaveChangesAsync
方法调用时,EF6 代理有一个引用有时为 null。
运行 多次使用完全相同的代码会导致不同的行为。如果我在 Validate
方法之外检查我的股票 Sku
属性(即 stock.Sku == null
),它总是 returns 一个物化实体。如果我不这样做,只检查 Validate
方法中的 this.Sku
,那么 this.Sku
将 有时 对于完全相同的实体为 null。 "exact same entity" 我的意思是我在所有测试运行中多次测试具有相同 Id
和 SkuId
的一只股票。我不会在这里创建新股票或更改其 SkuId
属性 的价值。我正在做的一件事是调用股票的 ChangeQuantity
方法,然后保存更改。
此时我最好的猜测是,一旦保存更改被调用,所有实体和参考物化就会被冻结。如果 Sku
属性 尚未至少被访问过一次,那么它将为 null 并在数据库上下文的保存更改代码调用我的对象的 Validate
方法时保持为 null。
我的问题是:为什么会发生这种情况,为什么我不能依赖 属性 可以随时延迟加载?
public abstract class StockBase : RecordBase
{
// Snipped //
[Required, Display(Name = "SKU")]
public Guid SkuId { get; set; }
[Display(Name = "SKU")]
public virtual Sku Sku { get; protected set; }
[Required]
public int Quantity { get; private set; }
[DataType("StockActions")]
public virtual ICollection<StockAction> Actions { get; private set; }
public void ChangeQuantity(DateTime logged, Guid loggedById, int changeInQuantity, string notes = null)
{
TrackChange(logged, loggedById);
Quantity += changeInQuantity;
Actions.Add(new StockAction(logged, loggedById, changeInQuantity));
}
}
public class StandardStock : StockBase, IValidatableObject
{
// Snipped //
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
// Right here is where `this.Sku` is sometimes null!
if (Sku.IsExpiringStock)
{
throw new InvalidOperationException("Standard stock must have a non-expiring SKU.");
}
yield break;
}
}
您似乎在使用延迟加载来填充 Sku 对象。当您手动测试 Sku 属性 时,您会强制延迟加载到 运行,并且该值会具体化。如果您在期望加载上下文时已经在对上下文执行某些操作,或者上下文已被释放,那么它将保持为 null。
如果您总是需要填充此 属性,请考虑在加载实体时显式加载它。这将解决您的延迟加载问题,并消除对数据库的访问。
不幸的是,当通过 Entity Framework 执行验证时,延迟加载被禁用。
https://msdn.microsoft.com/en-us/data/gg193959#Considerations
当我实体的 IValidatableObject.Validate
方法被 DbContext 的 SaveChangesAsync
方法调用时,EF6 代理有一个引用有时为 null。
运行 多次使用完全相同的代码会导致不同的行为。如果我在 Validate
方法之外检查我的股票 Sku
属性(即 stock.Sku == null
),它总是 returns 一个物化实体。如果我不这样做,只检查 Validate
方法中的 this.Sku
,那么 this.Sku
将 有时 对于完全相同的实体为 null。 "exact same entity" 我的意思是我在所有测试运行中多次测试具有相同 Id
和 SkuId
的一只股票。我不会在这里创建新股票或更改其 SkuId
属性 的价值。我正在做的一件事是调用股票的 ChangeQuantity
方法,然后保存更改。
此时我最好的猜测是,一旦保存更改被调用,所有实体和参考物化就会被冻结。如果 Sku
属性 尚未至少被访问过一次,那么它将为 null 并在数据库上下文的保存更改代码调用我的对象的 Validate
方法时保持为 null。
我的问题是:为什么会发生这种情况,为什么我不能依赖 属性 可以随时延迟加载?
public abstract class StockBase : RecordBase
{
// Snipped //
[Required, Display(Name = "SKU")]
public Guid SkuId { get; set; }
[Display(Name = "SKU")]
public virtual Sku Sku { get; protected set; }
[Required]
public int Quantity { get; private set; }
[DataType("StockActions")]
public virtual ICollection<StockAction> Actions { get; private set; }
public void ChangeQuantity(DateTime logged, Guid loggedById, int changeInQuantity, string notes = null)
{
TrackChange(logged, loggedById);
Quantity += changeInQuantity;
Actions.Add(new StockAction(logged, loggedById, changeInQuantity));
}
}
public class StandardStock : StockBase, IValidatableObject
{
// Snipped //
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
// Right here is where `this.Sku` is sometimes null!
if (Sku.IsExpiringStock)
{
throw new InvalidOperationException("Standard stock must have a non-expiring SKU.");
}
yield break;
}
}
您似乎在使用延迟加载来填充 Sku 对象。当您手动测试 Sku 属性 时,您会强制延迟加载到 运行,并且该值会具体化。如果您在期望加载上下文时已经在对上下文执行某些操作,或者上下文已被释放,那么它将保持为 null。
如果您总是需要填充此 属性,请考虑在加载实体时显式加载它。这将解决您的延迟加载问题,并消除对数据库的访问。
不幸的是,当通过 Entity Framework 执行验证时,延迟加载被禁用。
https://msdn.microsoft.com/en-us/data/gg193959#Considerations