Entity FrameworkCore获取主键值的方法

How to get primary key value with Entity Framework Core

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

我们想升级到 Entity Framework Core。这是我们唯一的障碍。有没有办法通过 Entity Framework 核心 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);
}

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

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

根据 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);

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

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

这允许您获取创建为 影子 属性.

的密钥

我不知道调用的性能如何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];
}