Aracılığıyla paylaş


Eşzamanlılık Çakışmalarını İşleme (EF6)

İyimser eşzamanlılık, varlığınızın yüklenmesinden bu yana verilerin değişmemesi umuduyla varlığınızı veritabanına kaydetmeyi iyimser bir şekilde denemeyi içerir. Verilerin değiştiği ortaya çıkarsa bir özel durum oluşur ve yeniden kaydetmeyi denemeden önce çakışmayı çözmeniz gerekir. Bu konu, Entity Framework'te bu tür özel durumların nasıl işleneceğini kapsar. Bu konu başlığında gösterilen teknikler, gerek Code First gerekse EF Designer ile oluşturulan modellere için geçerlidir.

Bu gönderi, iyimser eşzamanlılığın tam olarak tartışılmaması için uygun bir yer değildir. Aşağıdaki bölümlerde eşzamanlılık çözümlemesi hakkında bazı bilgiler olduğu varsayılır ve ortak görevler için desenler gösterilir.

Bu desenlerin çoğu, Özellik Değerleriyle Çalışma bölümünde açıklanan konuları kullanır.

Bağımsız ilişkilendirmeler kullanırken (yabancı anahtarın varlığınızdaki bir özelliğe eşlenmediği durumlarda) eşzamanlılık sorunlarını çözmek, yabancı anahtar ilişkilendirmelerini kullandığınızdan çok daha zordur. Bu nedenle, uygulamanızda eşzamanlılık çözümlemesi yapacaksanız, yabancı anahtarları her zaman varlıklarınızla eşlemeniz tavsiye edilir. Aşağıdaki tüm örneklerde yabancı anahtar ilişkilendirmeleri kullandığınız varsayılır.

Yabancı anahtar ilişkilendirmeleri kullanan bir varlığı kaydetmeye çalışırken iyimser bir eşzamanlılık özel durumu algılandığında SaveChanges tarafından dbUpdateConcurrencyException oluşturulur.

Yeniden Yükleme ile iyimser eşzamanlılık özel durumlarını çözme (veritabanı kazanır)

Reload yöntemi, varlığın geçerli değerlerinin üzerine veritabanındaki değerlerle yazmak için kullanılabilir. Daha sonra varlık genellikle kullanıcıya bir biçimde geri verilir ve değişikliklerini yeniden yapıp yeniden kaydetmeyi denemesi gerekir. Örnek:

using (var context = new BloggingContext())
{
    var blog = context.Blogs.Find(1);
    blog.Name = "The New ADO.NET Blog";

    bool saveFailed;
    do
    {
        saveFailed = false;

        try
        {
            context.SaveChanges();
        }
        catch (DbUpdateConcurrencyException ex)
        {
            saveFailed = true;

            // Update the values of the entity that failed to save from the store
            ex.Entries.Single().Reload();
        }

    } while (saveFailed);
}

Eşzamanlılık özel durumunu simüle etmenin iyi bir yolu, SaveChanges çağrısında bir kesme noktası ayarlamak ve ardından SQL Server Management Studio gibi başka bir araç kullanarak veritabanına kaydedilen bir varlığı değiştirmektir. Veritabanını doğrudan SqlCommand kullanarak güncelleştirmek için SaveChanges'in önüne bir satır da ekleyebilirsiniz. Örnek:

context.Database.SqlCommand(
    "UPDATE dbo.Blogs SET Name = 'Another Name' WHERE BlogId = 1");

DbUpdateConcurrencyException üzerindeki Entrys yöntemi, güncelleştirilemeyen varlıklar için DbEntityEntry örneklerini döndürür. (Bu özellik şu anda eşzamanlılık sorunları için her zaman tek bir değer döndürür. Genel güncelleştirme özel durumları için birden çok değer döndürebilir.) Bazı durumlar için alternatif olarak, veritabanından yeniden yüklenmesi gerekebilecek tüm varlıklar için girdiler almak ve bunların her biri için yeniden yüklemeyi çağırmak verilebilir.

İstemci kazanırken iyimser eşzamanlılık özel durumlarını çözme

Yeniden Yükle'yi kullanan yukarıdaki örnek bazen veritabanındaki değerlerin üzerine veritabanındaki değerler yazıldığından veritabanı wins veya depo kazançları olarak adlandırılır. Bazen bunun tersini yapmak ve veritabanındaki değerlerin üzerine varlıkta bulunan değerleri yazmak isteyebilirsiniz. Buna bazen istemci kazanır denir ve geçerli veritabanı değerleri alınıp varlık için özgün değerler olarak ayarlanarak yapılabilir. (Bkz. Geçerli ve özgün değerler hakkında bilgi için Özellik Değerleriyle çalışma.) Örneğin:

