调用实体的 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" 我的意思是我在所有测试运行中多次测试具有相同 IdSkuId 的一只股票。我不会在这里创建新股票或更改其 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