2015-01-08 4 views
0

Я попробую объяснить просто мою модель Entity Framework. У меня есть объект User, у которого есть коллекция из нуля или более объектов UserInterest. Каждый объект интереса пользователя имеет только три свойства, уникальный идентификатор, идентификатор пользователя и описание.Не удается обновить объекты виртуального списка объектов Entity Framework

Всякий раз, когда пользователь обновляет объект User, он также должен обновлять связанные объекты UserInterest, но поскольку они являются бесплатной формой (то есть не являются частью списка разрешенных интересов), я хочу, чтобы пользователь передал список типов " строка "к веб-методу имен всех их интересов. В идеале код будет выглядеть в списке существующих пользователей, удалять все, что уже не актуально и добавлять новые, и оставить те, которые уже существуют.

Мои определения объектной модели

[Table("User")] 
public class DbUser { 
    [Key] 
    [DatabaseGenerated(DatabaseGeneratedOption.Identity)] 
    public int UserId { get; set; } 

    public virtual IList<DbUserInterest> Interests { get; set; } 
} 

[Table("UserInterest")] 
public class DbUserInterest : IEntityComparable<DbUserInterest> 
{ 
    [Key] 
    [DatabaseGenerated(DatabaseGeneratedOption.Identity)] 
    public long Id { get; set; } 

    public virtual DbUser User { get; set; } 
    public int? UserId { get; set; } 
} 

Контекст Fluent отображения

modelBuilder.Entity<DbUser>() 
    .HasKey(u => u.UserId); 
modelBuilder.Entity<DbUser>() 
    .Property(u => u.UserId) 
    .HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity); 
modelBuilder.Entity<DbUser>() 
    .HasMany(u => u.Interests) 
    .WithRequired(p => p.User) 
    .WillCascadeOnDelete(true); 

modelBuilder.Entity<DbUserInterest>() 
    .HasKey(p => p.Id); 
modelBuilder.Entity<DbUserInterest>() 
    .Property(p => p.Id) 
    .HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity); 
modelBuilder.Entity<DbUserInterest>() 
    .HasRequired(p => p.User) 
    .WithMany(u => u.Interests) 
    .WillCascadeOnDelete(true); 

И, наконец, мой WebMethod код и метод хранилища, чтобы сделать обновление

public UpdateUserProfileDetailsResponse UpdateUserProfileDetails(UpdateUserProfileDetailsRequest request) 
{ 
    try 
    { 
     var dbItem = _userDataRepository.GetItem(request.Header.UserId); 

     dbItem.Interests.Clear(); 
     foreach (var dbInterest in request.UserInterests) 
      dbItem.Interests.Add(new DbUserInterest { Name = dbInterest, UserId = dbItem.UserId}); 

     _userDataRepository.UpdateItem(dbItem); 
     _userDataRepository.Save(); 
    } 
    catch (Exception ex) 
    { 
    } 
} 

public override bool UpdateItem(DbUser item) 
{ 
    var dbItem = GetItem(item.UserId); 
    if (dbItem == null) 
     throw new DataRepositoryException("User not found to update", "UserDataRepository.UpdateItem"); 

    var dbInterests = Work.Context.UserInterests.Where(b => b.UserId == item.UserId).ToList(); 
    var interestsToRemove = (from interest in dbInterests let found = item.Interests.Any(p => p.IsSame(interest)) where !found select interest).ToList(); 
    var interestsToAdd = (from interest in item.Interests let found = dbInterests.Any(p => p.IsSame(interest)) where !found select interest).ToList(); 

    foreach (var interest in interestsToRemove) 
     Work.Context.UserInterests.Remove(interest); 

    foreach (var interest in interestsToAdd) 
    { 
     interest.UserId = item.UserId; 
     Work.Context.UserInterests.Add(interest); 
    }     

    Work.Context.Entry(dbItem).State = EntityState.Modified; 
    return Work.Context.Entry(dbItem).GetValidationResult().IsValid; 
} 

Когда я запускаю это, на линии Repository.Save() Я получаю исключение

Assert.IsTrue failed. An unexpected error occurred: An error occurred while updating the entries. See the inner exception for details. 

Но интересно в WebMethod, если я закомментировать строку dbItem.Interests.Clear(); не выдаст ошибку, хотя тогда, конечно, вы получаете дубликаты или дополнительные элементы, как он думает, что все это новый интерес добавить. Однако удаление этой строки является единственным способом получить код, чтобы не ошибка

Раньше у меня было свойство UserId объекта Interest, которое было равно NULL, а затем ошибка была немного иной, что-то о вас не может изменить отношение внешнего ключа, который не имеет значения NULL, поэтому я изменил свойство на nullable, но все равно не пошел.

Любые мысли?

+0

Что внутреннее исключение? – IronMan84

+0

Это было нудно досадно – NZJames

ответ

0

Вы не можете просто очистить коллекцию, а затем попытаться ее перестроить. EF не работает. DbContext отслеживает все объекты, которые были возвращены из базы данных в своем Tracker изменений. Разумеется, делать это по-разному, потому что EF видит, что они вообще не находятся в Track Tracker, поэтому они должны быть совершенно новыми объектами, требующими добавления в базу данных.

Вам нужно либо сделать логику добавления/удаления в вашем методе UpdateUserProfileDetails, либо вам нужно найти способ передать request.UserInterests в ваш метод UpdateItem. Поскольку вам нужно настроить существующие объекты, а не те, которые были найдены в запросе (что, по мнению EF, является новым).

+0

Спасибо, это объясняет это красиво – NZJames

0

вы могли бы попробовать таким образом удалить

dbItem.Interests.Clear(); 

затем

foreach (var dbInterest in request.UserInterests){ 
     if(dbItem.Interests.Any()){ 
     if (dbItem.Interests.Count(i=> i.Name==dbInterest && i.UserId==dbItem.UserId) == 0){ 
      dbItem.Interests.Add(new DbUserInterest { Name = dbInterest, UserId = dbItem.UserId}); 
     } 
     } 
    } 
Смежные вопросы