using (var context = new BloggingContext())
{
    var blog = context.Blogs.Find(1);
    blog.Name = "The New ADO.NET Blog";

    bool saveFailed;
    do
    {
        saveFailed = false;
        try
        {
            context.SaveChanges();
        }
        catch (DbUpdateConcurrencyException ex)
        {
            saveFailed = true;

            // Update original values from the database
            var entry = ex.Entries.Single();
            entry.OriginalValues.SetValues(entry.GetDatabaseValues());
        }

    } while (saveFailed);
}

İyimser eşzamanlılık özel durumlarının özel çözümü

Bazen veritabanındaki değerleri varlıkta bulunan değerlerle birleştirmek isteyebilirsiniz. Bu genellikle bazı özel mantık veya kullanıcı etkileşimi gerektirir. Örneğin, kullanıcıya geçerli değerleri, veritabanındaki değerleri ve varsayılan çözümlenmiş değerler kümesini içeren bir form sunabilirsiniz. Kullanıcı daha sonra çözümlenen değerleri gerektiği gibi düzenler ve veritabanına kaydedilen bu çözümlenmiş değerler olur. Bu, varlığın girdisinde CurrentValues ve GetDatabaseValues'tan döndürülen DbPropertyValues nesneleri kullanılarak yapılabilir. Örnek:

using (var context = new BloggingContext())
{
    var blog = context.Blogs.Find(1);
    blog.Name = "The New ADO.NET Blog";

    bool saveFailed;
    do
    {
        saveFailed = false;
        try
        {
            context.SaveChanges();
        }
        catch (DbUpdateConcurrencyException ex)
        {
            saveFailed = true;

            // Get the current entity values and the values in the database
            var entry = ex.Entries.Single();
            var currentValues = entry.CurrentValues;
            var databaseValues = entry.GetDatabaseValues();

            // Choose an initial set of resolved values. In this case we
            // make the default be the values currently in the database.
            var resolvedValues = databaseValues.Clone();

            // Have the user choose what the resolved values should be
            HaveUserResolveConcurrency(currentValues, databaseValues, resolvedValues);

            // Update the original values with the database values and
            // the current values with whatever the user choose.
            entry.OriginalValues.SetValues(databaseValues);
            entry.CurrentValues.SetValues(resolvedValues);
        }
    } while (saveFailed);
}

public void HaveUserResolveConcurrency(DbPropertyValues currentValues,
                                       DbPropertyValues databaseValues,
                                       DbPropertyValues resolvedValues)
{
    // Show the current, database, and resolved values to the user and have
    // them edit the resolved values to get the correct resolution.
}

Nesneleri kullanarak iyimser eşzamanlılık özel durumlarının özel çözümü

Yukarıdaki kod geçerli, veritabanı ve çözümlenen değerleri geçirmek için DbPropertyValues örneklerini kullanır. Bazen bunun için varlık türünüzün örneklerini kullanmak daha kolay olabilir. Bu, DbPropertyValues'un ToObject ve SetValues yöntemleri kullanılarak yapılabilir. Örnek:

using (var context = new BloggingContext())
{
    var blog = context.Blogs.Find(1);
    blog.Name = "The New ADO.NET Blog";

    bool saveFailed;
    do
    {
        saveFailed = false;
        try
        {
            context.SaveChanges();
        }
        catch (DbUpdateConcurrencyException ex)
        {
            saveFailed = true;

            // Get the current entity values and the values in the database
            // as instances of the entity type
            var entry = ex.Entries.Single();
            var databaseValues = entry.GetDatabaseValues();
            var databaseValuesAsBlog = (Blog)databaseValues.ToObject();

            // Choose an initial set of resolved values. In this case we
            // make the default be the values currently in the database.
            var resolvedValuesAsBlog = (Blog)databaseValues.ToObject();

            // Have the user choose what the resolved values should be
            HaveUserResolveConcurrency((Blog)entry.Entity,
                                       databaseValuesAsBlog,
                                       resolvedValuesAsBlog);

            // Update the original values with the database values and
            // the current values with whatever the user choose.
            entry.OriginalValues.SetValues(databaseValues);
            entry.CurrentValues.SetValues(resolvedValuesAsBlog);
        }

    } while (saveFailed);
}

public void HaveUserResolveConcurrency(Blog entity,
                                       Blog databaseValues,
                                       Blog resolvedValues)
{
    // Show the current, database, and resolved values to the user and have
    // them update the resolved values to get the correct resolution.
}