Not
Bu sayfaya erişim yetkilendirme gerektiriyor. Oturum açmayı veya dizinleri değiştirmeyi deneyebilirsiniz.
Bu sayfaya erişim yetkilendirme gerektiriyor. Dizinleri değiştirmeyi deneyebilirsiniz.
Her DbContext örnek, varlıklarda yapılan değişiklikleri izler. İzlenen bu varlıklar, SaveChanges çağrıldığında veritabanındaki değişikliklere yol açar. Bu, EF Core'da Değişiklik İzleme bölümünde ele alınmıştır ve bu belgede varlık durumlarının ve Entity Framework Core (EF Core) değişiklik izlemesinin temellerinin anlaşıldığı varsayılmaktadır.
özellik ve ilişki değişikliklerini izleme, DbContext'in bu değişiklikleri algılayabilmesini gerektirir. Bu belge, bu algılamanın nasıl gerçekleştiğini ve değişikliklerin anında algılanması için özellik bildirimlerinin veya değişiklik izleme proxy'lerinin nasıl kullanılacağını kapsar.
Tavsiye
GitHub'dan örnek kodu indirerek bu belgedeki tüm kodu çalıştırabilir ve hatalarını ayıklayabilirsiniz.
Anlık görüntüdeki değişikliklerin izlenmesi
Varsayılan olarak, EF Core bir DbContext örneği tarafından ilk izlendiğinde her varlığın özellik değerlerinin anlık görüntüsünü oluşturur. Bu anlık görüntüde depolanan değerler, hangi özellik değerlerinin değiştiğini belirlemek için varlığın geçerli değerleriyle karşılaştırılır.
Bu değişiklik algılama işlemi, veritabanına güncelleştirme göndermeden önce değiştirilen tüm değerlerin algılandığından emin olmak için SaveChanges çağrıldığında gerçekleşir. Ancak, uygulamanın up-totarih izleme bilgileriyle çalıştığından emin olmak için değişikliklerin algılanması başka zamanlarda da gerçekleşir. Değişikliklerin algılanması, herhangi bir zamanda ChangeTracker.DetectChanges() çağrılarak zorlanabilir.
Değişiklik algılama gerektiğinde
Bu değişikliği yapmak için EF Core kullanılmadan bir özellik veya gezinti değiştirildiğinde değişikliklerin algılanması gerekir. Örneğin, blogları ve gönderileri yüklemeyi ve sonra bu varlıklarda değişiklik yapmayı göz önünde bulundurun:
using var context = new BlogsContext();
var blog = await context.Blogs.Include(e => e.Posts).FirstAsync(e => e.Name == ".NET Blog");
// Change a property value
blog.Name = ".NET Blog (Updated!)";
// Add a new entity to a navigation
blog.Posts.Add(
new Post
{
Title = "What’s next for System.Text.Json?", Content = ".NET 5.0 was released recently and has come with many..."
});
Console.WriteLine(context.ChangeTracker.DebugView.LongView);
context.ChangeTracker.DetectChanges();
Console.WriteLine(context.ChangeTracker.DebugView.LongView);
Çağrıdan önce ChangeTracker.DetectChanges() baktığımızda, yapılan değişikliklerin algılanmadığı ve bu nedenle varlık durumlarına ve değiştirilen özellik verilerine yansıtılmadığını gösterir:
Blog {Id: 1} Unchanged
Id: 1 PK
Name: '.NET Blog (Updated!)' Originally '.NET Blog'
Posts: [{Id: 1}, {Id: 2}, <not found>]
Post {Id: 1} Unchanged
Id: 1 PK
BlogId: 1 FK
Content: 'Announcing the release of EF Core 5.0, a full featured cross...'
Title: 'Announcing the Release of EF Core 5.0'
Blog: {Id: 1}
Post {Id: 2} Unchanged
Id: 2 PK
BlogId: 1 FK
Content: 'F# 5 is the latest version of F#, the functional programming...'
Title: 'Announcing F# 5'
Blog: {Id: 1}
Özel olarak, blog girdisinin durumu hala Unchangedşeklindedir ve yeni gönderi izlenen bir varlık olarak görünmez. (Bu değişiklikler EF Core tarafından henüz algılanmamış olsa bile, astute özelliklerin yeni değerlerini raporladığını fark eder. Bunun nedeni, hata ayıklama görünümünün geçerli değerleri doğrudan varlık örneğinden okumasıdır.)
DetectChanges çağrıldıktan sonra hata ayıklama görünümünü karşılaştırın:
Blog {Id: 1} Modified
Id: 1 PK
Name: '.NET Blog (Updated!)' Modified Originally '.NET Blog'
Posts: [{Id: 1}, {Id: 2}, {Id: -2147482643}]
Post {Id: -2147482643} Added
Id: -2147482643 PK Temporary
BlogId: 1 FK
Content: '.NET 5.0 was released recently and has come with many...'
Title: 'What's next for System.Text.Json?'
Blog: {Id: 1}
Post {Id: 1} Unchanged
Id: 1 PK
BlogId: 1 FK
Content: 'Announcing the release of EF Core 5.0, a full featured cross...'
Title: 'Announcing the Release of EF Core 5.0'
Blog: {Id: 1}
Post {Id: 2} Unchanged
Id: 2 PK
BlogId: 1 FK
Content: 'F# 5 is the latest version of F#, the functional programming...'
Title: 'Announcing F# 5'
Blog: {Id: 1}
Şimdi blog doğru olarak Modified işaretlendi ve yeni gönderi algılandı. Yeni gönderi Added olarak izleniyor.
Bu bölümün başında, değişikliği yapmak için EF Core kullanmadığınızda değişiklikleri algılamanın gerekli olduğunu belirttik. Yukarıdaki kodda bu durum yaşanıyor. Başka bir ifadeyle, özellik ve gezinti değişiklikleri herhangi bir EF Core yöntemi kullanılarak değil doğrudan varlık örneklerinde yapılır.
Varlıkları aynı şekilde değiştiren ancak bu kez EF Core yöntemlerini kullanan aşağıdaki kodla karşıtlık yapın:
using var context = new BlogsContext();
var blog = await context.Blogs.Include(e => e.Posts).FirstAsync(e => e.Name == ".NET Blog");
// Change a property value
context.Entry(blog).Property(e => e.Name).CurrentValue = ".NET Blog (Updated!)";
// Add a new entity to the DbContext
context.Add(
new Post
{
Blog = blog,
Title = "What’s next for System.Text.Json?",
Content = ".NET 5.0 was released recently and has come with many..."
});
Console.WriteLine(context.ChangeTracker.DebugView.LongView);
Bu durumda değişiklik izleyicisi hata ayıklama görünümü, değişikliklerin algılanmamasına rağmen tüm varlık durumlarının ve özellik değişikliklerinin bilindiğini gösterir. Bunun nedeni PropertyEntry.CurrentValue bir EF Core yöntemi olmasıdır. Bu, EF Core'un bu yöntem tarafından yapılan değişikliği hemen bildiği anlamına gelir. Benzer şekilde çağırma DbContext.Add , EF Core'un yeni varlık hakkında hemen bilgi edinmesini ve uygun şekilde izlemesini sağlar.
Tavsiye
Varlık değişiklikleri yapmak için her zaman EF Core yöntemlerini kullanarak değişiklikleri algılamaktan kaçınmaya çalışma. Bunu yapmak genellikle daha hantaldır ve varlıklarda normal şekilde değişiklik yapmaktan daha az iyi performans gösterir. Bu belgenin amacı, değişikliklerin ne zaman algılanması gerektiğini ve ne zaman gerekmediğini bildirmektir. Amaç, değişiklik algılamanın önlenmesini teşvik etmek değildir.
Değişiklikleri otomatik olarak algılayan yöntemler
DetectChanges() , bunu yapmanın sonuçları etkileme olasılığı olan yöntemler tarafından otomatik olarak çağrılır. Bu yöntemler şunlardır:
- DbContext.SaveChanges ve DbContext.SaveChangesAsync, veritabanını güncelleştirmeden önce tüm değişikliklerin algılandığından emin olmak için.
- ChangeTracker.Entries() ve ChangeTracker.Entries<TEntity>(), varlık durumlarının ve değiştirilen özelliklerin up-to-date olduğundan emin olmak için.
- ChangeTracker.HasChanges(), sonucun doğru olduğundan emin olmak için.
- ChangeTracker.CascadeChanges(), basamaklanmadan önce asıl/üst varlıklar için doğru varlık durumlarını güvence altına almak için.
- DbSet<TEntity>.Local, izlenen grafiğin up-totarih olduğundan emin olmak için.
Değişiklikleri algılamanın izlenen varlıkların tüm grafiği yerine yalnızca tek bir varlık örneğinde gerçekleştiği bazı yerler de vardır. Bu yerler şunlardır:
- kullanırken DbContext.Entryvarlığın durumunun ve değiştirilen özelliklerinin up-to-date olduğundan emin olun.
-
EntityEntry gibi
Property,Collection,ReferenceveyaMemberyöntemleri kullanılırken, özellik değişikliklerinin, geçerli değerlerin, vb. up-to-tarihe uygun olduğundan emin olunmalıdır. - Gerekli bir ilişki kesildiği için bağımlı/alt varlık silinecekse. Bu, bir varlığın yeniden üst öğe atanması nedeniyle silinmemesi gerektiği durumları algılar.
Tek bir varlık için değişikliklerin yerel olarak algılanması, çağrılarak EntityEntry.DetectChanges()açıkça tetiklenebilir.
Uyarı
Yerel algılama değişiklikleri, tam algılamanın bulabileceği bazı değişiklikleri kaçırabilir. Diğer varlıklarda algılanmayan değişikliklerden kaynaklanan art arda eylemlerin söz konusu varlık üzerinde etkisi olduğunda bu durum ortaya çıkar. Böyle durumlarda, uygulama ChangeTracker.DetectChanges() çağrısı yaparak tüm varlıkların tam taramasını zorlamak zorunda kalabilir.
Otomatik değişiklik algılamayı devre dışı bırakma
Değişiklikleri algılama performansı çoğu uygulama için bir performans sorunu değildir. Ancak, değişikliklerin algılanması binlerce varlığı izleyen bazı uygulamalar için bir performans sorunu haline gelebilir. (Tam sayı, varlıktaki özelliklerin sayısı gibi birçok şeye bağımlıdır.) Bu nedenle değişiklikleri otomatik algılama özelliği kullanılarak ChangeTracker.AutoDetectChangesEnableddevre dışı bırakılabilir. Örneğin, yük içeren çoka çok ilişkilerdeki birleştirme varlıklarını işlemeyi göz önünde bulundurun.
public override async Task<int> SaveChangesAsync(CancellationToken cancellationToken = default)
{
foreach (var entityEntry in ChangeTracker.Entries<PostTag>()) // Detects changes automatically
{
if (entityEntry.State == EntityState.Added)
{
entityEntry.Entity.TaggedBy = "ajcvickers";
entityEntry.Entity.TaggedOn = DateTime.Now;
}
}
try
{
ChangeTracker.AutoDetectChangesEnabled = false;
return await base.SaveChangesAsync(cancellationToken); // Avoid automatically detecting changes again here
}
finally
{
ChangeTracker.AutoDetectChangesEnabled = true;
}
}
Önceki bölümden bildiğimiz gibi, hem ChangeTracker.Entries<TEntity>() hem de DbContext.SaveChanges değişiklikleri otomatik olarak algılar. Ancak, Girdileri çağırdıktan sonra kod herhangi bir varlık veya özellik durumu değişikliği yapmaz. (Eklenen varlıklarda normal özellik değerlerinin ayarlanması herhangi bir durum değişikliğine neden olmaz.) Bu nedenle kod, temel SaveChanges yöntemine çağrı yaparken gereksiz otomatik değişiklik algılamayı devre dışı bırakır. Kod ayrıca SaveChanges başarısız olsa bile varsayılan ayarın geri yüklendiğinden emin olmak için try/finally bloğunu kullanır.
Tavsiye
Kodunuzun iyi performans için otomatik değişiklik algılamayı devre dışı bırakması gerektiğini varsaymayın. Bu yalnızca birçok varlığı izleyen bir uygulama profili oluşturulurken gereklidir ve değişiklik algılama performansının bir sorun olduğunu gösterir.
Değişiklikleri ve değer dönüştürmelerini algılama
Varlık türüyle anlık görüntü değişikliği izlemeyi kullanmak için EF Core'un şunları yapabilmesi gerekir:
- Varlık izlendiğinde her bir özellik değerinin anlık görüntüsü alın.
- Bu değeri özelliğin geçerli değeriyle karşılaştırın
- Değer için bir karma kodu oluştur
Bu, doğrudan veritabanına eşlenebilen türler için EF Core tarafından otomatik olarak işlenir. Ancak, bir özelliği eşlemek için bir değer dönüştürücüsü kullanıldığında, bu dönüştürücü bu eylemlerin nasıl gerçekleştirileceğini belirtmelidir. Bu, bir değer karşılaştırıcı ile elde edilir ve Değer Karşılaştırıcıları belgelerinde ayrıntılı olarak açıklanmıştır.
Bildirim unsurları
Çoğu uygulama için anlık görüntü değişikliği izlemesi önerilir. Ancak, birçok varlığı izleyen ve/veya bu varlıklarda birçok değişiklik yapan uygulamalar, özellikleri ve gezinti değerleri değiştiğinde EF Core'a otomatik olarak bildirimde bulunan varlıkların uygulanmasından yararlanabilir. Bunlar "bildirim varlıkları" olarak bilinir.
Bildirim varlıklarını uygulama
Bildirim varlıkları, .NET temel sınıf kitaplığı (BCL) parçası olan INotifyPropertyChanging ve INotifyPropertyChanged arabirimlerini kullanır. Bu arabirimler, bir özellik değeri değiştirilmeden önce ve sonra tetiklenecek olayları tanımlar. Örneğin:
public class Blog : INotifyPropertyChanging, INotifyPropertyChanged
{
public event PropertyChangingEventHandler PropertyChanging;
public event PropertyChangedEventHandler PropertyChanged;
private int _id;
public int Id
{
get => _id;
set
{
PropertyChanging?.Invoke(this, new PropertyChangingEventArgs(nameof(Id)));
_id = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Id)));
}
}
private string _name;
public string Name
{
get => _name;
set
{
PropertyChanging?.Invoke(this, new PropertyChangingEventArgs(nameof(Name)));
_name = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Name)));
}
}
public IList<Post> Posts { get; } = new ObservableCollection<Post>();
}
Buna ek olarak, tüm koleksiyon gezintilerinin uygulaması INotifyCollectionChangedgerekir; yukarıdaki örnekte bu, gönderilerden biri ObservableCollection<T> kullanılarak karşılanır. EF Core ayrıca kararlı sıralamanın maliyeti karşılığında daha verimli aramalar sunan bir ObservableHashSet<T> uygulamayla birlikte de kullanıma alınır.
Bu bildirim kodunun çoğu genellikle eşlenmemiş bir temel sınıfa taşınır. Örneğin:
public class Blog : NotifyingEntity
{
private int _id;
public int Id
{
get => _id;
set => SetWithNotify(value, out _id);
}
private string _name;
public string Name
{
get => _name;
set => SetWithNotify(value, out _name);
}
public IList<Post> Posts { get; } = new ObservableCollection<Post>();
}
public abstract class NotifyingEntity : INotifyPropertyChanging, INotifyPropertyChanged
{
protected void SetWithNotify<T>(T value, out T field, [CallerMemberName] string propertyName = "")
{
NotifyChanging(propertyName);
field = value;
NotifyChanged(propertyName);
}
public event PropertyChangingEventHandler PropertyChanging;
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyChanged(string propertyName)
=> PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
private void NotifyChanging(string propertyName)
=> PropertyChanging?.Invoke(this, new PropertyChangingEventArgs(propertyName));
}
Bildirim varlıklarını yapılandırma
EF Core'un INotifyPropertyChanging veya INotifyPropertyChanged'in EF Core ile kullanım için tamamen uygulanmış olduğunu doğrulamasının hiçbir yolu yoktur. Özellikle, bu arabirimler bazı durumlarda EF Core'un gerektirdiği tüm özelliklerde (navigasyonlar dahil) değil, yalnızca belirli özelliklerde bildirimlerle çalışmaktadır. Bu nedenle EF Core bu olaylara otomatik olarak bağlanmaz.
Bunun yerine, EF Core'un bu bildirim varlıklarını kullanacak şekilde yapılandırılması gerekir. Bu işlem genellikle ModelBuilder.HasChangeTrackingStrategy çağrılarak tüm varlık türleri için yapılır. Örneğin:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.HasChangeTrackingStrategy(ChangeTrackingStrategy.ChangingAndChangedNotifications);
}
(farklı varlık türleri için strateji, EntityTypeBuilder.HasChangeTrackingStrategy kullanılarak farklı şekilde ayarlanabilir, ancak bildirimde bulunmayan varlık türleri için DetectChanges hala gerekli olduğundan, bu genellikle ters etki yaratır.)
Tam bildirim değişikliği takibi için hem INotifyPropertyChanging hem INotifyPropertyChanged uygulanmalıdır. Bu, özgün değerlerin özellik değeri değiştirilmeden hemen önce kaydedilmesine olanak tanır ve varlığı takip ederken EF Core'un anlık görüntü oluşturma gereksinimini önler. Yalnızca INotifyPropertyChanged uygulayan varlık türleri EF Core ile de kullanılabilir. Bu durumda EF, özgün değerleri izlemek için bir varlığı izlerken anlık görüntü oluşturmaya devam eder, ancak ardından DetectChanges'in çağrılması yerine değişiklikleri hemen algılamak için bildirimleri kullanır.
Farklı ChangeTrackingStrategy değerler aşağıdaki tabloda özetlenir.
| DeğişiklikTakipStratejisi | Gerekli arabirimler | DetectChanges özelliği gerekiyor | Anlık görüntüler özgün değerleri |
|---|---|---|---|
| Anlık Görüntü | Hiç kimse | Evet | Evet |
| DeğiştirilenBildirimler | INotifyPropertyChanged | Hayı | Evet |
| Değiştirilen ve Değişmekte Olan Bildirimler | INotifyPropertyChanged ve INotifyPropertyChanging | Hayı | Hayı |
| DeğişenVeDeğiştirilenBildirimlerOrijinalDeğerlerle | INotifyPropertyChanged ve INotifyPropertyChanging | Hayı | Evet |
Bildirim varlıkları kullanma
Bildirim varlıkları, diğer varlıklar gibi davranır, ancak varlık örneklerinde yapılan değişiklikleri algılamak için ChangeTracker.DetectChanges() çağrısına ihtiyaç duyulmaz. Örneğin:
using var context = new BlogsContext();
var blog = await context.Blogs.Include(e => e.Posts).FirstAsync(e => e.Name == ".NET Blog");
// Change a property value
blog.Name = ".NET Blog (Updated!)";
// Add a new entity to a navigation
blog.Posts.Add(
new Post
{
Title = "What’s next for System.Text.Json?", Content = ".NET 5.0 was released recently and has come with many..."
});
Console.WriteLine(context.ChangeTracker.DebugView.LongView);
Normal varlıklarda değişiklik izleyicisi hata ayıklama görünümü DetectChanges çağrılana kadar bu değişikliklerin algılanmadığını gösterdi. Bildirim varlıkları kullanıldığında hata ayıklama görünümüne bakmak, bu değişikliklerin hemen algılandığını gösterir:
Blog {Id: 1} Modified
Id: 1 PK
Name: '.NET Blog (Updated!)' Modified
Posts: [{Id: 1}, {Id: 2}, {Id: -2147482643}]
Post {Id: -2147482643} Added
Id: -2147482643 PK Temporary
BlogId: 1 FK
Content: '.NET 5.0 was released recently and has come with many...'
Title: 'What's next for System.Text.Json?'
Blog: {Id: 1}
Post {Id: 1} Unchanged
Id: 1 PK
BlogId: 1 FK
Content: 'Announcing the release of EF Core 5.0, a full featured cross...'
Title: 'Announcing the Release of EF Core 5.0'
Blog: {Id: 1}
Post {Id: 2} Unchanged
Id: 2 PK
BlogId: 1 FK
Content: 'F# 5 is the latest version of F#, the functional programming...'
Title: 'Announcing F# 5'
Blog: {Id: 1}
Değişiklik izleme proxy'leri
EF Core, INotifyPropertyChanging ve INotifyPropertyChanged uygulayan proxy türlerini dinamik olarak oluşturabilir. Bunun için Microsoft.EntityFrameworkCore.Proxies NuGet paketinin yüklenmesi ve UseChangeTrackingProxies değişiklik izleme proxy'lerinin etkinleştirilmesi gerekir. Örneğin:
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
=> optionsBuilder.UseChangeTrackingProxies();
Dinamik ara sunucu oluşturmak, varlık türünden devralan ve ardından tüm özellik ayarlayıcılarını geçersiz kılan yeni, dinamik bir .NET türü ( Castle.Core proxy'leri uygulamasını kullanarak) oluşturmayı içerir. Bu nedenle proxy'ler için varlık türlerinin devralınabilecek türler olması ve geçersiz kılınabilecek özelliklere sahip olması gerekir. Ayrıca, açıkça oluşturulan koleksiyon gezintilerinin INotifyCollectionChanged'yi uygulaması gerekir. Örneğin:
public class Blog
{
public virtual int Id { get; set; }
public virtual string Name { get; set; }
public virtual IList<Post> Posts { get; } = new ObservableCollection<Post>();
}
public class Post
{
public virtual int Id { get; set; }
public virtual string Title { get; set; }
public virtual string Content { get; set; }
public virtual int BlogId { get; set; }
public virtual Blog Blog { get; set; }
}
Değişiklik izleme proxy'lerinin önemli bir dezavantajı, EF Core'un her zaman sadece proxy örneklerini izlemesi, temel alınan varlık türlerinin örneklerini asla izlememesi gerektiğidir. Bunun nedeni, temel alınan varlık türünün örneklerinin bildirim oluşturmamasıdır ve bu da bu varlıklarda yapılan değişikliklerin kaçırılacağı anlamına gelir.
EF Core, veritabanını sorgularken otomatik olarak ara sunucu örnekleri oluşturur, bu nedenle bu dezavantaj genellikle yeni varlık örneklerini izlemekle sınırlıdır. Bu örneklerin uzantı yöntemleri kullanılarak CreateProxy oluşturulması ve normal şekilde değilnew yapılması gerekir. Bu, önceki örneklerde yer alan kodun şu şekilde kullanılması CreateProxygerektiği anlamına gelir:
using var context = new BlogsContext();
var blog = await context.Blogs.Include(e => e.Posts).FirstAsync(e => e.Name == ".NET Blog");
// Change a property value
blog.Name = ".NET Blog (Updated!)";
// Add a new entity to a navigation
blog.Posts.Add(
context.CreateProxy<Post>(
p =>
{
p.Title = "What’s next for System.Text.Json?";
p.Content = ".NET 5.0 was released recently and has come with many...";
}));
Console.WriteLine(context.ChangeTracker.DebugView.LongView);
Değişiklik izleme olayları
EF Core, bir varlık ilk kez izlemeye alındığında ChangeTracker.Tracked olayını tetikler. Gelecekteki varlık durumu değişiklikleri ChangeTracker.StateChanged olaylarına neden olur. Daha fazla bilgi için bkz. EF Core'da .NET Olayları .
Uyarı
Bir varlık ilk kez izlendiğinde, durum StateChanged'den diğer durumlardan birine değişmiş olsa bile Detached olayı tetiklenmez. Tüm ilgili bildirimleri almak için hem StateChanged olaylarını hem de Tracked olaylarını dinlediğinizden emin olun.