現在、抽象リポジトリの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);
}
私も同様の問題に直面し、次の解決策を見つけました
// 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);
}
少なくとも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];
}
_
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);