オブジェクトの主キーを見つけるためにEF4を使用してジェネリックメソッドを作成しようとしています。
例
public string GetPrimaryKey<T>()
{
...
}
詳細情報を提供するために、私はTekpub StarterKitで作業しています。以下は、私が立ち上げて実行しようとしているクラスです。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Data.Objects;
using System.Data.Objects.ELinq;
using System.Data.Linq;
using Web.Infrastructure.Storage.EF4;
namespace Web.Infrastructure.Storage {
public class EFSession:ISession {
PuzzleEntities _db;//This is an ObjectContext
public EFSession() {
_db = new PuzzleEntities();
}
public void CommitChanges() {
_db.SaveChanges();
}
/// <summary>
/// Gets the table provided by the type T and returns for querying
/// </summary>
private ObjectSet<T> GetObjectSet<T>() where T:class {
return _db.CreateObjectSet<T>();
}
private T GetByPrimaryKey<T>() where T: class
{
.....
}
public void Delete<T>(System.Linq.Expressions.Expression<Func<T, bool>> expression) where T: class{
foreach (T item in All<T>().Where(expression))
{
GetObjectSet<T>().DeleteObject(item);
}
}
public void Delete<T>(T item) where T : class {
GetObjectSet<T>().DeleteObject(item);
}
public void DeleteAll<T>() where T : class {
foreach(T item in All<T>())
{
GetObjectSet<T>().DeleteObject(item);
}
}
public void Dispose() {
_db.Dispose();
}
public T Single<T>(System.Linq.Expressions.Expression<Func<T, bool>> expression) where T:class {
return GetObjectSet<T>().SingleOrDefault(expression);
}
public IQueryable<T> All<T>() where T : class {
return GetObjectSet<T>().AsQueryable();
}
public void Add<T>(T item) where T : class {
GetObjectSet<T>().AddObject(item);
}
public void Add<T>(IEnumerable<T> items) where T : class {
foreach (T item in items)
{
GetObjectSet<T>().AddObject(item);
}
}
public void Update<T>(T item) where T : class {
//nothing needed here
}
}
}
それで、私はついにこれを機能させる方法を見つけることができました。コードを書かなかったので、昨夜読んだブログへのリンクを失っていなかったらよかったのに。
public T GetByPrimaryKey<T>(int id) where T : class
{
return (T)_db.GetObjectByKey(new EntityKey(_db.DefaultContainerName + "." + this.GetEntityName<T>(), GetPrimaryKeyInfo<T>().Name, id));
}
string GetEntityName<T>()
{
string name = typeof(T).Name;
if (name.ToLower() == "person")
return "People";
else if (name.Substring(name.Length - 1, 1).ToLower() == "y")
return name.Remove(name.Length - 1, 1) + "ies";
else if (name.Substring(name.Length - 1, 1).ToLower() == "s")
return name + "es";
else
return name + "s";
}
private PropertyInfo GetPrimaryKeyInfo<T>()
{
PropertyInfo[] properties = typeof(T).GetProperties();
foreach (PropertyInfo pI in properties)
{
System.Object[] attributes = pI.GetCustomAttributes(true);
foreach (object attribute in attributes)
{
if (attribute is EdmScalarPropertyAttribute)
{
if ((attribute as EdmScalarPropertyAttribute).EntityKeyProperty == true)
return pI;
}
else if (attribute is ColumnAttribute)
{
if ((attribute as ColumnAttribute).IsPrimaryKey == true)
return pI;
}
}
}
return null;
}
これが他の誰かに役立つことを願っています。私が言えるのは、これを行う方法についてもう少し明確にする必要があるということだけです。
各EF4エンティティにはEntityKey
というプロパティがあり、EntityKeyValues
の配列が含まれています(複合キーの場合は配列があります)。
これをエンティティインスタンスで直接参照することも、内部でこれを行う汎用ヘルパーメソッドを作成することもできます。サンプルコードを試してみることができたら、ここに投稿します。
編集:EntityKeyValueはKeyValuePair<TKey, TValue>
ここで、key
はエンティティの主キーフィールドであり、value
は関連する値です。
たとえば、主キーがフィールドCompany
であるSymbol
というエンティティがあります。
var firstCompany = (from c in context.Companies select c).FirstOrDefault();
var kvp = firstCompany.EntityKey.EntityKeyValues[0];
// kvp shows {[Symbol, FOO]}
サンドボックスで、コードでエンティティを作成したときに、このプロパティがnull
であることに気付きました。しかし、データベースからエンティティを読み取ると、正しく入力されました。したがって、主キーのEF4の概念は、データベースに到達したときにのみ機能するように見えます。ただし、必要に応じて、事前に明示的に設定することもできます。
「エンティティフレームワークで主キーを見つける方法」を見るだけで、多くの人がこの投稿に立ち寄ると思います。 EFバージョンに関係なく(私のように)。したがって、EF 6.1では、主キーを取得するための拡張メソッドを作成することもできます。以下は例であり、完全に正常に機能します。
PS:それが複合キーと複合キーで機能するかどうかは100%わかりません。
using System;
using System.Data.Entity;
using System.Data.Entity.Core.Metadata.Edm;
using System.Data.Entity.Infrastructure;
using System.Linq;
namespace System.Data.Entity
{
public static class DbContextExtensions
{
public static string[] GetKeyNames<TEntity>(this DbContext context)
where TEntity : class
{
return context.GetKeyNames(typeof(TEntity));
}
public static string[] GetKeyNames(this DbContext context, Type entityType)
{
var metadata = ((IObjectContextAdapter)context).ObjectContext.MetadataWorkspace;
// Get the mapping between CLR types and metadata OSpace
var objectItemCollection = ((ObjectItemCollection)metadata.GetItemCollection(DataSpace.OSpace));
// Get metadata for given CLR type
var entityMetadata = metadata
.GetItems<EntityType>(DataSpace.OSpace)
.Single(e => objectItemCollection.GetClrType(e) == entityType);
return entityMetadata.KeyProperties.Select(p => p.Name).ToArray();
}
}
}
これは不必要に長いようですか?私にも同じニーズがあり、上記の提案(SethOとdenis_nによる)を使用して、次のものを使用しています。
//get the primary key field name and location for the table
var primaryFieldName = entry.EntitySet.ElementType.KeyMembers[0].Name ;
int primaryFieldLocation = entry.CurrentValues.GetOrdinal(primaryFieldName);
//gets the value pair for the primary key (returns field name + value)
var primaryField = entry.EntityKey.EntityKeyValues[primaryFieldLocation];
String primaryFieldValue = primaryField.Value.ToString();
これが興味のある人に役立つことを願っています