Muitos para muitos relacionamentos não salvando

Eu tenho duas entidades com um relacionamento Muitos para Muitos que eu criei no EF 5 Code First. Estes são Service e ServiceItem. A entidade Service contém uma coleção de ServiceItems e o ServiceItem contém uma coleção de Services. Eu posso criar, alterar e salvar dados para qualquer uma das propriedades básicas das entidades sem problemas. Quando eu tento adicionar um ServiceItem para um serviço ou um serviço para um ServiceItem parece funcionar, mas nada é salvo. Verifiquei que todas as tabelas de database apropriadas foram criadas, incluindo uma tabela ServiceItemService com as chaves em cruz. A tabela ServiceItemService do database não recebe nenhuma input quando eu adiciono os itens. Não há erro e tudo parece funcionar perfeitamente.

Estou um pouco perplexo e poderia usar alguma ajuda. Abaixo estão as classs.

A class de serviço;

public class Service { //Default constructor public Service() { //Defaults IsActive = true; ServicePeriod = ServicePeriodType.Monthly; ServicePeriodDays = 0; ServiceItems = new Collection(); } public int ServiceID { get; set; } public string Title { get; set; } public string Description { get; set; } public ICollection ServiceItems { get; set; } public string TermsOfService { get; set; } public ServicePeriodType ServicePeriod { get; set; } public int ServicePeriodDays { get; set; } public bool IsActive { get; set; } } 

A class ServiceItem;

 public class ServiceItem { public ServiceItem() { IsActive = true; } public int ServiceItemID { get; set; } public string Title { get; set; } public string Description { get; set; } public ICollection Services { get; set; } public string UserRole { get; set; } public bool IsActive { get; set; } } 

Este é o mapeamento Fluente que fiz ao tentar depurar esse problema. O mesmo problema aconteceu antes e depois de adicionar este mapeamento.

  public DbSet Services { get; set; } public DbSet ServiceItems { get; set; } protected override void OnModelCreating(DbModelBuilder modelBuilder) { modelBuilder.Entity() .HasMany(p => p.ServiceItems) .WithMany(r => r.Services) .Map(mc => { mc.MapLeftKey("ServiceItemID"); mc.MapRightKey("ServiceID"); mc.ToTable("ServiceItemService"); }); } 

Aqui está o código que eu uso para salvar o item de serviço que inclui 2-3 ServiceItems na coleção Service.ServiceItems. Eu verifiquei cuidadosamente que os ServiceItems estavam na coleção adequada.

  db.Entry(dbService).State = EntityState.Modified; db.SaveChanges(); 

O object dbService não parece ser afetado de alguma forma. Os ServiceItems ainda estão na coleção adequada, mas nenhuma atualização é feita na tabela de database ServiceItemService. Qualquer conselho seria muito bem-vindo.

-Obrigado

Espera-se que nada aconteça.

O que você deseja alterar ou adicionar é um relacionamento entre as entidades Service e ServiceItem . Mas você não pode manipular relacionamentos definindo o estado de uma entidade como Modified . Isso atualiza apenas as propriedades escalares e complexas, mas não as propriedades de navegação (= relacionamentos). (Por exemplo, definir o estado de uma entidade de Service como Modified marcará Service.Title e Service.Description , etc. conforme modificados e garantirá que essas propriedades sejam salvas no database. Mas não se importa com o conteúdo de Service.ServiceItems .)

A única exceção em que você pode alterar um relacionamento definindo o estado como Modified é Associações de Chave Estrangeira . Essas são associações que possuem propriedades de chave estrangeira expostas na sua entidade modelo e podem ocorrer apenas para associações um-para-muitos ou um-para-um. Muitos-para-muitos relacionamentos são sempre associações independentes, o que significa que eles nunca podem ter uma propriedade de chave estrangeira em uma entidade. (Porque os FKs estão na tabela de junit, mas a tabela de junit não é uma entidade e “oculta” de suas classs de modelo.)

Existe uma maneira de manipular diretamente relacionamentos para uma associação muitos-para-muitos, mas é necessário ir até o ObjectContext e seu RelationshipManager que é – na minha opinião – bastante avançado e complicado.

A maneira usual e direta de adicionar e remover inputs de relacionamento de / para uma associação muitos-para-muitos é adicionando itens e removendo itens das collections enquanto as entidades estão anexadas ao contexto. O mecanismo de rastreamento de alterações da EF reconhecerá as alterações feitas e gerará as instruções INSERT, UPDATE e DELETE apropriadas quando você chamar SaveChanges .

O procedimento exato depende se você também deseja salvar Service e / ou ServiceItem como novas entidades ou se desejar apenas adicionar relacionamentos entre entidades existentes. Aqui estão alguns exemplos:

