EF

2013-08-01 34 views
5

'daki bir nesneyi kaydederken veritabanında birçok bağlantıya yeni bağlantı yapılmadı. İki nesnenin arasında çok fazla bağlantı kurmaya çalıştığım kodla ilgili bir sorunum var, ancak bir neden olmasa bile kaydedildi.EF

, veri tabanımızda şu varlıklara sahip bu sorun hakkında Bizim veritabanı oluşturmak için kod ilk yöntem kullanılır

olduğunu burada:

public class Product 
{ 
    public int Id { get; set; } 
    public string Name { get; set; } 
    public virtual ICollection<ProductTag> ProductTags { get; set; } 
} 
public class ProductTag 
{ 
    public int Id { get; set; } 
    public string Name { get; set; } 
    public virtual ICollection<Product> Products { get; set; } 
} 

elbette sadece bir bağlantı taşımaktadır ProductTagProducts otomatik olarak oluşturulur var masa, iki arasındaki tablo.

Artık ürünler oluşturmak iyi çalışıyor. Biz sadece şu çalıştırabilir ve ProductTagProducts tablosundaki Connnections oluşturur:

Product.ProductTags.Add(productTag); 

yinelenen görevler veritabanında emin, bunu kendimiz için tasarruf işlemek yapmak. ProductTag her zaman mevcut bir kimliğe sahip bir ürün etiketi içerir.

Aynı veya başka bir ürünü düzenlemek istediğimizde sorun ortaya çıkıyor. Ürün için mevcut etiketler var. Ve bunu kaydetmek için aşağıdaki işlemi kullanın:

List<ProductTag> productTags = new List<ProductTag>(); 
string[] splittedTags = productLanguagePost.TagList.Split(','); 
foreach (string tag in splittedTags) { 
    ProductTag productTag = new ProductTag(); 
    productTag.Name = tag; 
    productTags.Add(productTagRepository.InsertAndOrUse(productTag)); 
} 

Biz o HTML öğesi alındıktan nasıl, virgül ile etiketler bölün. Sonra bunun için yeni bir varlık tanımlamak ve etiketin zaten var olup olmadığını belirlemek için InsertAndOrUse kullanın. Etiket zaten mevcutsa, aynı varlığı döndürür, ancak kimliğin doldurulduğu halde, henüz mevcut değilse, etiketi veritabanına ekler ve ardından kimliğe sahip öğeyi de döndürür. Ürünün kopya kimliğine sahip olmadığından emin olmak için yeni bir liste oluşturuyoruz (ürünün mevcut etiket listesine doğrudan, aynı sonucu ekleyerek denedim).
product.ProductTags = productTags; 
productRepository.InsertOrUpdate(product); 
productRepository.Save(); 

Sonra ProductTags listeyi ayarlamak ve depo insert veya güncelleme, tabii ki, bir güncelleme yapılacaktır halledeyim.

public void InsertOrUpdate(Product product) { 
    if (product.Id == default(int)) { 
     context.Products.Add(product); 
    } else { 
     context.Entry(product).State = EntityState.Modified; 
    } 
} 

tasarruf yöntemi sadece bağlam en SaveChanges yöntemini çağırır: Her ihtimale karşı, bu InsertOrUpdate işlevidir. Ürünü düzenlediğimde ve başka bir etiket eklediğimde yeni etiketi kaydetmez. Kurtarmam işlevi bir kesme noktası ayarlamak Ancak, ben her ikisi de orada olduğunu görebilirsiniz: enter image description here

Ve yeni eklenen etiketi açtığınızda 'Oeh-la-la' Hatta aracılığıyla ürüne dönüp bakabileceğiniz Ancak, diğer tüm değerler ile başarılı olan kayıt gerçekleştiğinde, ProductTagProducts tablosunda hiçbir bağlantı yapılmaz. Belki bu gerçekten basit bir şey, ama şu anda clueless. Umarım bir başkası parlak bir görünüm verebilir.

Şimdiden teşekkürler.

Düzenleme: ProductTag'ın InsertAndOrUse yöntemini istediği gibi. Çağırdığı InsertOrUpdate yöntemi, tam olarak yukarıdakiyle aynıdır.

public ProductTag InsertAndOrUse(ProductTag productTag) 
{ 
    ProductTag resultingdProductTag = context.ProductTags.FirstOrDefault(t => t.Name.ToLower() == productTag.Name.ToLower()); 

    if (resultingdProductTag != null) 
    { 
     return resultingdProductTag; 
    } 
    else 
    { 
     this.InsertOrUpdate(productTag); 
     this.Save(); 
     return productTag; 
    } 
} 
+0

Tüm bu eylemler için tek bir bağlam mı kullanıyorsunuz? –

+0

Evet. Tüm kontrolörlerimize bağlamın aynı örneğini uygulamak için Ninject'i kullanıyorum. – Chris

cevap

6

Sen ... bu çizgiyi bilmek

context.Entry(product).State = EntityState.Modified; 

var ... ilişkisinin durumu üzerinde hiçbir etkisi yoktur. Sadece product'un Entry'a Modified olarak geçirildiği, yani skaler özellik Product.Name'un değiştirilmiş olarak ve başka hiçbir şekilde işaretlenmediğine işaret eder. Veritabanına gönderilen SQL UPDATE deyimi, yalnızca Name özelliğini günceller. Çoktan çoğa bağlantı tablosuna hiçbir şey yazmaz.

