在实体框架中的另一个DBContext中打开实体

本文关键字:实体 DBContext 框架 另一个 | 更新日期: 2023-09-27 18:21:11

我从一个DBContext加载了一个实体,并对其进行了一些更改。如果我创建了另一个DBCntext,我将如何从数据库中将同一实体加载到其中?这需要在不同的实体之间具有通用性,所以我不能只查找ID相同的地方——有些实体可能具有不同的键属性。

这个问题背后的原因是,我想使用加载到第二个上下文中的实体来验证第一个上下文中一些更改的属性,将以前的值与新的值进行比较。因此,在验证第一个实体期间,第二个上下文是纯只读的。


编辑

我尽量保持简单,我认为需要更多的细节。

假设我有这个实体:

public partial class SomeEntity : IValidatableObject
{
    public int Id { get; set; }
    public int StatusId { get; set; }
    //... Other properties
    public virtual ICollection<SomeOtherEntity> relatedEntity { get; set; }
    public IEnumerable<ValidationResult> Validate(ValidationContext validationContext){
        // validation code
    }
}

我试图做的是,当验证实体时,将StatusId与数据库中当前的StatusId进行比较,可能会出现验证错误——例如,StatusId不能从1更改为5。作为

正如Pawel所提到的,这可以通过使用OriginalValues来完成,但我也需要能够基于relatedEntity中的值来验证StatusId。例如,如果relatedEntity中存在某些值,则StatusId不能从1更改为2。

因此,为了进行验证,我需要从数据库中复制未修改形式的SomeEntity对象,以便将其与我试图修改的对象进行比较。

这有点混乱,但我想到的想法是:

  • 使SomeEntity成为新的空接口IMyValidationInterface的成员。

    public interface IMyValidationInterface
    {
    }
    public partial class SomeEntity : IValidatableObject, IMyValidationInterface
    {
        public int Id { get; set; }
        // ....
    }
    
  • 重写MyDBContext上的ValidateEntity方法,以便将具有原始值的对象传递到实体上的验证方法中。

    public class MyDBContext : DbContext
    {
        protected override DbEntityValidationResult ValidateEntity(DbEntityEntry entityEntry, IDictionary<object, object> items)
        {
            if (entityEntry.Entity is IMyValidationInterface)
            {
                var _validationContext = new MyDbContext();
                /// TODO: some code here to load a duplicated version of 
                ///       the entity from the database
                var originalEntity; // unchanged entity is here
                // unmodified entity is passed as an item to the entities
                // Validate method 
                items.Add("OriginalEntity", originalEntity);
             }
             return base.ValidateEntity(entityEntry, items);
         }
    }
    public partial class SomeEntity : IValidatableObject, IMyValidationInterface
    {
        // .....
        public IEnumerable<ValidationResult> Validate(ValidationContext validationContext){
            // validation code
            if (validationContext.Items.ContainsKey("OriginalEntity")){
                  var originalEntity = (SomeEntity)validationContext.Items["OriginalEntity"];
                  // do validation here and yield return any validation errors
            }
        }
    }
    

我所关注的是上面片段中的TODO部分。

我是不是走错路了?

在实体框架中的另一个DBContext中打开实体

一种快速而肮脏的方法是在将原始状态修改为未映射到数据库的自己的属性或字段之前存储原始状态,最好是在从数据库加载时。

interface IMaterializable
{
    void OnMaterialized();
}
class SomeEntity : IMaterializable, IValidatableObject
{
    public int Id { get; set; }
    public int StatusId { get; set; }
    //... Other properties
    public virtual ICollection<SomeOtherEntity> relatedEntity { get; set; }
    int OriginalStatusId;
    public void OnMaterialized()
    {
        OriginalStatusId = StatusId;
    }
    public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
    {
        // compare current status to original + related
    }
}

然后将此处理程序添加到ObjectMaterialized事件中(请参阅本答案中的示例):

void OnObjectMaterialized( object sender, ObjectMaterializedEventArgs e )
{
    var entity = e.Entity as IMaterializable;
    if ( entity != null )
    {
        entity.OnMaterialized();
    }
}

我最终对数据上下文执行了此操作:

public class MyDBContext : DbContext
{
    public virtual T GetEntityByPrimaryKey<T>(T entity) where T : class
    {
        var entityType = entity.GetType();
        var objectSet = ((IObjectContextAdapter)this).ObjectContext.CreateObjectSet<T>();
        var keyNames = objectSet.EntitySet.ElementType.KeyMembers.Select(edmMember => edmMember.Name);
        var keyValues = keyNames.Select(name => entityType.GetProperty(name).GetValue(entity, null)).ToArray();
        return this.Set<T>().Find(keyValues);
    }
    protected override DbEntityValidationResult ValidateEntity(DbEntityEntry entityEntry, IDictionary<object, object> items)
    {
        if (entityEntry.Entity is IMyValidationInterface)
        {
            var validationContext = new MyDBContext();
            var modifiedEntity = entityEntry.Entity;
            var originalEntity = validationContext.GetEntityByPrimaryKey(a);
            items.Add("OriginalEntity", originalEntity);
        }
        return base.ValidateEntity(entityEntry, items);
    }
}

GetEntityByPrimaryKey在此处找到实体框架按主键获取实体