Bağlantısı kesilmiş varlıklar
DbContext örneği, veritabanından döndürülen varlıkları otomatik olarak izler. Daha sonra SaveChanges çağrıldığında bu varlıklarda yapılan değişiklikler algılanır ve veritabanı gerektiği gibi güncelleştirilir. Ayrıntılar için bkz . Temel Kaydetme ve İlgili Veriler .
Ancak, bazen varlıklar bir bağlam örneği kullanılarak sorgulanır ve sonra farklı bir örnek kullanılarak kaydedilir. Bu durum genellikle varlıkların sorgulandığı, istemciye gönderildiği, değiştirildiği, istekte sunucuya geri gönderildiği ve ardından kaydedildiği bir web uygulaması gibi "bağlantısız" senaryolarda gerçekleşir. Bu durumda, ikinci bağlam örneğinin varlıkların yeni mi (eklenmelidir) yoksa var mı (güncelleştirilmelidir) olduğunu bilmesi gerekir.
Bahşiş
Bu makalenin örneğini GitHub'da görüntüleyebilirsiniz.
Bahşiş
EF Core, belirli bir birincil anahtar değerine sahip herhangi bir varlığın yalnızca bir örneğini izleyebilir. Bunun bir sorun olmasını önlemenin en iyi yolu, bağlamın boş başlaması, buna varlıklar eklenmesi, bu varlıkların kaydedilmesi ve bağlamın atılması gibi her çalışma birimi için kısa süreli bir bağlam kullanmaktır.
Yeni varlıkları tanımlama
İstemci yeni varlıkları tanımlar
Ele alınacak en basit durum, istemcinin varlığın yeni mi yoksa var mı olduğunu sunucuya bildirmesidir. Örneğin, yeni varlık ekleme isteği genellikle var olan bir varlığı güncelleştirme isteğinden farklıdır.
Bu bölümün geri kalanı, eklenip eklenmeyeceğini veya güncelleştirilip güncelleştirilmeyeceğini başka bir şekilde belirlemenin gerekli olduğu durumları kapsar.
Otomatik oluşturulan anahtarlarla
Otomatik olarak oluşturulan anahtarın değeri genellikle bir varlığın eklenmesi veya güncelleştirilmesi gerekip gerekmediğini belirlemek için kullanılabilir. Anahtar ayarlanmamışsa (yani, hala null, sıfır vb. CLR varsayılan değerine sahipse), varlığın yeni olması ve eklenmesi gerekir. Öte yandan anahtar değeri ayarlanmışsa, daha önce kaydedilmiş olması ve şimdi güncelleştirilmesi gerekir. Başka bir deyişle anahtarın bir değeri varsa varlık sorgulanır, istemciye gönderilir ve güncelleştirilmek üzere geri gelir.
Varlık türü bilindiğinde, kümelenmemiş bir anahtarı denetlemek kolaydır:
public static bool IsItNew(Blog blog)
=> blog.BlogId == 0;
Bununla birlikte, EF'in bunu herhangi bir varlık türü ve anahtar türü için yapmak için yerleşik bir yolu da vardır:
public static bool IsItNew(DbContext context, object entity)
=> !context.Entry(entity).IsKeySet;
Bahşiş
Varlıklar bağlam tarafından izlenir izlenmez, varlık Eklendi durumunda olsa bile anahtarlar ayarlanır. Bu, varlıkların grafiğinden geçiş yaparken ve TrackGraph API'sini kullanırken olduğu gibi her biriyle ne yapacağına karar verirken yardımcı olur. Anahtar değeri yalnızca varlığı izlemek için herhangi bir çağrı yapılmadan önce burada gösterilen şekilde kullanılmalıdır.
Diğer tuşlarla
Anahtar değerleri otomatik olarak oluşturulmadığında yeni varlıkları tanımlamak için başka bir mekanizma gerekir. Bunun iki genel yaklaşımı vardır:
- Varlık için sorgu
- İstemciden bayrak geçirme
Varlığı sorgulamak için Find yöntemini kullanmanız yeter:
public static bool IsItNew(BloggingContext context, Blog blog)
=> context.Blogs.Find(blog.BlogId) == null;
İstemciden bayrak geçirmeye yönelik tam kodu göstermek bu belgenin kapsamının dışındadır. Bir web uygulamasında genellikle farklı eylemler için farklı istekler yapmak veya istekte bir durum geçirmek ve ardından denetleyicide ayıklamak anlamına gelir.
Tek varlıkları kaydetme
Ekleme veya güncelleştirme gerekip gerekmediği biliniyorsa, Ekle veya Güncelleştir uygun şekilde kullanılabilir:
public static void Insert(DbContext context, object entity)
{
context.Add(entity);
context.SaveChanges();
}
public static void Update(DbContext context, object entity)
{
context.Update(entity);
context.SaveChanges();
}
Ancak varlık otomatik olarak oluşturulan anahtar değerlerini kullanıyorsa Update yöntemi her iki durumda da kullanılabilir:
public static void InsertOrUpdate(DbContext context, object entity)
{
context.Update(entity);
context.SaveChanges();
}
Update yöntemi normalde varlığı ekleme için değil güncelleştirme için işaretler. Ancak, varlığın otomatik olarak oluşturulan bir anahtarı varsa ve hiçbir anahtar değeri ayarlanmamışsa, varlık bunun yerine otomatik olarak ekleme için işaretlenir.
Varlık otomatik olarak oluşturulan anahtarları kullanmıyorsa, uygulamanın varlığın eklenip eklenmeyeceğine veya güncelleştirileceğine karar vermesi gerekir: Örneğin:
public static void InsertOrUpdate(BloggingContext context, Blog blog)
{
var existingBlog = context.Blogs.Find(blog.BlogId);
if (existingBlog == null)
{
context.Add(blog);
}
else
{
context.Entry(existingBlog).CurrentValues.SetValues(blog);
}
context.SaveChanges();
}
Buradaki adımlar şunlardır:
- Bul null döndürürse, veritabanı bu kimliği içeren blogu zaten içermez, bu nedenle Ekle'yi ekleme için işaretle olarak adlandırıyoruz.
- Bul bir varlık döndürürse, veritabanında var olur ve bağlam artık var olan varlığı izliyor demektir
- Ardından, bu varlık üzerindeki tüm özelliklerin değerlerini istemciden gelenlere ayarlamak için SetValues kullanırız.
- SetValues çağrısı, güncelleştirilecek varlığı gerektiği gibi işaretler.
Bahşiş
SetValues yalnızca izlenen varlıktakilerle farklı değerlere sahip olan özellikleri değiştirilmiş olarak işaretler. Bu, güncelleştirme gönderildiğinde yalnızca gerçekten değiştirilmiş olan sütunların güncelleştirileceği anlamına gelir. (Hiçbir şey değişmediyse hiçbir güncelleştirme gönderilmez.)
Grafiklerle çalışma
Kimlik çözümlemesi
Yukarıda belirtildiği gibi EF Core, belirli bir birincil anahtar değerine sahip herhangi bir varlığın yalnızca bir örneğini izleyebilir. Graflarla çalışırken, graf ideal olarak bu sabitin korunacağı şekilde oluşturulmalıdır ve bağlam yalnızca bir çalışma birimi için kullanılmalıdır. Graf yinelenenler içeriyorsa, birden çok örneği tek bir örnekte birleştirmek için grafı EF'ye göndermeden önce işlemek gerekir. Örneklerin çakışan değerlere ve ilişkilere sahip olması önemsiz olmayabilir, bu nedenle çakışma çözümlemesini önlemek için yinelenenleri birleştirme işlemi uygulama işlem hattınızda mümkün olan en kısa sürede yapılmalıdır.
Tüm yeni/tüm mevcut varlıklar
Grafiklerle çalışma örneği, ilişkili gönderi koleksiyonuyla birlikte bir blog eklemek veya güncelleştirmektir. Grafikteki tüm varlıkların eklenmesi veya tümünün güncelleştirilmesi gerekiyorsa, işlem yukarıda tek varlıklar için açıklananla aynıdır. Örneğin, aşağıdaki gibi oluşturulan blogların ve gönderilerin grafiği:
var blog = new Blog
{
Url = "http://sample.com", Posts = new List<Post> { new Post { Title = "Post 1" }, new Post { Title = "Post 2" }, }
};
şu şekilde eklenebilir:
public static void InsertGraph(DbContext context, object rootEntity)
{
context.Add(rootEntity);
context.SaveChanges();
}
Ekle çağrısı blogu ve eklenecek tüm gönderileri işaretler.
Benzer şekilde, bir grafikteki tüm varlıkların güncelleştirilmiş olması gerekiyorsa Update kullanılabilir:
public static void UpdateGraph(DbContext context, object rootEntity)
{
context.Update(rootEntity);
context.SaveChanges();
}
Blog ve tüm gönderileri güncelleştirilecek şekilde işaretlenir.
Yeni ve mevcut varlıkların karışımı
Otomatik oluşturulan anahtarlarla, grafik ekleme gerektiren varlıkların bir karışımını ve güncelleştirilmesini gerektiren varlıkların bir karışımını içerse bile Güncelleştirme, hem eklemeler hem de güncelleştirmeler için yeniden kullanılabilir:
public static void InsertOrUpdateGraph(DbContext context, object rootEntity)
{
context.Update(rootEntity);
context.SaveChanges();
}
Güncelleştirme, grafik, blog veya gönderideki herhangi bir varlığı, anahtar değeri ayarlanmadıysa ve diğer tüm varlıklar güncelleştirme için işaretlenirse ekleme için işaretler.
Daha önce olduğu gibi, otomatik oluşturulan anahtarları kullanmadığınızda bir sorgu ve bazı işlemler kullanılabilir:
public static void InsertOrUpdateGraph(BloggingContext context, Blog blog)
{
var existingBlog = context.Blogs
.Include(b => b.Posts)
.FirstOrDefault(b => b.BlogId == blog.BlogId);
if (existingBlog == null)
{
context.Add(blog);
}
else
{
context.Entry(existingBlog).CurrentValues.SetValues(blog);
foreach (var post in blog.Posts)
{
var existingPost = existingBlog.Posts
.FirstOrDefault(p => p.PostId == post.PostId);
if (existingPost == null)
{
existingBlog.Posts.Add(post);
}
else
{
context.Entry(existingPost).CurrentValues.SetValues(post);
}
}
}
context.SaveChanges();
}
Silmeleri işleme
Silme işleminin işlenmesi zor olabilir çünkü çoğu durumda varlığın olmaması, varlığın silinmesi gerektiği anlamına gelir. Bununla başa çıkmanın bir yolu, varlığın aslında silinmek yerine silinmiş olarak işaretlenmesi için "geçici silmeleri" kullanmaktır. Ardından siler, güncelleştirmelerle aynı olur. Geçici silme işlemleri sorgu filtreleri kullanılarak uygulanabilir.
Gerçek silmeler için yaygın bir desen, temelde bir grafik farkını gerçekleştirmek için sorgu deseninin bir uzantısını kullanmaktır. Örnek:
public static void InsertUpdateOrDeleteGraph(BloggingContext context, Blog blog)
{
var existingBlog = context.Blogs
.Include(b => b.Posts)
.FirstOrDefault(b => b.BlogId == blog.BlogId);
if (existingBlog == null)
{
context.Add(blog);
}
else
{
context.Entry(existingBlog).CurrentValues.SetValues(blog);
foreach (var post in blog.Posts)
{
var existingPost = existingBlog.Posts
.FirstOrDefault(p => p.PostId == post.PostId);
if (existingPost == null)
{
existingBlog.Posts.Add(post);
}
else
{
context.Entry(existingPost).CurrentValues.SetValues(post);
}
}
foreach (var post in existingBlog.Posts)
{
if (!blog.Posts.Any(p => p.PostId == post.PostId))
{
context.Remove(post);
}
}
}
context.SaveChanges();
}
TrackGraph
Dahili olarak, Ekle, Ekle ve Güncelleştir, her varlık için eklenen (eklemek için), Değiştirildi (güncelleştirmek için), Değişmedi (hiçbir şey yapma) veya Silinmiş (silmek için) olarak işaretlenmesi gereken bir belirlemeyle graf geçişi kullanır. Bu mekanizma, TrackGraph API'sini kullanarak kullanıma sunulur. Örneğin, istemci varlıkların grafiğini geri gönderdiğinde her varlıkta nasıl işleneceğini belirten bir bayrak ayarlandığını varsayalım. TrackGraph daha sonra bu bayrağı işlemek için kullanılabilir:
public static void SaveAnnotatedGraph(DbContext context, object rootEntity)
{
context.ChangeTracker.TrackGraph(
rootEntity,
n =>
{
var entity = (EntityBase)n.Entry.Entity;
n.Entry.State = entity.IsNew
? EntityState.Added
: entity.IsChanged
? EntityState.Modified
: entity.IsDeleted
? EntityState.Deleted
: EntityState.Unchanged;
});
context.SaveChanges();
}
Bayraklar yalnızca örneğin basitliği için varlığın bir parçası olarak gösterilir. Bayraklar genellikle bir DTO'nun parçası veya isteğe dahil edilen başka bir durumun parçası olur.