2

我在数据层中使用了一个存储库,其中包含 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 传递给它时,附加实体为空,我得到异常:“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
            }
        }

    }

谢谢。

4

2 回答 2

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; }
}

参考: 允许创建代理的存储库模式

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

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;
}

实体框架代码优先的软删除模式中提取的代码

其他参考:

类型和表之间的 EF 代码优先映射

改进元数据 API 问题

于 2013-11-08T13:31:47.867 回答
1

在实体构建器中

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

key1并且key2是复合键。

于 2021-05-10T12:03:30.730 回答