Bu çizgiyle ilişkilerinizi değiştirebileceğiniz tek durum, yabancı anahtar ilişkilendirmelerdir; örneğin, modelde özellik olarak gösterilen yabancı anahtarın bulunduğu ilişkilendirmelerdir.

Şimdi, çoktan çoğa ilişkiler hiçbir zaman yabancı anahtar ilişkilendirmeleri değildir, çünkü yabancı anahtarlar, modelinizde karşılık gelen bir varlığa sahip olmayan bağlantı tablosunda olduğundan, yabancı bir anahtarı modelinize gösteremezsiniz. Çoktan çoğa ilişkiler her zaman bağımsız derneklerdir. (Oldukça gelişmiş olduğunu ve aşağı ObjectContext gitmek gerekir) bağımsız dernekler sadece Varlık Framework'ün değişim izlemeyi kullanmaya eklenebilir veya silinebilir ilişki durumu girişlerinin doğrudan manipülasyonlara bir yana

. Ayrıca, bir etiketin kullanıcı tarafından kaldırıldığını ve bağlantı tablosunda bir ilişki girişinin silinmesini gerektirdiğini hesaba katmanız gerekir. Böyle bir değişikliği takip etmek için, söz konusu ürüne ait mevcut tüm etiketlerin öncelikle veritabanından yüklenmesi gerekir.

(yeni bir ihtisas yöntem veya tanıtmak) Eğer InsertOrUpdate yöntemini değiştirmek zorunda kalacak hep birlikte bu koymak için:

kodunuzu parçacıkları
public void InsertOrUpdate(Product product) { 
    if (product.Id == default(int)) { 
     context.Products.Add(product); 
    } else { 
     var productInDb = context.Products.Include(p => p.ProductTags) 
      .SingleOrDefault(p => p.Id == product.Id); 

     if (productInDb != null) { 
      // To take changes of scalar properties like Name into account... 
      context.Entry(productInDb).CurrentValues.SetValues(product); 

      // Delete relationship 
      foreach (var tagInDb in productInDb.ProductTags.ToList()) 
       if (!product.ProductTags.Any(t => t.Id == tagInDb.Id)) 
        productInDb.ProductTags.Remove(tagInDb); 

      // Add relationship 
      foreach (var tag in product.ProductTags) 
       if (!productInDb.ProductTags.Any(t => t.Id == tag.Id)) { 
        var tagInDb = context.ProductTags.Find(tag.Id); 
        if (tagInDb != null) 
         productInDb.ProductTags.Add(tagInDb); 
       } 
     } 
    } 
ben emin değilim Yukarıdaki çünkü kodunda Find kullanıyordum

product.ProductTags koleksiyonundaki etiketler context örneğine bağlıysa veya eklenmediyse (InsertAndOrUse tam kodu eksik). Find kullanarak, eklenmiş olsun ya da olmasın, bir etiket yüklemek için bir veritabanı gidiş geliş pahasına olursa olsun çalışmalıdır. product.ProductTags tüm etiketlerini değiştirebilir iliştirmişseniz

...

    var tagInDb = context.ProductTags.Find(tag.Id); 
        if (tagInDb != null) 
         productInDb.ProductTags.Add(tagInDb); 

... sadece

    productInDb.ProductTags.Add(tag); 

tarafından Yoksa hepsi bağlı olduğu garanti edilemez ve istediğiniz değilse Veritabanına gidiş gelişinden kaçınmak (etiketlerin en azından eklendiğinde veya eklenmediğinden emin olduğunuzdan emin olabilirsiniz) ile kodu değiştirebilirsiniz:

    var tagInDb = context.ProductTags.Local 
         .SingleOrDefault(t => t.Id == tag.Id); 
        if (tagInDb == null) { 
         tagInDb = tag; 
         context.ProductTags.Attach(tagInDb); 
        } 
        productInDb.ProductTags.Add(tagInDb); 
+0

Cevabınız için çok teşekkür ederim. SingleOrDefault() işlevini .SingleOrDefault (p => p.Id == product.Id) olarak değiştirdim, aksi halde bir istisna oluşturdu. Kodun geri kalanı çalışır, ancak sorun, productInDb'nin eklemeye çalıştığım etikete zaten sahip olmasıdır. Yani her ikisi de her zaman geri dönüyor! Ürünü, bu kodun düzgün bir şekilde çalışmasını engelleyen bir yere çok yakın bir zamanda içeri sokmam mümkün mü? – Chris

+0

@Chris: Yanlış 'SingleOrDefault' için özür dilerim, bunu düzelttim. Diğer problemini tam olarak anlamıyorum. Bir etiket DB ile 'productInDb' arasında zaten ilişkiliyse ve bunu UI'ye tekrar eklemeyi denerseniz, kod aslında bu etiketle hiçbir şey yapmaz. Bu yüzden her ikisi de" Any "çağrıları * true * döndürür; "productInDb.ProductTags" öğesinden bir 'Çıkarma 'veya bu koleksiyona' Ekle 'işlemi yapılmalıdır. Senin noktanı yanlış mı anladım? – Slauma

+0

Merhaba Slauma. Bağıntı aslında veritabanında mevcut değil, ancak bağlamda var. Yani, SingleOrDefault yönteminden hemen sonra kırıldığımda ve ürüne baktığımda. Eklemeye çalıştığım Tag tam orada, ancak veritabanına eklemedi. Ne zaman tasarruf dediğimde. Ve bu nedenle önerdiğiniz kod .Add yöntemine ulaşmıyor çünkü etiketin zaten var olduğunu düşünüyor. – Chris