5

Używam Entity Framework 6.1.3 i mam scenariusz, w którym pobieram obiekt z jego nawigacją nieruchomość (za pomocą include()) i odłączyć je od kontekstu, zmienić klucz obcy Id następnie ponownie podłączyć go do nowego DbContext:Kod EF 6 Po pierwsze, zmiana identyfikatora klucza obcego z właściwością Włącz na nawigacji powoduje "Naruszenie ograniczenia więzów integralności" Błąd

// Init the Db 
using (var db = new MyContext()) 
{ 
    var theWarranty = new ProductWarranty { WarrantyName = "The Warranty" }; 
    var newWarranty = new ProductWarranty { WarrantyName = "New Warranty" }; 
    var brand = new ProductBrand { BrandName = "The Brand", DefaultWarranty = theWarranty }; 

    db.ProductBrands.Add(brand); 
    db.ProductWarranties.Add(newWarranty); 

    db.SaveChanges(); 
} 

// Load the detached Brand 
ProductBrand detachedBrand; 
using (var db = new MyContext()) 
{ 
    detachedBrand = db.ProductBrands.AsNoTracking() 
     .Include(b => b.DefaultWarranty) // <<< If this line is removed the Attach works 
     .First(x => x.Id == 1); 
} 

// Modify the Default Warranty Foreign Key 
detachedBrand.DefaultWarranty = null; 
detachedBrand.DefaultWarranty_Id = 2; 

// Attempt to re-attach and save the changes 
using (var db = new MyContext()) 
{ 
    var entity = db.Set<ProductBrand>().Attach(detachedBrand); // <<< This line throws the exception 

    db.Entry(entity).State = EntityState.Modified; 

    db.SaveChanges(); 
} 

dostaję:

więzów integralności Wystąpiło naruszenie ograniczenia: Wartość (wartości) właściwości> "ProductWarranty.Id" na jednym końcu relacji nie pasuje do właściwości> wartości produktu "ProductBrand.DefaultWarranty_Id" na drugim końcu.

Jednakże, jeśli NIE użyję funkcji Uwzględnij(), załącz działa dobrze.

Potrzebuję właściwości nawigacji (DefaultWarranty) w realnym scenariuszu, ale nie widzę różnicy w włączaniu nawigacji w oddzielonym obiekcie, zamiast ładowania go w odłączonym obiekcie. Z mojego doświadczenia i czytania powinno wynikać ustawienie klucza obcego na nową wartość i ustawienie właściwości nawigacji na wartość null.

Przeczytałem na blogu Ladislava o filmie Klucz obcy kontra własność niezależna http://www.ladislavmrnka.com/2011/05/foreign-key-vs-independent-associations-in-ef-4/, ale nie radzę sobie z tym scenariuszem iz tego, co mogę powiedzieć, używam w tym przypadku kluczy obcych.

Co się dzieje i jaki jest właściwy sposób radzenia sobie ze zmianą kluczy obcych przy uwzględnieniu właściwości nawigacji, takich jak ten scenariusz?

To prawie tak, jakby EF nie "w pełni" odłączyło obiektu, gdy użyto Include ... co również wydaje się dziwne.

Oto uproszczona konfiguracja:

Marka produktu

public partial class ProductBrand 
{ 
    public int Id { get; set; } 
    public string BrandName { get; set; } 

    public Nullable<int> DefaultWarranty_Id { get; set; } 
    public virtual ProductWarranty DefaultWarranty { get; set; } 
} 

Marka produktu Mapa

public class ProductBrandMap : EntityTypeConfiguration<ProductBrand> 
{ 
    public ProductBrandMap() 
    { 
     // Primary Key 
     this.HasKey(t => t.Id); 

     // Properties 
     this.Property(t => t.BrandName) 
      .IsRequired() 
      .HasMaxLength(40); 

     // Table & Column Mappings 
     this.ToTable("ProductBrands"); 
     this.Property(t => t.Id).HasColumnName("Id"); 
     this.Property(t => t.BrandName).HasColumnName("BrandName"); 
     this.Property(t => t.DefaultWarranty_Id).HasColumnName("DefaultWarranty_Id"); 

     // Relationships 
     this.HasOptional(t => t.DefaultWarranty) 
      .WithMany(t => t.ProductBrands) 
      .HasForeignKey(d => d.DefaultWarranty_Id) 
      .WillCascadeOnDelete(false); 
    } 
} 

