【问题标题】:Open Entity in another DBContext in Entity Framework在实体框架的另一个 DBContext 中打开实体
【发布时间】:2014-09-09 20:12:13
【问题描述】:

我从一个 DBContext 加载了一个实体,并对其进行了一些更改。如果我然后创建另一个 DBContext,我将如何从数据库中将相同的实体加载到其中?这需要跨不同实体通用,因此我不能只查找 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
            }
        }
    }
    

我卡在上面的 sn-p 中的 TODO 部分。

我是不是走错路了?

【问题讨论】:

  • 您是否正在保存从第一个 DBContext 加载的已修改实体上的更改?
  • 也许您可以使用 OriginalValues 而不必拥有多个上下文?
  • 我认为您只需要知道对实体进行了哪些更改即可进行验证。如果有帮助请查看此链接,[LINK]:stackoverflow.com/questions/3265257/…
  • 您是否考虑过直接查询 EF 模型以从实体中获取实际的关键信息?

标签: c# asp.net-mvc entity-framework


【解决方案1】:

一种快速而简单的方法是在将原始状态修改为未映射到数据库的自己的属性或字段之前存储原始状态,理想情况下是从数据库加载时。

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 事件(参见this answer 中的示例):

void OnObjectMaterialized( object sender, ObjectMaterializedEventArgs e )
{
    var entity = e.Entity as IMaterializable;

    if ( entity != null )
    {
        entity.OnMaterialized();
    }
}

【讨论】:

  • 这是我的 B 计划。我真的想确保在保存之前我有一个来自数据库的最新版本。如果在创建模型之后但在保存之前被其他人更改,OriginalStatusId 在数据库中可能会有所不同。在验证期间再次加载它会降低这种风险。
【解决方案2】:

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

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 在这里找到Entity Framework Get Entity By Primary Key

【讨论】:

    猜你喜欢
    • 2018-05-30
    • 1970-01-01
    • 2017-05-14
    • 1970-01-01
    • 1970-01-01
    • 2013-12-26
    • 2022-12-12
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多