【问题标题】:Entity Framework : Update an Entity with Composite Primary Key实体框架:使用复合主键更新实体
【发布时间】:2021-05-11 13:00:22
【问题描述】:

我在数据层使用了一个存储库,其中包含 chrisb 建议的更新实体的以下方法,代码在更新之前首先访问主键:

var entry = _dbContext.Entry<T>(entity);
// Retreive the Id through reflection
var pkey = _dbset.Create().GetType().GetProperty("Id").GetValue(entity);
if (entry.State == EntityState.Detached)
{
    var set = _dbContext.Set<T>();
    T attachedEntity = set.Find(pkey); // You need to have access to key
    if (attachedEntity != null)
    {
        var attachedEntry = _dbContext.Entry(attachedEntity);
        attachedEntry.CurrentValues.SetValues(entity);
    }
    else
    {
        entry.State = EntityState.Modified; // This should attach entity
    }
}

问题是如何将此方法与复合主键一起使用:即当主键由两列或多列组成时。

更新:我的问题在于 Find() 方法,例如,当我将两个变量作为复合 PK 传递给它时,attachEntity 为空,我得到了异常: “ObjectStateManager 中已存在具有相同键的对象。ObjectStateManager 无法跟踪具有相同键的多个对象。”

update2:这里是修改后的完整方法代码

public virtual void Update(T entity, params Object[] pkey)
    {
        var entry = _dbContext.Entry<T>(entity);

        if (entry.State == EntityState.Detached)
        {
            var set = _dbContext.Set<T>();
            T attachedEntity = set.Find(pkey);  // You need to have access to key
            if (attachedEntity != null)
            {
                var attachedEntry = _dbContext.Entry(attachedEntity);
                attachedEntry.CurrentValues.SetValues(entity);
            }
            else
            {
                entry.State = EntityState.Modified; // This should attach entity
            }
        }

    }

谢谢。

【问题讨论】:

  • 你能告诉我们你将 2 个变量传递给 Find() 的新代码吗?
  • 好的,我去掉了反射语句,将变量作为参数传递给包含代码的方法,所以调用该方法的代码将主键部分的名称传递给它。这是一种解决方法,但它对我可用,因为我从 WCF 服务层调用此方法。
  • 刚刚在您发表评论的同时将该技术添加到我的帐户中!现在可以用了吗?
  • 我得到与上面相同的异常,并且附加实体为空。对于信息,我使用数据库优先方法和 EF 从数据库创建实体数据模型
  • 能贴出代码吗?

标签: entity-framework


【解决方案1】:

您可以将密钥传递给您的InsertOrUpdate 方法,而不是使用反射检索密钥:

   public virtual T InsertOrUpdate(T e, params Object[] pkey)
   {
      //....
      T attachedEntity = set.Find(pkey);
      //...
   }

如果您为主键传递了错误的值,没有什么可以防止发生的错误。

在泛型方法中获取键的另一种方法是创建一个抽象类,您的实体继承并约束存储库:

public class RepositoryBase<T> : IRepository<T> where T : ModelBase
{

    public virtual T InsertOrUpdate(T e)
     {
         //....
         T attachedEntity = set.Find(e.ID);
         //...
     }
}  

public abstract class ModelBase
{
    public int ID { get; set; }
}

参考: Repository pattern that allows for proxy creation

我更喜欢这种方法而不是反射,因为它是在编译时强制执行的,但是您必须对其进行调整以应对多个抽象类。例如

public abstract class CompositeBase
{ 
   public int Key1 {get; set:}
   public int Key2 {get; set;}
}

public virtual T InsertOrUpdate(T e) where T: CompositeBase
{
   //....
   T attachedEntity = set.Find(e.Key1, e.Key2);
   //...
}

或者,您可以修改以下代码以从元数据中检索 KeyMembers(请参阅 GetPrimaryKeyName 方法)

private static Dictionary<Type, EntitySetBase> _mappingCache = 
   new Dictionary<Type, EntitySetBase>();

private EntitySetBase GetEntitySet( Type type )
{
    if ( !_mappingCache.ContainsKey( type ) )
    {
        ObjectContext octx = ( (IObjectContextAdapter)this ).ObjectContext;

        string typeName = ObjectContext.GetObjectType( type ).Name;

        var es = octx.MetadataWorkspace
                        .GetItemCollection( DataSpace.SSpace )
                        .GetItems<EntityContainer>()
                        .SelectMany( c => c.BaseEntitySets
                                        .Where( e => e.Name == typeName ) )
                        .FirstOrDefault();

        if ( es == null )
            throw new ArgumentException( "Entity type not found in GetTableName", typeName );

        _mappingCache.Add( type, es );
    }

    return _mappingCache[type];
}

private string GetTableName( Type type )
{
    EntitySetBase es = GetEntitySet( type );

    return string.Format( "[{0}].[{1}]", 
        es.MetadataProperties["Schema"].Value, 
        es.MetadataProperties["Table"].Value );
}

private string GetPrimaryKeyName( Type type )
{
    EntitySetBase es = GetEntitySet( type );

    return es.ElementType.KeyMembers[0].Name;
}

Soft Delete pattern for Entity Framework Code First 提取的代码

其他参考资料:

EF Code First Mapping Between Types & Tables

Improve MetaData API issue

【讨论】:

  • 请检查我在问题末尾添加的更新语句,这是核心问题
【解决方案2】:

在实体构建器中

modelBuilder.Entity<T>()
            .HasKey(o => new { key1, key2});

key1key2 是复合键。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-12-20
    • 1970-01-01
    • 1970-01-01
    • 2011-12-10
    相关资源
    最近更新 更多