Bağlantısı kesilmiş varlıklar
DbContext örneği, veritabanından döndürülen varlıkları otomatik olarak takip eder. Daha sonra SaveChanges çağrıldıkça bu varlıklarda yapılan değişiklikler algılanır ve veritabanı gerektiğinde 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 sorgulanan ve farklı bir örnek kullanılarak kaydedilen varlıklardır. Bu durum genellikle varlıkların sorgulanan, istemciye gönderilen, değiştirilen, istekte sunucuya geri gönderilen ve ardından kaydedilen bir web uygulaması gibi "bağlantısız" senaryolarda gerçekleşir. Bu durumda, ikinci bağlam örneğinin varlıkların yeni mi (eklenmeli) yoksa mevcut mu (güncelleştirilmiş olması gerektiğini) biliyor olması gerekir.
İpucu
Bu makalenin örneğini daha sonra GitHub.
İpucu
EF Core birincil anahtar değerine sahip herhangi bir varlığın yalnızca bir örneğini izleyebilir. Bu sorundan kaçınmanın en iyi yolu, her çalışma birimi için kısa süreli bir bağlam kullanmaktır; böylece bağlam boş başlar, buna bağlı varlıklar vardır, bu varlıkları kaydeder ve ardından bağlam atılır ve atılır.
Yeni varlıkları tanımlama
İstemci yeni varlıkları tanımlar
Üzerinde ilgilenilen 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ında, ekleme veya güncelleştirmenin başka bir şekilde belirlenmesinin gerekli olduğu durumlar yer almaktadır.
Otomatik olarak oluşturulan anahtarlarla
Otomatik olarak oluşturulan bir anahtarın değeri genellikle bir varlığın eklenmesi veya güncelleştirilip güncelleştirilip güncelleştiril gerekip gerek olmadığını belirlemek için kullanılabilir. Anahtar ayarlanmazsa (yani hala null, sıfır vb.) CLR varsayılan değerine sahipse, varlığın yeni olması ve eklemesi gerekir. Öte yandan, anahtar değeri ayarlanmışsa önceden kaydedilmiş olması ve şimdi güncelleştiriliyor olması gerekir. Başka bir deyişle, anahtar bir değere sahipse varlık sorgulandı, istemciye gönderildi ve artık güncelleştirildi.
Varlık türü bilindiği zaman, bir unset anahtarının kontrol etmek kolaydır:
public static bool IsItNew(Blog blog)
=> blog.BlogId == 0;
Ancak, EF'nin 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;
İpucu
Varlıklar bağlam tarafından izleniyorsa, varlık Eklendi durumda olsa bile anahtarlar ayarlanır. Bu, varlık grafı arasında geçiş yapma ve TrackGraph API'sini kullanma gibi her biri ile ne yapacaklarını karar verirken yardımcı olur. Anahtar değeri yalnızca varlığı izlemek için herhangi bir çağrı öncesinde burada gösterilen şekilde kullanılmalıdır.
Diğer anahtarlarla
Anahtar değerleri otomatik olarak oluşturulmazsa yeni varlıkları tanımlamak için başka bir mekanizma gerekir. Bunun için iki genel yaklaşım vardır:
- Varlık için sorgu
- İstemciden bayrak geçişi
Varlığı sorgulamak için Find yöntemini kullanın:
public static bool IsItNew(BloggingContext context, Blog blog)
=> context.Blogs.Find(blog.BlogId) == null;
İstemciden bayrak geçirme kodunun tamamını 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çirmeyi ve ardından denetleyicide ayıklamayı ifade eder.
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ık otomatik olarak oluşturulan bir anahtara sahipse ve anahtar değeri ayarlandı ise varlık bunun yerine ekleme için otomatik olarak işaretlenir.
Varlık otomatik olarak oluşturulan anahtarları kullanamasa da uygulamanın varlığın ek mi yoksa güncelleştiril mi olduğuna 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:
- Bul işlevi null değerini döndürürse, veritabanı bu kimli blogu zaten içermez, bu nedenle ekleme için Ekle mark it çağrısında bulunuz.
- Bul işlevi bir varlık döndürürse veritabanında mevcut olur ve bağlam mevcut varlığı takip eder
- Ardından, bu varlık üzerinde tüm özelliklerin değerlerini istemciden gelen değerlere ayarlamak için SetValues kullanırsınız.
- SetValues çağrısı, varlığı gerektiğinde güncelleştirilecek şekilde işaret eder.
İpucu
SetValues yalnızca izilen varlığa göre farklı değerlere sahip olan özellikleri değiştirilmiş olarak işaretlenir. Bu, güncelleştirme gönderdiğinde yalnızca gerçekten değiştirilmiş olan sütunların güncelleştirilecek olduğu anlamına gelir. (Hiçbir şey değişmemişse hiçbir güncelleştirme gönderilmez.)
Graflarla çalışma
Kimlik çözümlemesi
Yukarıda belirtildiği gibi, EF Core birincil anahtar değerine sahip herhangi bir varlığın yalnızca bir örneğini izleyebilirsiniz. Grafiklerle çalışırken graf ideal olarak bu sabitin korunacak şekilde oluşturulmalı ve bağlam yalnızca bir iş birimi için kullanılmalıdır. Grafikte yinelenen değerler varsa, birden çok örneği tek bir örnekte birleştirmek için grafı EF'ye göndermeden önce işlemesi gerekir. Bu, örneklerin çakışan değerlere ve ilişkilere sahip olduğu önemsiz bir durum değildir, bu nedenle çakışma çözümünden kaçınmak için uygulama işlem hattında yinelenenleri birleştirmenin mümkün olan en kısa sürede yapılması gerekir.
Tüm yeni/tüm mevcut varlıklar
Graflarla çalışma örneği, blog ekleme veya güncelleştirme ile ilişkili gönderi koleksiyonudur. Grafikte tüm varlıkların eklenmesi veya tüm varlıkların güncelleştirilmiş olması gerekirse, işlem tek varlıklar için yukarıda açıklananla aynıdır. Örneğin, aşağıdaki gibi oluşturulan blogların ve gönderilerin bir 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şaretletir.
Benzer şekilde, bir grafikteki tüm varlıkların güncelleştirilmiş olması gerekirse 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 olarak oluşturulan anahtarlar ile, grafik ekleme gerektiren varlıkların bir karışımını ve güncelleştirme gerektiren varlıkları içeriyor olsa 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, grafikte, blogda veya gönderide herhangi bir varlığı, anahtar değeri ayarlanmayacaksa ekleme için işaretler, diğer tüm varlıklar ise güncelleştirme için işaretlenir.
Daha önce olduğu gibi, otomatik olarak oluşturulan anahtarları kullanmama sırasında 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
Genellikle bir varlığın olmaması silinmesi gerektiği anlamına gelir ve silme işlemesi karmaşık olabilir. Bu durumla başa olmanın bir yolu, varlığın gerçekten silinmek yerine silinmiş olarak işaretlenen "yazılım silmelerini" kullanmaktır. Silmeler daha sonra güncelleştirmeler ile aynı hale gelir. Sorgu filtreleri kullanılarak yazılım silmeleri gerçekleştirilebilir.
Gerçek silme işlemleri için yaygın bir desen, temelde graf 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 (eklenecek), Değiştirilen (güncelleştirmek için), Değiştirilmemiş (hiçbir şey yapma) veya Silindi (silmek için) olarak işaretlenen bir belirleme ile graf geçişi kullanır. Bu mekanizma TrackGraph API'si aracılığıyla ortaya çıkar. Örneğin, istemci varlık grafı geri gönderdiği zaman her varlığa nasıl iş gerektiğini belirten bazı bayraklar ayarl olduğunu 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, örneğin basitliği için varlığın yalnızca bir parçası olarak gösterilir. Bayraklar genellikle bir DTO'nun veya istekte yer alan başka bir durumun parçası olur.