2013-03-06 7 views
1

Я использую Entity Framework 4.4 и у меня есть модель One-To-Many отношения вроде этого:Как добавить объекты в DbContext с рекурсивными отношениями, избегая дублирования дополнений?

class Item { 
    public string keyPart1 { get; set; } 
    public string keyPart2 { get; set; } 

    public virtual Container container { get; set; } 
    public string ContainerId { get; set; } 
} 

// Idea is that many Items will be assigned to a container 
class Container { 
    public string ContainerId { get; set; } 

    private ICollection<Item> _Items; 
    public virtual ICollection<Item> As 
    { 
     get { return _Items ?? (_Items = new HashSet<A>()); } 
     protected set { _Items = value; } 
    } 
} 

Теперь вот DbContext:

public class StorageContext : DbContext 
{ 
    public DbSet<Item> Items { get; set; } 
    public DbSet<Bucket> Buckets { get; set; } 

    public override void OnModelCreating(DbModelBuilder modelBuilder) 
    { 
     base.OnModelCreating(modelBuilder); 
     modelBuilder.Entity<Item>().HasKey(i => new { i.keyPart1, i.keyPart2 }).HasRequired(i => i.Container); 
    } 
} 

Теперь предположим, что у меня есть N Пункт экземпляров. Каждый элемент принадлежит контейнеру, который содержит несколько экземпляров элементов, каждый из которых принадлежит контейнеру, и поэтому модель повторяется бесконечно.

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

foreach (var i in LocalItemList) 
{ 
    IDbSetExtensions.AddOrUpdate<Item>(db.Items, i); 
} 
dbContext.SaveChanges(); 

Проблема, которую я не могу понять, как сказать контекст AddOrUpdateContainer так что я не получаю дубликаты первичных ключей. В какой-то момент мы столкнемся с Item, который имеет тот же Container как другой, но я получу дублирующее первичное ключевое исключение на SaveChanges().

Если I Add Контейнер для DbSet, то есть Item s, который также добавлен в комплект? Как я могу сделать это AddOrUpdate?

ответ

2

Я не уверен, что если вы хотите, чтобы вставить соответствующие Container S в базу данных вместе с Item с, или если вы хотите, чтобы создать отношения к уже существующему Container с. Возможный случай, когда объекты Container не существуют в базе данных, и на вашем графике объектов у вас есть только один Containerэкземпляр за ключ (несколько ссылок на эти экземпляры разрешены) не должно быть проблемой вообще и простым кодом ...

foreach (var i in LocalItemList) 
    { 
     dbContext.Items.Add(i); 
    } 
    dbContext.SaveChanges(); 

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

  • В Container объекты уже существуют в базе данных и в вашем графике объекта у вас есть только один Containerэкземпляр за ключ (несколько экземпляров в эти экземпляры разрешены снова). Это легкий случай, и вы можете решить с помощью:

    foreach (var i in LocalItemList) 
    { 
        dbContext.Containers.Attach(i.Container); 
        dbContext.Items.Add(i); 
    } 
    dbContext.SaveChanges(); 
    

    Если у вас есть несколько Container экземпляров того же ключа, это не будет работать, и бросить »... объект с тем же ключом уже существует в ObjectContext ... " (или подобное) исключение. Он также не будет работать, если Container s еще не существует в базе данных (тогда вы, вероятно, получите нарушение ограничения внешнего ключа).

  • Если у вас несколько экземпляров объектов Container с одним и тем же ключом в графе объектов, вы должны заменить дубликаты на один уникальный экземпляр на ключ, прежде чем присоединить или добавить объекты в контекст.Для того, чтобы сделать этот процесс проще, я бы удалить циклические ссылки, а затем использовать Local коллекцию, чтобы выяснить, если Container с тем же ключом уже прилагается:

    foreach (var i in LocalItemList) 
    { 
        i.Container.Items = null; 
    
        var attachedContainer = dbContext.Containers.Local 
         .SingleOrDefault(c => c.ContainerId == i.Container.ContainerId); 
    
        if (attachedContainer != null) 
         i.Container = attachedContainer; 
        else 
         dbContext.Containers.Attach(i.Container); 
         // or dbContext.Containers.Add(i.Container); 
         // depending on if you want to insert the Container or not 
    
        dbContext.Items.Add(i); 
    } 
    dbContext.SaveChanges(); 
    
1

Это, кажется, работает нормально, до тех пор, так как вы делаете EntityFramework осведомленными о любых контейнерах pre-exisitng.

Например, этот тест запускается в пустой базе данных. Оба элемента находятся в одном контейнере, но EF только один раз вставляет контейнер.

[TestMethod] 
    public void Populate() 
    { 
     const string conStr = "Data Source=(local);Initial Catalog=EfExperiment1; Integrated Security=True"; 
     var context = new MyDbContext(conStr); 

     var container = new Container { ContainerId = "12345" }; 
     var item1 = new Item { keyPart1 = "i1k1", keyPart2 = "i1k2", Container = container }; 
     var item2 = new Item { keyPart1 = "i2k1", keyPart2 = "i2k2", Container = container }; 
     context.Items.Add(item1); 
     context.Items.Add(item2); 
     context.SaveChanges(); 
    } 

Этот тест выполняется, когда контейнер уже существует. Пытаясь прочитать контейнер из db, мы делаем EF осведомленным о любом существующем экземпляре.

[TestMethod] 
    public void PopulateSomeMore() 
    { 
     const string conStr = "Data Source=(local);Initial Catalog=EfExperiment1; Integrated Security=True"; 
     var context = new MyDbContext(conStr); 

     var container = context.Buckets.FirstOrDefault(c => c.ContainerId == "12345") ?? 
         new Container { ContainerId = "12345" }; 

     var item3 = new Item { keyPart1 = "i3k1", keyPart2 = "i3k2", Container = container }; 
     var item4 = new Item { keyPart1 = "i4k1", keyPart2 = "i4k2", Container = container }; 
     context.Items.Add(item3); 
     context.Items.Add(item4); 
     context.SaveChanges(); 
    } 

Этот тест не наедаться существующий контейнер, поэтому EF считает, что нужно вставить и не с дубликатом ключа ошибки.

[TestMethod] 
    public void PopulateSomeMoreAgain() 
    { 
     const string conStr = "Data Source=(local);Initial Catalog=EfExperiment1; Integrated Security=True"; 
     var context = new MyDbContext(conStr); 

     var container = new Container { ContainerId = "12345" }; 
     var item5 = new Item { keyPart1 = "i5k1", keyPart2 = "i5k2", Container = container }; 
     var item6 = new Item { keyPart1 = "i6k1", keyPart2 = "i6k2", Container = container }; 
     context.Items.Add(item5); 
     context.Items.Add(item6); 
     context.SaveChanges(); 
    } 

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

  • Нет связанных вопросов^_^