gwarancji produktu

public partial class ProductWarranty 
{ 
    public ProductWarranty() 
    { 
     this.ProductBrands = new List<ProductBrand>(); 
    } 

    public int Id { get; set; } 
    public string WarrantyName { get; set; } 
    public virtual ICollection<ProductBrand> ProductBrands { get; set; } 
} 

Gwarancja produktów Mapa

public class ProductWarrantyMap : EntityTypeConfiguration<ProductWarranty> 
{ 
    public ProductWarrantyMap() 
    { 
     // Primary Key 
     this.HasKey(t => t.Id); 

     // Properties 
     this.Property(t => t.WarrantyName) 
      .IsRequired() 
      .HasMaxLength(40); 

     // Table & Column Mappings 
     this.ToTable("ProductWarranties"); 
     this.Property(t => t.Id).HasColumnName("Id"); 
     this.Property(t => t.WarrantyName).HasColumnName("WarrantyName"); 
    } 
} 
+0

Przetestowałem to i działa bez żadnego wyjątku! – Alireza

+0

@Alireza - Specyfikacje mojego dewelopera i projektu to VS2013, .net 4.5.2. Ponownie uruchomiłem kod i nadal dostaję błąd. Wysłałem również mój projekt do innego programisty i on sam zachowuje się tak samo ... i jest równie zakłopotany jak ja. Czy podjąłeś konkretne kroki w celu uruchomienia kodu? – mips

+0

Czy wysłałbyś mi swój projekt? – Alireza

Odpowiedz

3

Kiedy ProxyCreationEnabled jest włączona, nawet jeśli podmioty nie są śledzone przez dowolnym kontekście, są one jakoś wewnętrznie ze sobą połączone. Mam na myśli, że wszelkie zmiany w FK są starannie rejestrowane przez dynamiczną jednostkę proxy w celu wymuszenia więzów integralności. Tak, po prostu wyłączyć ProxyCreationEnabled:

public MyContext() 
{ 
    this.Configuration.ProxyCreationEnabled = false; 
} 

Ale jeśli pytasz mnie o zdanie, wolę zmienić podmioty, gdy są śledzone i po modyfikacji ja je odłączyć.

+0

Fantastyczne! Ustawienie ProxyCreationEnabled = false podczas ładowania encji załatwiło sprawę! Co do "Wolę zmieniać jednostki, gdy są śledzone i po modyfikacji odrywam je" - Przykładem jest uproszczona wersja aplikacji WPF, która warstwa użytkownika/interfejsu użytkownika modyfikuje odłączony obiekt, a następnie odsyła go z powrotem do warstwy aplikacji. – mips

0

Doświadczyłem tego samego problemu podczas próby przypisania nowej wartości foreign_key.

Zrobiłem załączyć po aktualizacji klucza obcego na obiekt podstawowy to był problem.

Zamiast tego, teraz dołączam do obiektu, a następnie stosuję zmianę w polu.

public static bool ChangeForeignKeyAssocation(baseobject existing, int newFK, bool throwOnError = true) { 
    try { 
    using (var e = new Entities()) { 
     e.table.Attach(existing); 
     e.Entry(existing).State = EntityState.Modified; 

     existing.related_table_id = newFK; 

     int result = sbe.SaveChanges(); 
     return (result == 1); 
    } 
    } catch (Exception ex) { 
    //LogIt.E(ex); 
    if (throwOnError) { 
     throw ex; 
    } else { 
     return false; 
    } 
    } 
} 
Powiązane problemy