2014-02-23 67 views
5

我有禁用延遲加載的數據庫上下文。我正在使用熱切加載來加載所有的實體。我無法更新多對多的關係。使用通用存儲庫更新多對多關係

這是存儲庫。

public class GenericRepository<TEntity> : IGenericRepository<TEntity> 
     where TEntity : class 
{ 
    ... other code here... 

    public virtual void Update(TEntity t) 
    { 
     Set.Attach(t); 
     Context.Entry(t).State = EntityState.Modified; 
    } 

    ...other code here... 
} 

這裏是用戶模型。

public partial class User 
{ 
    public User() 
    { 
     this.Locks = new HashSet<Lock>(); 
     this.BusinessModels = new HashSet<BusinessModel>(); 
    } 

    public int UserId { get; set; } 
    public string Username { get; set; } 
    public string Name { get; set; } 
    public string Phone { get; set; } 
    public string JobTitle { get; set; } 
    public string RecoveryEmail { get; set; } 
    public Nullable<double> Zoom { get; set; } 

    public virtual ICollection<Lock> Locks { get; set; } 
    public virtual ICollection<BusinessModel> BusinessModels { get; set; } 
} 

如果我修改的商業模式集合,它保存的商業模式集合雖然我已經連接了整個實體。

Worker.UserRepository.Update(user); 

我不確定發生了什麼事。我不想爲了更新多對多關係而破壞我的通用存儲庫/工作模式單元。

編輯2:我有這個工作......但它是非常不同的模式,我要去。硬實現意味着我需要爲每種類型創建一個具有多對多關係的方法。我現在正在調查,看看我是否可以將此作爲一種通用方法。

編輯3:因此,我以前的實現沒有像我認爲的那樣工作。但是現在,我有一個稍微工作的實現。如果有人願意幫助我,我可以從此繼續前行,我會永遠愛你。

public virtual void Update(TEntity updated, 
    IEnumerable<object> set, 
    string navigationProperty, 
    Expression<Func<TEntity, bool>> filter, 
    Type propertyType) 
{ 
    // Find the existing item 
    var existing = Context.Set<TEntity>().Include(navigationProperty).FirstOrDefault(filter); 

    // Iterate through every item in the many-to-many relationship 
    foreach (var o in set) 
    { 
     // Attach it if its unattached 
     if (Context.Entry(o).State == EntityState.Detached) 
      // Exception "an object with the same key already exists" 
      // This is due to the include statement up above. That statement 
      // is necessary in order to edit the entity's navigation 
      // property. 
      Context.Set(propertyType).Attach(o); 
    } 

    // Set the new value on the navigation property. 
    Context.Entry(existing).Collection(navigationProperty).CurrentValue = set; 

    // Set new primitive property values. 
    Context.Entry(existing).CurrentValues.SetValues(updated); 
    Context.Entry(existing).State = EntityState.Modified; 
} 

我再這樣稱呼它:

Worker.UserRepository.Update(user, user.BusinessModels, "BusinessModels", i => i.UserId == user.UserId, typeof (BusinessModel)); 

非常凌亂,但它讓我更新許多一對多使用泛型的關係。當我去附加已經存在的新值時,我的大問題就是例外。由於包含聲明,它們已經被加載。

這工作:

Update fromUpdate to

這不:

Update fromUpdate to

+0

它不這樣工作。您需要分別更新BusinessModel。 – Kaf

+0

我有,但它不能保存兩者之間的關係。我需要更新用戶內部的業務模型集合還是調用業務模型庫並將其保存在那裏? – dimiguel

+0

分別更新關係。 – Kaf

回答

7

許多痛苦小時後,我終於找到了一種方法來更新許多一對多與完全通用的存儲庫的關係。這將允許我創建(並保存)許多不同類型的實體,而無需爲每個實體創建樣板代碼。

此方法假定:

  • 你的實體已經存在
  • 你的多對多的關係被存儲在一個表的複合鍵
  • 您正在使用預先加載來加載你的人際關係的來龍去脈
  • 您正在使用工作單元/通用存儲庫模式來保存實體。

這裏是更新通用方法。

public virtual void Update(Expression<Func<TEntity, bool>> filter, 
    IEnumerable<object> updatedSet, // Updated many-to-many relationships 
    IEnumerable<object> availableSet, // Lookup collection 
    string propertyName) // The name of the navigation property 
{ 
    // Get the generic type of the set 
    var type = updatedSet.GetType().GetGenericArguments()[0]; 

    // Get the previous entity from the database based on repository type 
    var previous = Context 
     .Set<TEntity>() 
     .Include(propertyName) 
     .FirstOrDefault(filter); 

    /* Create a container that will hold the values of 
     * the generic many-to-many relationships we are updating. 
     */ 
    var values = CreateList(type); 

    /* For each object in the updated set find the existing 
     * entity in the database. This is to avoid Entity Framework 
     * from creating new objects or throwing an 
     * error because the object is already attached. 
     */ 
    foreach (var entry in updatedSet 
     .Select(obj => (int)obj 
      .GetType() 
      .GetProperty("Id") 
      .GetValue(obj, null)) 
     .Select(value => Context.Set(type).Find(value))) 
    { 
     values.Add(entry); 
    } 

    /* Get the collection where the previous many to many relationships 
     * are stored and assign the new ones. 
     */ 
    Context.Entry(previous).Collection(propertyName).CurrentValue = values; 
} 

這是我在網上找到的幫助器方法,它允許我創建基於我給它的任何類型的泛型列表。

public IList CreateList(Type type) 
{ 
    var genericList = typeof(List<>).MakeGenericType(type); 
    return (IList)Activator.CreateInstance(genericList); 
} 

,從現在起,這就是調用更新許多一對多的關係是這樣的:

Worker.UserRepository.Update(u => u.UserId == user.UserId, 
    user.BusinessModels, // Many-to-many relationship to update 
    Worker.BusinessModelRepository.Get(), // Full set 
    "BusinessModels"); // Property name 

當然,最終你會需要在一些地方電話:

Context.SaveChanges(); 

我希望這可以幫助那些從未真正發現如何在Entity Framework中使用通用存儲庫和工作單元類的多對多關係的人。

+0

FYI Enumerable.Empty ().ToList()是創建通用列表/枚舉的一個很好的乾淨方法。 –

+0

@dimiguel很好的答案。 – footyapps27

0

@dimgl您的解決方案適用於我。我已經在另外做的是用動態檢索那些更換的PrimaryKey的硬編碼類型和名稱:

ObjectContext objectContext = ((IObjectContextAdapter)context).ObjectContext; 
ObjectSet<TEntity> set = objectContext.CreateObjectSet<TEntity>(); 
IEnumerable<string> keyNames = set.EntitySet.ElementType.KeyMembers.Select(k => k.Name); 

var keyName = keyNames.FirstOrDefault(); 
var keyType = typeof(TEntity).GetProperty(keyName).PropertyType 

foreach (var entry in updatedSet 
       .Select(obj => 
        Convert.ChangeType(obj.GetType() 
              .GetProperty(keyName) 
              .GetValue(obj, null), keyType)) 
       .Select(value => context.Set<TEntity>().Find(value))) 
      { 
       values.Add(entry); 
      } 

你這樣的代碼不會依賴於實體鍵的名稱和類型。