2013-11-08 2 views
2

Я использую Repository в слое данных содержит следующий метод, предложенный chrisb для обновления лиц, доступ к коду первичного ключ первого перед обновлением:Entity Framework: Обновление Entity с составным первичным ключом

var entry = _dbContext.Entry<T>(entity); 
// Retreive the Id through reflection 
var pkey = _dbset.Create().GetType().GetProperty("Id").GetValue(entity); 
if (entry.State == EntityState.Detached) 
{ 
    var set = _dbContext.Set<T>(); 
    T attachedEntity = set.Find(pkey); // You need to have access to key 
    if (attachedEntity != null) 
    { 
     var attachedEntry = _dbContext.Entry(attachedEntity); 
     attachedEntry.CurrentValues.SetValues(entity); 
    } 
    else 
    { 
     entry.State = EntityState.Modified; // This should attach entity 
    } 
} 

вопрос заключается в том, как использовать этот метод с составным первичным ключом: т.е. когда первичный ключ состоит из двух или более столбцов.

Обновление: моя проблема связана с методом Find(), например, когда я передаю ему две переменные как составной PK, значение attachEntity равно null, и я получаю исключение: «Объект с тем же ключом уже существует в ObjectStateManager. ObjectStateManager не может отслеживать несколько объектов с одним и тем же ключом ».

Update2: здесь полный код метода после модификации

public virtual void Update(T entity, params Object[] pkey) 
    { 
     var entry = _dbContext.Entry<T>(entity); 

     if (entry.State == EntityState.Detached) 
     { 
      var set = _dbContext.Set<T>(); 
      T attachedEntity = set.Find(pkey); // You need to have access to key 
      if (attachedEntity != null) 
      { 
       var attachedEntry = _dbContext.Entry(attachedEntity); 
       attachedEntry.CurrentValues.SetValues(entity); 
      } 
      else 
      { 
       entry.State = EntityState.Modified; // This should attach entity 
      } 
     } 

    } 

Спасибо.

+0

Можете ли вы показать нам новый код, в котором вы передаете 2 переменных для поиска()? – Colin

+0

ОК, я удалил оператор отражения и передал переменные в качестве параметров методу, содержащему код, поэтому код, вызывающий этот метод, передает ему имя первичных ключевых частей. Это работа, но она доступна для меня, потому что я вызываю этот метод из уровня сервиса WCF. – Hussein

+0

Просто добавила эту технику к моему в то же время, когда вы отправили свой комментарий! Работает ли он сейчас? – Colin

ответ

1

Вместо извлечения ключа с помощью отражения, можно передать ключ к вашему методу InsertOrUpdate:

public virtual T InsertOrUpdate(T e, params Object[] pkey) 
    { 
     //.... 
     T attachedEntity = set.Find(pkey); 
     //... 
    } 

Там нет ничего, чтобы предотвратить ошибку, которая будет иметь место, если вы передаете в неправильных значений для первичного ключ.

Другим способ получить ключ в общих методах, чтобы создать абстрактный класс, что ваши объекты наследуют и ограничить хранилище:

public class RepositoryBase<T> : IRepository<T> where T : ModelBase 
{ 

    public virtual T InsertOrUpdate(T e) 
    { 
     //.... 
     T attachedEntity = set.Find(e.ID); 
     //... 
    } 
} 

public abstract class ModelBase 
{ 
    public int ID { get; set; } 
} 

Ссылка: Repository pattern that allows for proxy creation

Я предпочитаю этот метод отражение, потому что он применяется во время компиляции, но вам придется адаптировать его, чтобы справиться с несколькими абстрактными классами. например

public abstract class CompositeBase 
{ 
    public int Key1 {get; set:} 
    public int Key2 {get; set;} 
} 

public virtual T InsertOrUpdate(T e) where T: CompositeBase 
{ 
    //.... 
    T attachedEntity = set.Find(e.Key1, e.Key2); 
    //... 
} 

В качестве альтернативы вы можете адаптировать следующий код для извлечения KeyMembers из метаданных (см GetPrimaryKeyName метод)

private static Dictionary<Type, EntitySetBase> _mappingCache = 
    new Dictionary<Type, EntitySetBase>(); 

private EntitySetBase GetEntitySet(Type type) 
{ 
    if (!_mappingCache.ContainsKey(type)) 
    { 
     ObjectContext octx = ((IObjectContextAdapter)this).ObjectContext; 

     string typeName = ObjectContext.GetObjectType(type).Name; 

     var es = octx.MetadataWorkspace 
         .GetItemCollection(DataSpace.SSpace) 
         .GetItems<EntityContainer>() 
         .SelectMany(c => c.BaseEntitySets 
             .Where(e => e.Name == typeName)) 
         .FirstOrDefault(); 

     if (es == null) 
      throw new ArgumentException("Entity type not found in GetTableName", typeName); 

     _mappingCache.Add(type, es); 
    } 

    return _mappingCache[type]; 
} 

private string GetTableName(Type type) 
{ 
    EntitySetBase es = GetEntitySet(type); 

    return string.Format("[{0}].[{1}]", 
     es.MetadataProperties["Schema"].Value, 
     es.MetadataProperties["Table"].Value); 
} 

private string GetPrimaryKeyName(Type type) 
{ 
    EntitySetBase es = GetEntitySet(type); 

    return es.ElementType.KeyMembers[0].Name; 
} 

код снят с Soft Delete pattern for Entity Framework Code First

Другие ссылки:

EF Code First Mapping Between Types & Tables

Improve MetaData API issue

+0

, пожалуйста, проверьте заявление об обновлении, которое я добавил в конце моего вопроса, что является основной проблемой – Hussein

Смежные вопросы