  • service deve ser INSERIDO, todos os itens de serviceItem devem ser INSERIDOS e os relacionamentos entre as entidades devem ser INSERIDOS na tabela de junit também:

     using (var context = new MyContext()) { var service = new Service(); var serviceItem1 = new ServiceItem(); var serviceItem2 = new ServiceItem(); service.ServiceItems.Add(serviceItem1); service.ServiceItems.Add(serviceItem2); context.Services.Add(service); context.SaveChanges(); } 

    Adicionar o service “raiz” do gráfico de object é suficiente porque o EF reconhecerá que todas as outras entidades no gráfico não estão anexadas ao contexto e assumem que elas precisam ser INSERIDAS no database.

  • service já existe e não deve ser INSERIDO, todos os serviceItem s devem ser INSERIDOS e os relacionamentos entre as entidades devem ser INSERIDOS na tabela de junit também:

     using (var context = new MyContext()) { var service = new Service { ServiceID = 15 }; context.Services.Attach(service); var serviceItem1 = new ServiceItem(); var serviceItem2 = new ServiceItem(); service.ServiceItems.Add(serviceItem1); service.ServiceItems.Add(serviceItem2); context.SaveChanges(); } 

    A EF reconhece aqui (quando SaveChanges é chamado) esse service é anexado, mas as outras entidades não são. Nenhum INSERT para o service acontece, mas o serviceItem1/2 será INSERIDO junto com as inputs de relacionamento.

  • service já existe e NÃO deve ser INSERIDO, todos os serviceItem já existem e NÃO devem ser INSERT, mas os relacionamentos entre as entidades devem ser INSERIDOS na tabela de junit:

     using (var context = new MyContext()) { var service = new Service { ServiceID = 15 }; context.Services.Attach(service); var serviceItem1 = new ServiceItem { ServiceItemID = 23 }; context.ServiceItems.Attach(serviceItem1); var serviceItem2 = new ServiceItem { ServiceItemID = 37 }; context.ServiceItems.Attach(serviceItem2); service.ServiceItems.Add(serviceItem1); service.ServiceItems.Add(serviceItem2); context.SaveChanges(); } 
  • Para completar: como remover relacionamentos entre entidades existentes?

     using (var context = new MyContext()) { var service = context.Services .Include(s => s.ServiceItems) // load the existing Items .Single(s => s.ServiceID == 15); var serviceItem1 = service.ServiceItems .Single(s => s.ServiceItemID == 23); // query in memory, no DB query var serviceItem2 = service.ServiceItems .Single(s => s.ServiceItemID == 37); // query in memory, no DB query service.ServiceItems.Remove(serviceItem1); service.ServiceItems.Remove(serviceItem2); context.SaveChanges(); } 

    As duas linhas de relacionamento na tabela de junit que vinculam o serviço 15 com serviceItem 23 e 37 serão excluídas.

Alternativamente, em vez de chamar Attach você pode carregar as entidades existentes do database. Também funcionará:

 var service = context.Services.Single(s => s.ServiceID == 15); 

E o mesmo para os ServiceItem s existentes.