web-dev-qa-db-ja.com

Entity Framework Coreで主キーの値を取得する方法

現在、抽象リポジトリの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);
}
17
cResults

私も同様の問題に直面し、次の解決策を見つけました

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

少なくとも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];
}
_
8
Pavel Voronin

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);
5
tb-mtg