如何使用 Entity Framework Core 获取主键值

Posted

技术标签:

【中文标题】如何使用 Entity Framework Core 获取主键值【英文标题】:How to get primary key value with Entity Framework Core 【发布时间】:2015-08-21 17:24:04 【问题描述】:

我们目前正在使用下面的方法,它依赖于抽象存储库中的 IObjectContextAdapter。从我正在阅读的内容来看,与 ObjectContext 相关的任何内容似乎都已从 Entity Framework Core 中删除。下面的方法是我们唯一依赖与 ObjectContext 相关的任何东西的地方。

我们想升级到 Entity Framework Core。这是我们唯一的路障。有没有办法通过Entity Framework Core api获取实体的主键值?

// Entity Framework
public virtual int GetKey(T entity)

    var oContext = ((IObjectContextAdapter)_DbContext).ObjectContext;
    var oSet = oContext.CreateObjectSet<T>();
    var keyName = oSet.EntitySet.ElementType
                                .KeyMembers
                                .Select(k => k.Name)
                                .Single();

    return (int)entity.GetType().GetProperty(keyName).GetValue(entity, null);

【问题讨论】:

var entry = _dbContext.Entry(entity); entry.Metadata.FindPrimaryKey().Properties.Select(p =&gt; entry.Property(p.Name).CurrentValue); 【参考方案1】:

我也遇到了类似的问题,找到了以下解决方案

// Entity Framework Core
public virtual int GetKey<T>(T entity)

    var keyName = Context.Model.FindEntityType(typeof (T)).FindPrimaryKey().Properties
        .Select(x => x.Name).Single();

    return (int)entity.GetType().GetProperty(keyName).GetValue(entity, null);

【讨论】:

感谢您的回答。看起来它应该工作。我目前不在那个项目上,但我希望在不久的将来会回来。我会尽快尝试。 是EF7中添加的Context.Model吗?我正在尝试在 EF6 环境中进行更改,但由于该属性不存在而中断。 是的,这是 EF7(核心)属性 在 EntityFrameworkCore v1.1.2 中,FindEntityType() 的签名只需要一个字符串作为实体类型名称。 ... Context.Model.FindEntityType(typeof(T).Name).FindPrimaryKey()... 似乎被 Core 2.1 破坏了。现在只返回空值。【参考方案2】:

至少在 EF Core 2.1 中,您可以通过这种方式获取密钥:

var entry = dbContext.Entry(entity);
object[] keyParts = entry.Metadata.FindPrimaryKey()
             .Properties
             .Select(p => entry.Property(p.Name).CurrentValue)
             .ToArray();

这允许您获取创建为 shadow 属性的密钥。

我不知道调用Property(p.Name).CurrentValue 的性能如何,但是仍然可以缓存键的属性名称:

private static readonly ConcurrentDictionary<Type,string[]> KeyPropertiesByEntityType = new ConcurrentDictionary<Type, string[]>();

public object[] KeyOf<TEntity>(TEntity entity) where TEntity : class

    Guard.ArgumentNotNull(entity, nameof(entity));

    var entry = _dbContext.Entry(entity);
    var keyProperties = KeyPropertiesByEntityType.GetOrAdd(
        entity.GetType(),
        t => entry.Metadata.FindPrimaryKey().Properties.Select(property => property.Name).ToArray());

    var keyParts = keyProperties
        .Select(propertyName => entry.Property(propertyName).CurrentValue)
        .ToArray();

    return keyParts;


public TKey KeyOf<TEntity, TKey>(TEntity entity) where TEntity : class

    var keyParts = KeyOf(entity);
    if (keyParts.Length > 1)
    
        throw new InvalidOperationException($"Key is composite and has 'keyParts.Length' parts.");
    

    return (TKey) keyParts[0];

【讨论】:

【参考方案3】:

根据 YuriyP 的回答,您还可以获得复合键信息:

 public static class Extensions 

    public static IEnumerable<string> FindPrimaryKeyNames<T>(this DbContext dbContext, T entity) 
      return from p in dbContext.FindPrimaryKeyProperties(entity) 
             select p.Name;
    

    public static IEnumerable<object> FindPrimaryKeyValues<T>(this DbContext dbContext, T entity) 
      return from p in dbContext.FindPrimaryKeyProperties(entity) 
             select entity.GetPropertyValue(p.Name);
    

    static IReadOnlyList<IProperty> FindPrimaryKeyProperties<T>(this DbContext dbContext, T entity) 
      return dbContext.Model.FindEntityType(typeof(T)).FindPrimaryKey().Properties;
    

    static object GetPropertyValue<T>(this T entity, string name) 
      return entity.GetType().GetProperty(name).GetValue(entity, null);
    

  

然后可以像这样使用:

var keyValues = context.FindPrimaryKeyValues(entity);    
var existingEntity = context.Set<TEntity>().Find(keyValues);

【讨论】:

当心 PK 是影子属性的可能性。

以上是关于如何使用 Entity Framework Core 获取主键值的主要内容,如果未能解决你的问题,请参考以下文章

Entity Framework 学习系列 - 认识理解Entity Framework

如何使用 Entity Framework Core 模拟异步存储库

如何使用 Entity Framework 生成和自动递增 Id

如何使用 Entity Framework Core 正确保存 DateTime?

如何使用 Entity Framework Core 获取主键值

如何在 Entity Framework 6 中使用 DbDataReader 获取表名?