Aracılığıyla paylaş


İyimser Eşzamanlılık Uygulaması (C#)

tarafından Scott Mitchell

PDF'i indirin

Birden çok kullanıcının verileri düzenlemesine izin veren bir web uygulaması için, iki kullanıcının aynı verileri aynı anda düzenleme riski vardır. Bu öğreticide bu riski ele almak için iyimser eşzamanlılık denetimi uygulayacağız.

Giriş

Yalnızca kullanıcıların verileri görüntülemesine izin veren web uygulamaları veya yalnızca verileri değiştirebilen tek bir kullanıcı içeren web uygulamaları için, iki eş zamanlı kullanıcının yanlışlıkla başka bir kullanıcının değişikliklerinin üzerine yazma tehdidi yoktur. Ancak, birden çok kullanıcının verileri güncelleştirmesine veya silmesine olanak sağlayan web uygulamaları için, bir kullanıcının değişikliklerinin başka bir eşzamanlı kullanıcınınkiyle çakışma olasılığı vardır. Herhangi bir eşzamanlılık ilkesi olmadığında, iki kullanıcı aynı anda tek bir kaydı düzenlerken, değişikliklerini en son işleyen kullanıcı ilk kayıt tarafından yapılan değişiklikleri geçersiz kılar.

Örneğin, jisun ve Sam adlı iki kullanıcının uygulamamızda ziyaretçilerin gridview denetimi aracılığıyla ürünleri güncelleştirmesine ve silmesine izin veren bir sayfayı ziyaret ettiğini düşünün. Her ikisi de GridView'da Düzenle düğmesine aynı anda tıklayın. Jisun, ürün adını "Chai Tea" olarak değiştirir ve Güncelleştir düğmesine tıklar. Net sonuç, veritabanına gönderilen ve ürünün UPDATE (Jisun yalnızca bir alanı güncelleştirmiş olsa bile) ayarlayan bir ProductName deyimdir. Bu noktada veritabanı, bu ürün için "Chai Tea", beverages kategorisi, tedarikçi Exotic Liquids vb. değerlerine sahiptir. Ancak, Sam'in ekranındaki GridView yine de düzenlenebilir GridView satırında ürün adını "Chai" olarak gösterir. Jisun'un değişiklikleri işlendikten birkaç saniye sonra Sam kategoriyi Condiments olarak güncelleştirir ve Güncelleştir'e tıklar. Bu, veritabanına gönderilen ve ürün adını "Chai" olarak ayarlayan bir UPDATE ifadeyi, ilgili İçecek kategori kimliğine CategoryID olarak ayarlayan ve diğer ayarlamaları gerçekleştiren bir sonuç verir. Jisun'un ürün adındaki değişiklikler geçersiz kılındı. Şekil 1'de bu olay serisi grafik olarak gösterilmektedir.

İki Kullanıcı Aynı Anda Bir Kaydı Güncellediğinde, Bir Kullanıcının Diğerinin üzerine Yazacak Şekilde Değişiklik Yapma Olasılığı Vardır

Şekil 1: İki Kullanıcı Aynı Anda Bir Kaydı Güncelleştirdiğinde, Bir Kullanıcının DiğerInin Üzerine YazmaSına Yönelik Değişiklikler Olabilir (Tam boyutlu görüntüyü görüntülemek için tıklayın)

Benzer şekilde, iki kullanıcı bir sayfayı ziyaret ederken, bir kullanıcı kaydı güncelleştiriyor olabilir ve bu sırada başka bir kullanıcı kaydı silebilir. Veya bir kullanıcı bir sayfayı yüklediğinde ve Sil düğmesine tıkladığında, başka bir kullanıcı bu kaydın içeriğini değiştirmiş olabilir.

Üç eşzamanlılık denetimi stratejisi vardır:

  • Hiçbir Şey Yapma -if eşzamanlı kullanıcılar aynı kaydı değiştiriyorsa, son işlemenin kazanmasına izin verin (varsayılan davranış)
  • İyimser Eşzamanlılık - her seferinde eşzamanlılık çakışmaları olsa da, bu tür çakışmaların çoğu zaman ortaya çıkmayacağını varsayalım; bu nedenle, bir çakışma oluşursa, başka bir kullanıcı aynı verileri değiştirdiği için değişikliklerinin kaydedilemediğini kullanıcıya bildirmeniz yeterlidir
  • Kötümser Eşzamanlılık - Eşzamanlılık çakışmalarının yaygın olduğunu ve kullanıcıların başka bir kullanıcının eşzamanlı etkinliği nedeniyle değişikliklerinin kaydedilmediğinin söylenmesine izin vermeyeceklerini varsayalım; bu nedenle, bir kullanıcı bir kaydı güncelleştirmeye başladığında kaydı kilitleyerek kullanıcı değişikliklerini yürütene kadar diğer kullanıcıların bu kaydı düzenlemesini veya silmesini önleyin

Şimdiye kadar tüm öğreticilerimiz varsayılan eşzamanlılık çözümleme stratejisini kullandı. Yani son yazma işleminin kazanmasını sağladık. Bu öğreticide iyimser eşzamanlılık denetimini nasıl uygulayacağımızı inceleyeceğiz.

Uyarı

Bu öğretici serisinde kötümser eşzamanlılık örneklerine bakmayacağız. Kötümser eşzamanlılık nadiren kullanılır çünkü bu tür kilitler düzgün bir şekilde iptal edilmediği takdirde diğer kullanıcıların verileri güncelleştirmesini engelleyebilir. Örneğin, bir kullanıcı kaydı düzenlemek üzere kilitleyip kilidini açmadan bir gün önce ayrılırsa, özgün kullanıcı geri dönüp güncelleştirmesini tamamlayana kadar başka hiçbir kullanıcı bu kaydı güncelleştiremez. Bu nedenle, kötümser eşzamanlılığın kullanıldığı durumlarda, genellikle, eğer bu süre aşılırsa kilidi iptal eden bir zaman aşımı vardır. Kullanıcı sipariş işlemini tamamlarken belirli bir oturma konumunu kısa bir süre için kilitleyen bilet satış web siteleri, kötümser eşzamanlılık denetimine bir örnektir.

1. Adım: İyimser Eşzamanlılığın Nasıl Uygulandığına Bakma

İyimser eşzamanlılık denetimi, güncelleştirilen veya silinen kaydın, güncelleştirme veya silme işlemi başladığındaki değerlerle aynı değerlere sahip olduğundan emin olarak çalışır. Örneğin, düzenlenebilir bir GridView'da Düzenle düğmesine tıklandığında, kaydın değerleri veritabanından okunur ve TextBoxes ve diğer Web denetimlerinde görüntülenir. Bu özgün değerler GridView tarafından kaydedilir. Daha sonra, kullanıcı değişikliklerini yaptıktan ve Güncelleştir düğmesine tıkladıktan sonra, özgün değerler ve yeni değerler İş Mantığı Katmanı'na ve ardından Veri Erişim Katmanı'na gönderilir. Veri Erişim Katmanı, yalnızca kullanıcının düzenlemeye başladığı özgün değerler veritabanındaki değerlerle aynıysa kaydı güncelleştirecek bir SQL deyimi vermelidir. Şekil 2'de bu olay dizisi gösterilmektedir.

Güncelleştirme veya Silme işleminin Başarılı Olması için Özgün Değerlerin Geçerli Veritabanı Değerlerine Eşit Olması Gerekir

Şekil 2: Güncelleştirme veya Silme işleminin Başarılı Olması için Özgün Değerlerin Geçerli Veritabanı Değerlerine Eşit Olması Gerekir (Tam boyutlu görüntüyü görüntülemek için tıklayın)

İyimser eşzamanlılık uygulamak için çeşitli yaklaşımlar vardır (birkaç seçeneğe kısa bir bakış için bkz . Peter A. Bromberg'inİyimser Eşzamanlılık Güncelleştirme Mantığı ). ADO.NET Yazılan Veri Kümesi, yalnızca onay kutusunun işaretiyle yapılandırılabilir bir uygulama sağlar. Typed DataSet içindeki bir TableAdapter için iyimser eşzamanlılığı etkinleştirmek, TableAdapter'ın UPDATE ve DELETE deyimlerini, WHERE yan tümcesinde tüm özgün değerlerin karşılaştırılmasını içerecek şekilde genişletir. Örneğin aşağıdaki UPDATE deyim, yalnızca geçerli veritabanı değerleri GridView'da kaydı güncelleştirirken alınan değerlere eşitse ürünün adını ve fiyatını güncelleştirir. @ProductName ve @UnitPrice parametreleri kullanıcı tarafından girilen yeni değerleri içerirken @original_ProductName ve @original_UnitPrice Düzenle düğmesine tıklandığında başlangıçta GridView'a yüklenen değerleri içerir:

UPDATE Products SET
    ProductName = @ProductName,
    UnitPrice = @UnitPrice
WHERE
    ProductID = @original_ProductID AND
    ProductName = @original_ProductName AND
    UnitPrice = @original_UnitPrice

Uyarı

Bu UPDATE deyim okunabilirlik için basitleştirilmiştir. Uygulamada, UnitPrice yan tümcesinde WHERE denetimi daha kapsamlı olur çünkü UnitPrice'nin NULL içerebilmesi ve NULL = NULL'ün her zaman False döndürüldüğünü kontrol etmek gerekir (bunun yerine IS NULL kullanmanız gerekir).

Farklı bir temel alınan UPDATE deyimi kullanmanın yanı sıra, bir TableAdapter'ı iyimser eşzamanlılık kullanacak şekilde yapılandırmak, veritabanı doğrudan erişim yöntemlerinin tanımını da değiştirir. İlk öğreticimiz olan Veri Erişim Katmanı Oluşturma bölümünden hatırlayacağınız üzere veritabanı doğrudan yöntemleri, skaler değerlerin listesini giriş parametreleri olarak kabul eden yöntemlerdi (kesin olarak türü belirlenmiş datarow veya DataTable örneği yerine). İyimser eşzamanlılık kullanılırken, veritabanı doğrudan Update() ve Delete() yöntemleri özgün değerlerin giriş parametrelerini de içerir. Ayrıca, toplu güncelleştirme desenini kullanmaya yönelik BLL kodu ( Update() skaler değerler yerine DataRows ve DataTable'ları kabul eden yöntem aşırı yüklemeleri) de değiştirilmelidir.

Mevcut DAL'ın TableAdapter'larını iyimser eşzamanlılığı kullanacak şekilde genişletmek yerine (uyum sağlamak için BLL'nin değiştirilmesini gerektirecektir), bunun yerine iyimser eşzamanlılık kullanan bir TableAdapter ekleyeceğimiz adlı NorthwindOptimisticConcurrencyyeni bir Products Typed DataSet oluşturalım. Bundan sonra, iyimser eşzamanlılık DAL'sini desteklemek için uygun değişikliklere sahip bir ProductsOptimisticConcurrencyBLL İş Mantığı Katmanı sınıfı oluşturacağız. Bu temel çalışma hazırlandıktan sonra ASP.NET sayfasını oluşturmaya hazır olacağız.

2. Adım: İyimser Eşzamanlılığı Destekleyen Bir Veri Erişim Katmanı Oluşturma

Yeni bir Türlenmiş Veri Kümesi oluşturmak için, klasörün içindeki DAL klasöre sağ tıklayın App_Code ve adlı NorthwindOptimisticConcurrencyyeni bir DataSet ekleyin. İlk öğreticide gördüğümüz gibi, bunu yaptığınızda Typed DataSet'e yeni bir TableAdapter eklenir ve TableAdapter Yapılandırma Sihirbazı otomatik olarak başlatılır. İlk ekranda, aynı Northwind veritabanına bağlanmak için NORTHWNDConnectionString ayarını kullanarak Web.config ile hangi veritabanına bağlanılacağını belirtmemiz istenir.

Aynı Northwind Veritabanına Bağlanma

Şekil 3: Aynı Northwind Veritabanına Bağlanma (Tam boyutlu görüntüyü görüntülemek için tıklayın)

Ardından, geçici bir SQL deyimi, yeni bir saklı yordam veya mevcut bir saklı yordam aracılığıyla verileri nasıl sorgulayacağımız sorulur. Özgün DAL'mizde geçici SQL sorguları kullandığımızdan, bu seçeneği burada da kullanın.

Geçici SQL Deyimi Kullanarak Alınacak Verileri Belirtme

Şekil 4: Geçici SQL Deyimi Kullanarak Alınacak Verileri Belirtme (Tam boyutlu görüntüyü görüntülemek için tıklayın)

Aşağıdaki ekranda, ürün bilgilerini almak için kullanılacak SQL sorgusunu girin. Orijinal DAL'den Products TableAdapter için kullanılan aynı SQL sorgusunu kullanalım, bu sorgu tüm Product sütunlarının yanı sıra ürünün tedarikçi ve kategori adlarını da döndürür.

SELECT   ProductID, ProductName, SupplierID, CategoryID, QuantityPerUnit,
           UnitPrice, UnitsInStock, UnitsOnOrder, ReorderLevel, Discontinued,
           (SELECT CategoryName FROM Categories
              WHERE Categories.CategoryID = Products.CategoryID)
              as CategoryName,
           (SELECT CompanyName FROM Suppliers
              WHERE Suppliers.SupplierID = Products.SupplierID)
              as SupplierName
FROM     Products

Özgün DAL'deki Ürünler TabloAdaptöründeki Aynı SQL Sorgusunu Kullan

Şekil 5: Özgün DAL'de TableAdapter'dan Products Aynı SQL Sorgusunu Kullanma (Tam boyutlu görüntüyü görüntülemek için tıklayın)

Sonraki ekrana geçmeden önce Gelişmiş Seçenekler düğmesine tıklayın. Bu TableAdapter'ın iyimser eşzamanlılık denetimi kullanmasını sağlamak için "İyimser eşzamanlılık kullan" onay kutusunu işaretlemeniz yeterlidir.

Şekil 6: "İyimser eşzamanlılık kullan" Onay Kutusunu Denetleyerek İyimser Eşzamanlılık Denetimini Etkinleştirme (Tam boyutlu görüntüyü görüntülemek için tıklayın)

Son olarak, TableAdapter'ın hem DataTable'ı dolduran hem de DataTable döndüren veri erişim desenlerini kullanması gerektiğini belirtin; ayrıca veritabanı doğrudan yöntemlerinin oluşturulması gerektiğini de belirtin. DAL'ımızdaki özgün adlandırma kurallarını yansıtmak amacıyla "Return a DataTable" deseninin yöntem adını GetData'dan GetProducts'a değiştirin.

TableAdapter'ın Tüm Veri Erişim Desenlerini Kullanmasını

Şekil 7: TableAdapter'ın Tüm Veri Erişim Desenlerini Kullanmasını Sağlayın (Tam boyutlu görüntüyü görüntülemek için tıklayın)

Sihirbazı tamamladıktan sonra, DataSet Tasarımcısı güçlü bir şekilde yazılmış Products DataTable ve TableAdapter'ı içerir. Biraz zaman ayırarak, Products olan DataTable'ı, başlık çubuğuna sağ tıklayıp bağlam menüsünden Yeniden Adlandır'ı seçerek ProductsOptimisticConcurrency olarak yeniden adlandırın.

Yazılan DataSet'e DataTable ve TableAdapter Eklendi

Şekil 8: Yazılan Veri Kümesine DataTable ve TableAdapter Eklendi (Tam boyutlu görüntüyü görüntülemek için tıklayın)

Optimist eşzamanlılık kullanan UPDATE TableAdapter ile kullanmayan Products TableAdapter arasındaki farklılıkları ve bu TableAdapter'lar arasındaki DELETE ve ProductsOptimisticConcurrency sorgularını görmek için TableAdapter'a tıklayın ve Özellikler penceresine gidin. DeleteCommand ve UpdateCommand özelliklerinin CommandText alt özelliklerinde, DAL'ın güncelleme veya silme ile ilgili yöntemleri çağrıldığında, veritabanına gönderilen gerçek SQL söz dizimini görebilirsiniz. ProductsOptimisticConcurrency TableAdapter DELETE için kullanılan deyim şöyledir:

DELETE FROM [Products]
    WHERE (([ProductID] = @Original_ProductID)
    AND ([ProductName] = @Original_ProductName)
    AND ((@IsNull_SupplierID = 1 AND [SupplierID] IS NULL)
       OR ([SupplierID] = @Original_SupplierID))
    AND ((@IsNull_CategoryID = 1 AND [CategoryID] IS NULL)
       OR ([CategoryID] = @Original_CategoryID))
    AND ((@IsNull_QuantityPerUnit = 1 AND [QuantityPerUnit] IS NULL)
       OR ([QuantityPerUnit] = @Original_QuantityPerUnit))
    AND ((@IsNull_UnitPrice = 1 AND [UnitPrice] IS NULL)
       OR ([UnitPrice] = @Original_UnitPrice))
    AND ((@IsNull_UnitsInStock = 1 AND [UnitsInStock] IS NULL)
       OR ([UnitsInStock] = @Original_UnitsInStock))
    AND ((@IsNull_UnitsOnOrder = 1 AND [UnitsOnOrder] IS NULL)
       OR ([UnitsOnOrder] = @Original_UnitsOnOrder))
    AND ((@IsNull_ReorderLevel = 1 AND [ReorderLevel] IS NULL)
       OR ([ReorderLevel] = @Original_ReorderLevel))
    AND ([Discontinued] = @Original_Discontinued))

DELETE Ancak özgün DAL'mizdeki Product TableAdapter deyimi çok daha basittir:

DELETE FROM [Products] WHERE (([ProductID] = @Original_ProductID))

Gördüğünüz gibi, iyimser eşzamanlılık kullanan WHERE TableAdapter deyimindeki DELETE yan tümcesi, Product tablonun mevcut sütun değerlerinin her biri ile GridView'un (veya DetailsView veya FormView) son doldurulma zamanındaki orijinal değerler arasında bir karşılaştırma içerir. ProductID, ProductName ve Discontinued dışındaki tüm alanlar NULL değerlere sahip olabileceğinden, yan tümcedeki NULL değerlerini doğru bir şekilde karşılaştırmak için ek parametreler ve denetimler WHERE eklenir.

Bu öğretici için iyimser eşzamanlılık özellikli DataSet'e ek DataTable eklemeyeceğiz çünkü ASP.NET sayfamız yalnızca ürün bilgilerini güncelleme ve silme işlemlerine olanak tanıyacaktır. Ancak yine de GetProductByProductID(productID) yöntemini ProductsOptimisticConcurrency TableAdapter'a eklememiz gerekir.

Bunu yapmak için TableAdapter'ın başlık çubuğuna (ve Fill yöntemi adlarının hemen üstündeki GetProducts alan) sağ tıklayın ve bağlam menüsünden Sorgu Ekle'yi seçin. Bu işlem TableAdapter Sorgu Yapılandırma Sihirbazı'nı başlatır. TableAdapter'ın ilk yapılandırmasında GetProductByProductID(productID) olduğu gibi, doğaçlama bir SQL ifadesi kullanarak metodunu oluşturmayı tercih edin (bkz. Şekil 4). GetProductByProductID(productID) yöntemi belirli bir ürün hakkındaki bilgileri döndürdüğünden, bu sorgunun satırları döndüren bir SELECT sorgu türü olduğunu belirtin.

Sorgu Türünü

Şekil 9: Sorgu Türünü "satırları döndüren"SELECT olarak işaretleme (Tam boyutlu görüntüyü görüntülemek için tıklayın)

Sonraki ekranda, TableAdapter'ın varsayılan sorgusu önceden yüklenmiş olarak kullanıcıdan kullanılacak SQL sorgusu istenir. Şekil 10'da gösterildiği gibi, var olan sorguyu yan tümcesini WHERE ProductID = @ProductIDiçerecek şekilde genişletin.

Belirli bir Ürün Kaydını Döndürmek için Önceden Yüklenmiş Sorguya WHERE Yan Tümcesi Ekleme

Şekil 10: Belirli bir ürün kaydını döndürmek için önceden yüklenmiş sorguya bir madde ekleyin (WHERE)

Son olarak, oluşturulan yöntem adlarını FillByProductID ve GetProductByProductID olarak değiştirin.

Yöntemleri FillByProductID ve GetProductByProductID olarak yeniden adlandırın

Şekil 11: Yöntemleri FillByProductID ve GetProductByProductID olarak yeniden adlandırın (Tam boyutlu görüntüyü görüntülemek için tıklayın)

Bu sihirbaz tamamlandığında TableAdapter artık verileri almak için iki yöntem içerir: GetProducts()tüm ürünleri döndüren ve GetProductByProductID(productID)belirtilen ürünü döndüren .

3. Adım: İyimser Concurrency-Enabled DAL için İş Mantığı Katmanı Oluşturma

Mevcut ProductsBLL sınıfımızda hem toplu güncelleştirme hem de veritabanı doğrudan desenlerini kullanma örnekleri vardır. AddProduct yöntemi ve UpdateProduct aşırı yüklemeleri her ikisi de toplu güncelleştirme desenini kullanır ve bir ProductRow örneğini TableAdapter'ın Update yöntemine geçirir. Öte yandan, DeleteProduct yöntemi, TableAdapter'ın Delete(productID) yöntemini çağırarak veritabanına doğrudan erişim desenini kullanır.

Yeni ProductsOptimisticConcurrency TableAdapter ile veritabanı doğrudan yöntemleri artık özgün değerlerin de geçirilmesini gerektirir. Örneğin, Delete yöntemi artık on giriş parametresi bekler: özgün ProductID, ProductName, SupplierID, CategoryID, , QuantityPerUnit, , UnitPrice, UnitsInStock, UnitsOnOrder, ReorderLevelve Discontinued. Veritabanına gönderilen deyimin yan tümcesi içinde WHERE bu ek giriş parametrelerinin DELETE değerlerini kullanır, ancak yalnızca veritabanının geçerli değerleri orijinal değerlerle eşleşirse belirtilen kaydı siler.

Toplu güncelleştirme düzeninde kullanılan TableAdapter Update yönteminin yöntem imzası değişmemiş olsa da, özgün ve yeni değerleri kaydetmek için gereken kodda vardır. Bu nedenle, mevcut ProductsBLL sınıfımızla iyimser eşzamanlılık özellikli DAL'yi kullanmaya çalışmak yerine yeni DAL ile çalışmak için yeni bir İş Mantığı Katmanı sınıfı oluşturalım.

klasöründeki ProductsOptimisticConcurrencyBLL klasöre BLL adlı App_Code bir sınıf ekleyin.

ProductsOptimisticConcurrencyBLL Sınıfını BLL Klasörüne Ekleme

Şekil 12: Sınıfı BLL Klasörüne Ekleme ProductsOptimisticConcurrencyBLL

Ardından aşağıdaki kodu sınıfına ProductsOptimisticConcurrencyBLL ekleyin:

using System;
using System.Data;
using System.Configuration;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Web.UI.HtmlControls;
using NorthwindOptimisticConcurrencyTableAdapters;
[System.ComponentModel.DataObject]
public class ProductsOptimisticConcurrencyBLL
{
    private ProductsOptimisticConcurrencyTableAdapter _productsAdapter = null;
    protected ProductsOptimisticConcurrencyTableAdapter Adapter
    {
        get
        {
            if (_productsAdapter == null)
                _productsAdapter = new ProductsOptimisticConcurrencyTableAdapter();
            return _productsAdapter;
        }
    }
    [System.ComponentModel.DataObjectMethodAttribute
    (System.ComponentModel.DataObjectMethodType.Select, true)]
    public NorthwindOptimisticConcurrency.ProductsOptimisticConcurrencyDataTable GetProducts()
    {
        return Adapter.GetProducts();
    }
}

Sınıf bildiriminin başlangıcının üzerindeki using NorthwindOptimisticConcurrencyTableAdapters deyimine dikkat edin. NorthwindOptimisticConcurrencyTableAdapters ad alanı, DAL'nin yöntemlerini sağlayan sınıfını içerirProductsOptimisticConcurrencyTableAdapter. Ayrıca sınıf bildiriminden önce, Visual Studio'ya bu sınıfı ObjectDataSource sihirbazının açılan listesine eklemesini sağlayan System.ComponentModel.DataObject özniteliğini bulursunuz.

' ProductsOptimisticConcurrencyBLLs Adapter özelliği sınıfın ProductsOptimisticConcurrencyTableAdapter bir örneğine hızlı erişim sağlar ve özgün BLL sınıflarımızda (ProductsBLL, CategoriesBLLvb.) kullanılan deseni izler. Son olarak, GetProducts() yöntemi doğrudan DAL'ın GetProducts() yöntemine çağrı yapar ve veritabanındaki her bir ürün kaydı için ProductsOptimisticConcurrencyDataTable örneği ile doldurulmuş ProductsOptimisticConcurrencyRow nesnesini döndürür.

İyimser Eşzamanlılık ile DB Direct Tasarımı Kullanılarak Ürün Silme

İyimser eşzamanlılık kullanan bir DAL'ye karşı veritabanı doğrudan deseni kullanılırken yöntemlere yeni ve özgün değerler geçirilmelidir. Silmek için yeni değer olmadığından yalnızca özgün değerlerin geçirilmesi gerekir. BLL'mizde tüm özgün parametreleri giriş parametresi olarak kabul etmeliyiz. DeleteProduct yönteminin, ProductsOptimisticConcurrencyBLL sınıfındaki DB doğrudan yöntemini kullanmasını sağlayın. Bu, aşağıdaki kodda gösterildiği gibi bu yöntemin on ürün veri alanının tamamını giriş parametresi olarak alması ve dal'a geçirmesi gerektiği anlamına gelir:

[System.ComponentModel.DataObjectMethodAttribute
(System.ComponentModel.DataObjectMethodType.Delete, true)]
public bool DeleteProduct
    (int original_productID, string original_productName,
    int? original_supplierID, int? original_categoryID,
    string original_quantityPerUnit, decimal? original_unitPrice,
    short? original_unitsInStock, short? original_unitsOnOrder,
    short? original_reorderLevel, bool original_discontinued)
{
    int rowsAffected = Adapter.Delete(original_productID,
                                      original_productName,
                                      original_supplierID,
                                      original_categoryID,
                                      original_quantityPerUnit,
                                      original_unitPrice,
                                      original_unitsInStock,
                                      original_unitsOnOrder,
                                      original_reorderLevel,
                                      original_discontinued);
    // Return true if precisely one row was deleted, otherwise false
    return rowsAffected == 1;
}

Orijinal değerler - GridView'a (veya DetailsView veya FormView) son yüklenen bu değerler - kullanıcı Sil düğmesine tıkladığında veritabanındaki değerlerden farklı ise, WHERE ifadesi hiçbir veritabanı kaydıyla eşleşmeyecek ve hiçbir kayıt etkilenmeyecektir. Bu nedenle TableAdapter'ın Delete yöntemi döndürülecek 0 ve BLL'nin DeleteProduct yöntemi döndürülecektir false.

İyimser Eşzamanlılık ile Toplu Güncelleştirme Desenini Kullanarak Bir Ürünü Güncelleştirme

Daha önce belirtildiği gibi TableAdapter'ın Update toplu güncelleştirme deseni için yöntemi, iyimser eşzamanlılığın çalışıp çalışmadığına bakılmaksızın aynı yöntem imzasını kullanır. Özellikle Update yöntemi bir DataRow, DataRows dizisi, bir DataTable veya türlenmiş bir DataSet bekler. Özgün değerleri belirtmek için ek giriş parametresi yoktur. Bu mümkün çünkü DataTable, DataRow'larının özgün ve değiştirilmiş değerlerini takip ediyor. DAL kendi UPDATE deyimini verdiğinde, @original_ColumnName parametreler DataRow'un özgün değerleriyle doldurulurken ve @ColumnName parametreler DataRow'un değiştirilmiş değerleriyle doldurulur.

ProductsBLL sınıfında (özgün, iyimser olmayan eşzamanlılık DAL'mizi kullanır) ürün bilgilerini güncelleştirmek için toplu güncelleştirme desenini kullanırken kodumuz aşağıdaki olay dizisini gerçekleştirir:

  1. TableAdapter ProductRow yöntemini kullanarak geçerli veritabanı ürün bilgilerini bir GetProductByProductID(productID) örneğe okuma
  2. 1. Adım'dan ProductRow örneğe yeni değerler atama
  3. TableAdapter'ın Update yöntemini, ProductRow örneğini geçirerek çağırın.

Ancak bu adım dizisi, 1. Adımda doldurulan değerlerin doğrudan veritabanından doldurulması nedeniyle ProductRow iyimser eşzamanlılığı doğru şekilde desteklemez; başka bir deyişle DataRow tarafından kullanılan özgün değerler, düzenleme işleminin başlangıcında GridView'a bağlı olanlar değil, şu anda veritabanında bulunan değerlerdir. Bunun yerine, iyimser eşzamanlılık özellikli bir DAL kullanırken, aşağıdaki adımları uygulamak için yöntem aşırı yüklemelerini değiştirmemiz gerekir:

  1. TableAdapter ProductsOptimisticConcurrencyRow yöntemini kullanarak geçerli veritabanı ürün bilgilerini bir GetProductByProductID(productID) örneğe okuma
  2. 1. Adım'dan örneğe ProductsOptimisticConcurrencyRow değerleri atama
  3. ProductsOptimisticConcurrencyRow örneğinin DataRow üzerindeki AcceptChanges() yöntemini, geçerli değerlerinin "orijinal" değerler olduğunu belirten çağırın
  4. Yeni değerleri ProductsOptimisticConcurrencyRow örneğe atayın
  5. TableAdapter'ın Update yöntemini, ProductsOptimisticConcurrencyRow örneğini geçirerek çağırın.

1. Adım, belirtilen ürün kaydı için tüm geçerli veritabanı değerlerini okur. Bu adım, UpdateProduct ürün sütunlarını güncelleştiren aşırı yüklemede gereksizdir (2. Adımda bu değerlerin üzerine yazıldığı gibi), ancak sütun değerlerinin yalnızca bir alt kümesinin giriş parametresi olarak geçirildiği aşırı yüklemeler için önemlidir. Özgün değerler ProductsOptimisticConcurrencyRow örneğe atandıktan sonra, AcceptChanges() yöntemi çağrılarak geçerli DataRow değerlerini deyimindeki @original_ColumnName parametrelerinde kullanılacak özgün değerler olarak işaretler. Ardından, yeni parametre değerleri ProductsOptimisticConcurrencyRow öğesine atanır ve son olarak, Update yöntemi, DataRow iletilerek çağrılır.

Aşağıdaki kod, UpdateProduct tüm ürün veri alanlarını giriş parametresi olarak kabul eden aşırı yüklemeyi gösterir. Burada gösterilmese de, ProductsOptimisticConcurrencyBLL bu öğreticiyle birlikte indirilen sınıf, girdi parametreleri olarak sadece ürünün adını ve fiyatını kabul eden bir UpdateProduct aşırı yükleme içerir.

protected void AssignAllProductValues
    (NorthwindOptimisticConcurrency.ProductsOptimisticConcurrencyRow product,
    string productName, int? supplierID, int? categoryID, string quantityPerUnit,
    decimal? unitPrice, short? unitsInStock, short? unitsOnOrder,
    short? reorderLevel, bool discontinued)
{
    product.ProductName = productName;
    if (supplierID == null)
        product.SetSupplierIDNull();
    else
        product.SupplierID = supplierID.Value;
    if (categoryID == null)
        product.SetCategoryIDNull();
    else
        product.CategoryID = categoryID.Value;
    if (quantityPerUnit == null)
        product.SetQuantityPerUnitNull();
    else
        product.QuantityPerUnit = quantityPerUnit;
    if (unitPrice == null)
        product.SetUnitPriceNull();
    else
        product.UnitPrice = unitPrice.Value;
    if (unitsInStock == null)
        product.SetUnitsInStockNull();
    else
        product.UnitsInStock = unitsInStock.Value;
    if (unitsOnOrder == null)
        product.SetUnitsOnOrderNull();
    else
        product.UnitsOnOrder = unitsOnOrder.Value;
    if (reorderLevel == null)
        product.SetReorderLevelNull();
    else
        product.ReorderLevel = reorderLevel.Value;
    product.Discontinued = discontinued;
}
[System.ComponentModel.DataObjectMethodAttribute
(System.ComponentModel.DataObjectMethodType.Update, true)]
public bool UpdateProduct(
    // new parameter values
    string productName, int? supplierID, int? categoryID, string quantityPerUnit,
    decimal? unitPrice, short? unitsInStock, short? unitsOnOrder,
    short? reorderLevel, bool discontinued, int productID,
    // original parameter values
    string original_productName, int? original_supplierID, int? original_categoryID,
    string original_quantityPerUnit, decimal? original_unitPrice,
    short? original_unitsInStock, short? original_unitsOnOrder,
    short? original_reorderLevel, bool original_discontinued,
    int original_productID)
{
    // STEP 1: Read in the current database product information
    NorthwindOptimisticConcurrency.ProductsOptimisticConcurrencyDataTable products =
        Adapter.GetProductByProductID(original_productID);
    if (products.Count == 0)
        // no matching record found, return false
        return false;
    NorthwindOptimisticConcurrency.ProductsOptimisticConcurrencyRow product = products[0];
    // STEP 2: Assign the original values to the product instance
    AssignAllProductValues(product, original_productName, original_supplierID,
        original_categoryID, original_quantityPerUnit, original_unitPrice,
        original_unitsInStock, original_unitsOnOrder, original_reorderLevel,
        original_discontinued);
    // STEP 3: Accept the changes
    product.AcceptChanges();
    // STEP 4: Assign the new values to the product instance
    AssignAllProductValues(product, productName, supplierID, categoryID,
        quantityPerUnit, unitPrice, unitsInStock, unitsOnOrder, reorderLevel,
        discontinued);
    // STEP 5: Update the product record
    int rowsAffected = Adapter.Update(product);
    // Return true if precisely one row was updated, otherwise false
    return rowsAffected == 1;
}

4. Adım: ASP.NET Sayfasından BLL Yöntemlerine Özgün ve Yeni Değerleri Geçirme

DAL ve BLL tamamlandıktan sonra geriye kalan tek şey, sistemde yerleşik olarak bulunan iyimser eşzamanlılık mantığını kullanabilen bir ASP.NET sayfası oluşturmaktır. Özellikle, veri Web denetiminin (GridView, DetailsView veya FormView) özgün değerlerini hatırlaması ve ObjectDataSource'un her iki değer kümesini de İş Mantığı Katmanı'na geçirmesi gerekir. Ayrıca, ASP.NET sayfası eşzamanlılık ihlallerini düzgün bir şekilde işleyecek şekilde yapılandırılmalıdır.

İlk olarak OptimisticConcurrency.aspx klasöründeki EditInsertDelete sayfasını açın ve Tasarımcı'ya bir GridView ekleyerek ID özelliğini ProductsGrid olarak ayarlayın. GridView'un akıllı etiketinden adlı ProductsOptimisticConcurrencyDataSourceyeni bir ObjectDataSource oluşturmayı tercih edin. Bu ObjectDataSource'un iyimser eşzamanlılığı destekleyen DAL'yi kullanmasını istediğimizden, bunu ProductsOptimisticConcurrencyBLL nesnesini kullanacak şekilde yapılandırın.

ObjectDataSource'un ProductsOptimisticConcurrencyBLL Nesnesini Kullanmasını Sağlar

Şekil 13: ObjectDataSource'un Nesneyi Kullanmasını ProductsOptimisticConcurrencyBLL Sağlayın (Tam boyutlu görüntüyü görüntülemek için tıklayın)

Sihirbazdaki açılan listelerden GetProducts, UpdateProduct ve DeleteProduct yöntemlerini seçin. UpdateProduct yöntemi için, ürünün tüm veri alanlarını kabul eden aşırı yüklemeyi kullanın.

ObjectDataSource Denetiminin Özelliklerini Yapılandırma

Sihirbazı tamamladıktan sonra ObjectDataSource'un bildirim temelli işaretlemesi aşağıdaki gibi görünmelidir:

<asp:ObjectDataSource ID="ProductsOptimisticConcurrencyDataSource" runat="server"
    DeleteMethod="DeleteProduct" OldValuesParameterFormatString="original_{0}"
    SelectMethod="GetProducts" TypeName="ProductsOptimisticConcurrencyBLL"
    UpdateMethod="UpdateProduct">
    <DeleteParameters>
        <asp:Parameter Name="original_productID" Type="Int32" />
        <asp:Parameter Name="original_productName" Type="String" />
        <asp:Parameter Name="original_supplierID" Type="Int32" />
        <asp:Parameter Name="original_categoryID" Type="Int32" />
        <asp:Parameter Name="original_quantityPerUnit" Type="String" />
        <asp:Parameter Name="original_unitPrice" Type="Decimal" />
        <asp:Parameter Name="original_unitsInStock" Type="Int16" />
        <asp:Parameter Name="original_unitsOnOrder" Type="Int16" />
        <asp:Parameter Name="original_reorderLevel" Type="Int16" />
        <asp:Parameter Name="original_discontinued" Type="Boolean" />
    </DeleteParameters>
    <UpdateParameters>
        <asp:Parameter Name="productName" Type="String" />
        <asp:Parameter Name="supplierID" Type="Int32" />
        <asp:Parameter Name="categoryID" Type="Int32" />
        <asp:Parameter Name="quantityPerUnit" Type="String" />
        <asp:Parameter Name="unitPrice" Type="Decimal" />
        <asp:Parameter Name="unitsInStock" Type="Int16" />
        <asp:Parameter Name="unitsOnOrder" Type="Int16" />
        <asp:Parameter Name="reorderLevel" Type="Int16" />
        <asp:Parameter Name="discontinued" Type="Boolean" />
        <asp:Parameter Name="productID" Type="Int32" />
        <asp:Parameter Name="original_productName" Type="String" />
        <asp:Parameter Name="original_supplierID" Type="Int32" />
        <asp:Parameter Name="original_categoryID" Type="Int32" />
        <asp:Parameter Name="original_quantityPerUnit" Type="String" />
        <asp:Parameter Name="original_unitPrice" Type="Decimal" />
        <asp:Parameter Name="original_unitsInStock" Type="Int16" />
        <asp:Parameter Name="original_unitsOnOrder" Type="Int16" />
        <asp:Parameter Name="original_reorderLevel" Type="Int16" />
        <asp:Parameter Name="original_discontinued" Type="Boolean" />
        <asp:Parameter Name="original_productID" Type="Int32" />
    </UpdateParameters>
</asp:ObjectDataSource>

Gördüğünüz gibi koleksiyon, sınıfın DeleteParametersParameter yöntemindeki on giriş parametresinin ProductsOptimisticConcurrencyBLL her biri için bir DeleteProduct örnek içerir. Benzer şekilde, UpdateParameters koleksiyon içindeki Parametergiriş parametrelerinin her biri için bir UpdateProduct örnek içerir.

Bu özellik, BLL yönteminin hem eski (veya özgün) değerlerin hem de yeni değerlerin geçirilmesini beklediğini gösterdiğinden, veri değişikliğini içeren önceki öğreticilerde ObjectDataSource OldValuesParameterFormatString özelliğini bu noktada kaldıracağız. Ayrıca, bu özellik değeri özgün değerlerin giriş parametre adlarını gösterir. Özgün değerleri BLL'ye aktardığımız için bu özelliği kaldırmayın .

Uyarı

özelliğinin OldValuesParameterFormatString değeri, BLL'deki özgün değerleri bekleyen giriş parametresi adlarıyla eşlenmelidir. Bu parametreleri original_productName, original_supplierIDvb. adlandırdığımız için özellik değerini olarak OldValuesParameterFormatStringbırakabilirsinizoriginal_{0}. Ancak, BLL yöntemlerinin giriş parametrelerinin adları old_productName, old_supplierID gibi ve benzeri şekilde ise, OldValuesParameterFormatString özelliğini old_{0} olarak güncellemeniz gerekir.

ObjectDataSource'un özgün değerleri BLL yöntemlerine doğru şekilde geçirmesi için yapılması gereken son bir özellik ayarı vardır. ObjectDataSource iki değerden birine atanabilen bir ConflictDetection özelliğine sahiptir:

  • OverwriteChanges - varsayılan değer; özgün değerleri BLL yöntemlerinin özgün giriş parametrelerine göndermez
  • CompareAllValues - özgün değerleri BLL yöntemlerine gönderir; iyimser eşzamanlılık kullanırken bu seçeneği belirleyin

Bir an duraklayarak ConflictDetection özelliğini CompareAllValues olarak ayarlayın.

GridView'un Özelliklerini ve Alanlarını Yapılandırma

ObjectDataSource'un özellikleri düzgün yapılandırıldığında, GridView'u ayarlamaya dikkat edelim. İlk olarak, GridView'un düzenleme ve silmeyi desteklemesini istediğimizden, GridView'un akıllı etiketinden Düzenlemeyi Etkinleştir ve Silmeyi Etkinleştir onay kutularına tıklayın. Bu, hem \ShowEditButton hem de \ShowDeleteButton'in \true olarak ayarlandığı bir CommandField ekler.

ObjectDataSource'a ProductsOptimisticConcurrencyDataSource bağlıyken GridView, ürünün veri alanlarının her biri için bir alan içerir. Böyle bir GridView düzenlenebilir, ancak kullanıcı deneyimi hiç de kabul edilebilir değildir. CategoryID ve SupplierID BoundField'ler TextBoxes olarak işlenir ve kullanıcının uygun kategoriyi ve sağlayıcıyı kimlik numaraları olarak girmesini zorunlu kılır. Sayısal alanlar için biçimlendirme ve ürün adının sağlandığından ve birim fiyatın, stoktaki birimlerin, siparişteki birimlerin ve yeniden sıralama düzeyi değerlerinin hem doğru sayısal değerler olduğundan hem de sıfırdan büyük veya sıfıra eşit olduğundan emin olmak için hiçbir doğrulama denetimi olmayacaktır.

Düzenleme ve Ekleme Arabirimlerine Doğrulama Denetimleri Ekleme veVeri Değiştirme Arabirimini Özelleştirme öğreticilerinde ele aldığımız gibi, kullanıcı arabirimi BoundFields yerine TemplateFields ile değiştirilerek özelleştirilebilir. Bu GridView ve düzenleme arabirimini aşağıdaki yollarla değiştirdim:

  • ProductID, , SupplierNameve CategoryName BoundField'ler kaldırıldı
  • ProductName BoundField bir TemplateField'e dönüştürüldü ve RequiredFieldValidation denetimi eklendi.
  • CategoryID ve SupplierID BoundField'leri TemplateField'lere dönüştürdü ve düzenleme arabirimini TextBox'lar yerine DropDownList'ler kullanacak şekilde ayarladı. Bu TemplateField'lerde ItemTemplatesCategoryName ve SupplierName veri alanları görüntülenir.
  • UnitPrice, , UnitsInStockUnitsOnOrderve ReorderLevel BoundFields şablonalanlarına dönüştürüldü ve CompareValidator denetimleri eklendi.

Bu görevleri önceki öğreticilerde nasıl yerine getirebileceğimizi zaten incelediğimizden, burada sadece son bildirim söz dizimini listeleyip uygulamayı pratik olarak bırakacağım.

<asp:GridView ID="ProductsGrid" runat="server" AutoGenerateColumns="False"
    DataKeyNames="ProductID" DataSourceID="ProductsOptimisticConcurrencyDataSource"
    OnRowUpdated="ProductsGrid_RowUpdated">
    <Columns>
        <asp:CommandField ShowDeleteButton="True" ShowEditButton="True" />
        <asp:TemplateField HeaderText="Product" SortExpression="ProductName">
            <EditItemTemplate>
                <asp:TextBox ID="EditProductName" runat="server"
                    Text='<%# Bind("ProductName") %>'></asp:TextBox>
                <asp:RequiredFieldValidator ID="RequiredFieldValidator1"
                    ControlToValidate="EditProductName"
                    ErrorMessage="You must enter a product name."
                    runat="server">*</asp:RequiredFieldValidator>
            </EditItemTemplate>
            <ItemTemplate>
                <asp:Label ID="Label1" runat="server"
                    Text='<%# Bind("ProductName") %>'></asp:Label>
            </ItemTemplate>
        </asp:TemplateField>
        <asp:TemplateField HeaderText="Category" SortExpression="CategoryName">
            <EditItemTemplate>
                <asp:DropDownList ID="EditCategoryID" runat="server"
                    DataSourceID="CategoriesDataSource" AppendDataBoundItems="true"
                    DataTextField="CategoryName" DataValueField="CategoryID"
                    SelectedValue='<%# Bind("CategoryID") %>'>
                    <asp:ListItem Value=">(None)</asp:ListItem>
                </asp:DropDownList><asp:ObjectDataSource ID="CategoriesDataSource"
                    runat="server" OldValuesParameterFormatString="original_{0}"
                    SelectMethod="GetCategories" TypeName="CategoriesBLL">
                </asp:ObjectDataSource>
            </EditItemTemplate>
            <ItemTemplate>
                <asp:Label ID="Label2" runat="server"
                    Text='<%# Bind("CategoryName") %>'></asp:Label>
            </ItemTemplate>
        </asp:TemplateField>
        <asp:TemplateField HeaderText="Supplier" SortExpression="SupplierName">
            <EditItemTemplate>
                <asp:DropDownList ID="EditSuppliersID" runat="server"
                    DataSourceID="SuppliersDataSource" AppendDataBoundItems="true"
                    DataTextField="CompanyName" DataValueField="SupplierID"
                    SelectedValue='<%# Bind("SupplierID") %>'>
                    <asp:ListItem Value=">(None)</asp:ListItem>
                </asp:DropDownList><asp:ObjectDataSource ID="SuppliersDataSource"
                    runat="server" OldValuesParameterFormatString="original_{0}"
                    SelectMethod="GetSuppliers" TypeName="SuppliersBLL">
                </asp:ObjectDataSource>
            </EditItemTemplate>
            <ItemTemplate>
                <asp:Label ID="Label3" runat="server"
                    Text='<%# Bind("SupplierName") %>'></asp:Label>
            </ItemTemplate>
        </asp:TemplateField>
        <asp:BoundField DataField="QuantityPerUnit" HeaderText="Qty/Unit"
            SortExpression="QuantityPerUnit" />
        <asp:TemplateField HeaderText="Price" SortExpression="UnitPrice">
            <EditItemTemplate>
                <asp:TextBox ID="EditUnitPrice" runat="server"
                    Text='<%# Bind("UnitPrice", "{0:N2}") %>' Columns="8" />
                <asp:CompareValidator ID="CompareValidator1" runat="server"
                    ControlToValidate="EditUnitPrice"
                    ErrorMessage="Unit price must be a valid currency value without the
                    currency symbol and must have a value greater than or equal to zero."
                    Operator="GreaterThanEqual" Type="Currency"
                    ValueToCompare="0">*</asp:CompareValidator>
            </EditItemTemplate>
            <ItemTemplate>
                <asp:Label ID="Label4" runat="server"
                    Text='<%# Bind("UnitPrice", "{0:C}") %>'></asp:Label>
            </ItemTemplate>
        </asp:TemplateField>
        <asp:TemplateField HeaderText="Units In Stock" SortExpression="UnitsInStock">
            <EditItemTemplate>
                <asp:TextBox ID="EditUnitsInStock" runat="server"
                    Text='<%# Bind("UnitsInStock") %>' Columns="6"></asp:TextBox>
                <asp:CompareValidator ID="CompareValidator2" runat="server"
                    ControlToValidate="EditUnitsInStock"
                    ErrorMessage="Units in stock must be a valid number
                        greater than or equal to zero."
                    Operator="GreaterThanEqual" Type="Integer"
                    ValueToCompare="0">*</asp:CompareValidator>
            </EditItemTemplate>
            <ItemTemplate>
                <asp:Label ID="Label5" runat="server"
                    Text='<%# Bind("UnitsInStock", "{0:N0}") %>'></asp:Label>
            </ItemTemplate>
        </asp:TemplateField>
        <asp:TemplateField HeaderText="Units On Order" SortExpression="UnitsOnOrder">
            <EditItemTemplate>
                <asp:TextBox ID="EditUnitsOnOrder" runat="server"
                    Text='<%# Bind("UnitsOnOrder") %>' Columns="6"></asp:TextBox>
                <asp:CompareValidator ID="CompareValidator3" runat="server"
                    ControlToValidate="EditUnitsOnOrder"
                    ErrorMessage="Units on order must be a valid numeric value
                        greater than or equal to zero."
                    Operator="GreaterThanEqual" Type="Integer"
                    ValueToCompare="0">*</asp:CompareValidator>
            </EditItemTemplate>
            <ItemTemplate>
                <asp:Label ID="Label6" runat="server"
                    Text='<%# Bind("UnitsOnOrder", "{0:N0}") %>'></asp:Label>
            </ItemTemplate>
        </asp:TemplateField>
        <asp:TemplateField HeaderText="Reorder Level" SortExpression="ReorderLevel">
            <EditItemTemplate>
                <asp:TextBox ID="EditReorderLevel" runat="server"
                    Text='<%# Bind("ReorderLevel") %>' Columns="6"></asp:TextBox>
                <asp:CompareValidator ID="CompareValidator4" runat="server"
                    ControlToValidate="EditReorderLevel"
                    ErrorMessage="Reorder level must be a valid numeric value
                        greater than or equal to zero."
                    Operator="GreaterThanEqual" Type="Integer"
                    ValueToCompare="0">*</asp:CompareValidator>
            </EditItemTemplate>
            <ItemTemplate>
                <asp:Label ID="Label7" runat="server"
                    Text='<%# Bind("ReorderLevel", "{0:N0}") %>'></asp:Label>
            </ItemTemplate>
        </asp:TemplateField>
        <asp:CheckBoxField DataField="Discontinued" HeaderText="Discontinued"
            SortExpression="Discontinued" />
    </Columns>
</asp:GridView>

Tam olarak çalışan bir örneğimiz olmasına çok yaklaştık. Ancak, ortaya çıkacak ve bize sorunlar yaratacak birkaç nüans vardır. Buna ek olarak, eşzamanlılık ihlali oluştuğunda kullanıcıyı uyaran bir arabirime ihtiyacımız vardır.

Uyarı

Veri Web denetiminin özgün değerleri ObjectDataSource'a doğru şekilde geçirmesi için (daha sonra BLL'ye geçirilir), GridView EnableViewState özelliğinin (varsayılan) olarak ayarlanması true çok önemlidir. Görünüm durumunu devre dışı bırakırsanız, özgün değerler geri göndermede kaybolur.

ObjectDataSource'a Doğru Özgün Değerleri Geçirme

GridView'un yapılandırılma şekliyle ilgili birkaç sorun vardır. ObjectDataSource'un ConflictDetection özelliği bizim örneğimizdeki gibi CompareAllValues olarak ayarlanmışsa, GridView (veya DetailsView ya da FormView) tarafından ObjectDataSource'un Update() veya Delete() yöntemleri çağrıldığında, ObjectDataSource, GridView'un özgün değerlerini uygun Parameter örneklerine kopyalamaya çalışır. Bu işlemin grafik gösterimi için Şekil 2'ye geri bakın.

Özellikle, GridView'un özgün değerlerine, veriler GridView'a her bağlandığında iki yönlü veri bağlama ifadelerindeki değerler atanır. Bu nedenle, gerekli özgün değerlerin tümünün iki yönlü veri bağlama yoluyla yakalanması ve dönüştürülebilir bir biçimde sağlanması önemlidir.

Bunun neden önemli olduğunu görmek için bir dakika ayırarak sayfamızı bir tarayıcıda ziyaret edin. Beklendiği gibi, GridView her ürünü en soldaki sütunda Düzenle ve Sil düğmesiyle listeler.

Ürünler GridView'da Listelenir

Şekil 14: Ürünler GridView'da Listeleniyor (Tam boyutlu görüntüyü görüntülemek için tıklayın)

Herhangi bir ürün için Sil düğmesine tıklarsanız, bir FormatException oluşturulur.

FormatException'da Herhangi Bir Ürün Sonucunu Silmeye Çalışma

Şekil 15: Bir Içindeki FormatException Tüm Ürün Sonuçlarını Silmeye Çalışma (Tam boyutlu görüntüyü görüntülemek için tıklayın)

FormatException, ObjectDataSource özgün UnitPrice değeri okumaya çalıştığında tetiklenir. ItemTemplate para birimi ()UnitPrice olarak biçimlendirildiğinden<%# Bind("UnitPrice", "{0:C}") %>, 19,95 ABD doları gibi bir para birimi simgesi içerir. FormatException, ObjectDataSource bu dizeyi bir decimal içine dönüştürmeye çalıştığında gerçekleşir. Bu sorunu aşmak için çeşitli seçeneklerimiz vardır:

  • para birimi biçimlendirmesini ItemTemplate'den kaldırın. Yani <%# Bind("UnitPrice", "{0:C}") %> kullanmak yerine, sadece <%# Bind("UnitPrice") %> kullanın. Bunun dezavantajı, fiyatın artık biçimlendirilmemiş olmasıdır.
  • UnitPrice biçimlendirilmiş para birimini ItemTemplate içinde bir para birimi olarak görüntüleyin, ancak bunu yapmak için Eval anahtar kelimesini kullanın. Şunu hatırlayın: Eval tek yönlü veri bağlama işlemi gerçekleştirir. Özgün değerlerin UnitPrice değerini sağlamamız gerekir, bu nedenle, yine iki yönlü veri bağlama deyimine ItemTemplate ihtiyacımız olacak, ancak bu Visible özelliği olarak ayarlanan bir Etiket Web denetimine false yerleştirilebilir. ItemTemplate'da aşağıdaki işaretlemeyi kullanabiliriz:
<ItemTemplate>
    <asp:Label ID="DummyUnitPrice" runat="server"
        Text='<%# Bind("UnitPrice") %>' Visible="false"></asp:Label>
    <asp:Label ID="Label4" runat="server"
        Text='<%# Eval("UnitPrice", "{0:C}") %>'></asp:Label>
</ItemTemplate>
  • ItemTemplate kullanarak <%# Bind("UnitPrice") %>'den para birimi biçimlendirmesini kaldırın. GridView'un RowDataBound olay işleyicisinde, içinde UnitPrice değeri görüntülenen Label Web denetimine programatik olarak erişin ve Text özelliğini biçimlendirilmiş sürüme ayarlayın.
  • UnitPrice öğesini para birimi olarak biçimlendirilmiş bırakın. GridView'un RowDeleting olay işleyicisinde, mevcut özgün UnitPrice değeri ($19,95)'yi Decimal.Parse kullanarak gerçek bir ondalık değerle değiştirin. RowUpdating benzer bir şeyin nasıl gerçekleştirileceğini gördük.

Örneğim için, özelliği biçimlendirilmemiş Text değere bağlı iki yönlü veriler olan UnitPrice gizli bir Label Web denetimi ekleyerek ikinci yaklaşımı tercih ettim.

Bu sorunu çözdükten sonra herhangi bir ürün için Sil düğmesine tıklamayı yeniden deneyin. Bu kez, ObjectDataSource BLL'nin InvalidOperationException yöntemini çağırmaya çalıştığında bir UpdateProduct alırsınız.

ObjectDataSource Göndermek istediği Giriş Parametrelerine Sahip Bir Yöntem Bulamıyor

Şekil 16: ObjectDataSource Göndermek istediği Giriş Parametrelerine Sahip Bir Yöntemi Bulamıyor (Tam boyutlu görüntüyü görüntülemek için tıklayın)

İstisnanın iletisine baktığımızda, açıktır ki ObjectDataSource, DeleteProduct ve original_CategoryName giriş parametrelerini içeren bir BLL original_SupplierName yöntemini çağırmak istiyor. Bunun nedeni, ItemTemplate ve CategoryID TemplateFields için SupplierID ve CategoryName veri alanlarıyla iki yönlü Bind deyimlerini içeren s değerlerinin SupplierName bulunmasıdır. Bunun yerine, Bind ve CategoryID veri alanlarıyla birlikte SupplierID deyimler eklememiz gerekir. Bunu yapmak için, mevcut Bind deyimlerinin üzerine Eval deyimlerini koyun ve ardından, Text özellikleri CategoryID ve SupplierID veri alanlarına iki yönlü veri bağlama ile bağlı olan birer gizli Etiket denetimi ekleyin, aşağıda gösterildiği gibi:

<asp:TemplateField HeaderText="Category" SortExpression="CategoryName">
    <EditItemTemplate>
        ...
    </EditItemTemplate>
    <ItemTemplate>
        <asp:Label ID="DummyCategoryID" runat="server"
            Text='<%# Bind("CategoryID") %>' Visible="False"></asp:Label>
        <asp:Label ID="Label2" runat="server"
            Text='<%# Eval("CategoryName") %>'></asp:Label>
    </ItemTemplate>
</asp:TemplateField>
<asp:TemplateField HeaderText="Supplier" SortExpression="SupplierName">
    <EditItemTemplate>
        ...
    </EditItemTemplate>
    <ItemTemplate>
        <asp:Label ID="DummySupplierID" runat="server"
            Text='<%# Bind("SupplierID") %>' Visible="False"></asp:Label>
        <asp:Label ID="Label3" runat="server"
            Text='<%# Eval("SupplierName") %>'></asp:Label>
    </ItemTemplate>
</asp:TemplateField>

Bu değişikliklerle artık ürün bilgilerini başarıyla silip düzenleyebiliyoruz! 5. Adımda eşzamanlılık ihlallerinin algılandığını doğrulamayı inceleyeceğiz. Ancak şimdilik, tek bir kullanıcı için güncelleştirme ve silme işleminin beklendiği gibi çalıştığından emin olmak için birkaç kaydı güncelleştirmeyi ve silmeyi denemek için birkaç dakika bekleyin.

5. Adım: İyimser Eşzamanlılık Desteğini Test Etme

Eşzamanlılık ihlallerinin algılandığını doğrulamak için (verilerin üzerine körü körüne yazılmasına neden olmak yerine) bu sayfaya iki tarayıcı penceresi açmamız gerekir. Her iki tarayıcı örneğinde de Chai için Düzenle düğmesine tıklayın. Ardından, tarayıcılardan yalnızca birinde adı "Chai Tea" olarak değiştirin ve Güncelleştir'e tıklayın. Güncelleştirme başarılı olmalı ve GridView'u yeni ürün adı olarak "Chai Tea" ile ön düzenleme durumuna döndürmelidir.

Öte yandan diğer tarayıcı penceresi örneğinde, TextBox ürün adı hala "Chai" olarak gösterilir. Bu ikinci tarayıcı penceresinde, UnitPrice öğesini 25.00 olarak güncelleyin. İyimser eşzamanlılık desteği olmadan, ikinci tarayıcı oturumunda güncelleme düğmesine tıklamak ürün adını tekrar "Chai" yapar ve böylece ilk tarayıcı oturumu tarafından yapılan değişiklikleri geri alır. Ancak iyimser eşzamanlılık kullanıldığında, ikinci tarayıcı örneğinde Güncelleştir düğmesine tıklanması dbConcurrencyException sonucunu doğurabilir.

Eşzamanlılık İhlali Algılandığında DBConcurrencyException Oluşturulur

Şekil 17: Eşzamanlılık İhlali Algılandığında, bir DBConcurrencyException Oluşturulur (Tam boyutlu görüntüyü görüntülemek için tıklayın)

DBConcurrencyException yalnızca DAL'ın toplu güncelleştirme deseni kullanıldığında oluşturulur. Doğrudan veritabanı deseni özel durum oluşturmaz, yalnızca hiçbir satırın etkilenmediğini gösterir. Bunu göstermek için, her iki tarayıcı örneğinin GridView'ını da düzenleme öncesi durumlarına döndürin. Ardından, ilk tarayıcı örneğinde Düzenle düğmesine tıklayın ve ürün adını "Chai Tea" yerine "Chai" olarak değiştirin ve Güncelleştir'e tıklayın. İkinci tarayıcı penceresinde Chai için Sil düğmesine tıklayın.

Sil'e tıklandığında, sayfa yeniden yüklenir, GridView ObjectDataSource'un Delete() yöntemini çağırır ve ObjectDataSource, ProductsOptimisticConcurrencyBLL sınıfının DeleteProduct yöntemine özgün değerleri geçirerek bir çağrı yapar. İkinci tarayıcı örneğinin özgün ProductName değeri"Chai Tea"dır ve bu değer veritabanındaki geçerli ProductName değerle eşleşmez. Bu nedenle veritabanında DELETE yan tümcesini sağlayan bir kayıt bulunmadığından, WHERE deyimi veritabanında hiçbir satırı etkilemez. DeleteProduct yöntemi döndürür false ve ObjectDataSource'un verileri GridView'a geri döner.

Son kullanıcının perspektifinden, ikinci tarayıcı penceresinde Chai Tea için Sil düğmesine tıklanması ekranın yanıp sönmesine neden oldu ve geri döndükten sonra ürün hala oradadır, ancak artık "Chai" (ilk tarayıcı örneği tarafından yapılan ürün adı değişikliği) olarak listelenmiştir. Kullanıcı Sil düğmesine yeniden tıklarsa, GridView'un özgün ProductName değeri ("Chai") artık veritabanındaki değerle eşleştiğinden Sil başarılı olur.

Bu iki durumda da kullanıcı deneyimi idealden çok uzaktır. Toplu güncelleştirme deseni kullanılırken kullanıcıya özel durumun nitty-gritty ayrıntılarını DBConcurrencyException göstermek istemediğimiz açıktır. Kullanıcı komutu başarısız olduğundan veritabanı doğrudan desenini kullanma davranışı biraz kafa karıştırıcıydı, ancak nedeninin belirgin bir göstergesi yoktu.

Bu iki sorunu gidermek için, sayfada güncelleştirme veya silmenin neden başarısız olduğuna ilişkin bir açıklama sağlayan Etiket Web denetimleri oluşturabiliriz. Toplu güncelleştirme düzeni için, GridView'un son düzey olay işleyicisinde uyarı etiketini gerektiği gibi görüntüleyen bir DBConcurrencyException özel durumun oluşup oluşmadığını belirleyebiliriz. Doğrudan VERITABANı yöntemi için BLL yönteminin dönüş değerini inceleyebilir ( true başka bir şekilde bir satır etkilenmişse false ) ve gerektiğinde bilgilendirme iletisi görüntüleyebiliriz.

6. Adım: Bilgilendirici Mesajlar Ekleme ve Eşzamanlılık İhlali Durumunda Görüntüleme

Eşzamanlılık ihlali oluştuğunda, sergilenen davranış DAL'ın toplu güncelleştirmesinin mi yoksa VERITABANı doğrudan deseninin mi kullanıldığına bağlıdır. Eğitimimizde, güncelleme için toplu güncelleştirme deseni ve silme için veritabanı doğrudan erişim deseni kullanılarak her iki desen de kullanılmaktadır. Başlamak için sayfamıza verileri silmeye veya güncelleştirmeye çalışırken eşzamanlılık ihlalinin oluştuğuna ilişkin iki Etiket Web denetimi ekleyelim. Etiket denetiminin Visible ve EnableViewState özelliklerini false olarak ayarlayın; bu işlem, Visible özelliklerinin program aracılığıyla true olarak ayarlandığı belirli sayfa ziyaretleri hariç her sayfa ziyaretinde gizlenmelerine neden olur.

<asp:Label ID="DeleteConflictMessage" runat="server" Visible="False"
    EnableViewState="False" CssClass="Warning"
    Text="The record you attempted to delete has been modified by another user
           since you last visited this page. Your delete was cancelled to allow
           you to review the other user's changes and determine if you want to
           continue deleting this record." />
<asp:Label ID="UpdateConflictMessage" runat="server" Visible="False"
    EnableViewState="False" CssClass="Warning"
    Text="The record you attempted to update has been modified by another user
           since you started the update process. Your changes have been replaced
           with the current values. Please review the existing values and make
           any needed changes." />

Özelliklerini ayarlarken Visible, EnabledViewState, ve Text dışında, CssClass özelliğini Warning olarak da ayarladım. Bu, Etiketin büyük, kırmızı, italik ve kalın yazı tipinde görüntülenmesine neden oluyor. Bu CSS Warning sınıfı, Ekleme, Güncelleştirme ve Silme ile İlişkili Olayları İzleme öğreticisinde tanımlanıp Styles.css geri eklendi.

Bu Etiketleri ekledikten sonra, Visual Studio'daki Tasarımcı Şekil 18'e benzer görünmelidir.

Sayfaya İki Etiket Denetimi Eklendi

Şekil 18: Sayfaya İki Etiket Denetimi Eklendi (Tam boyutlu görüntüyü görüntülemek için tıklayın)

Bu Label Web denetimleri yerleştirildiğinde, bir eşzamanlılık ihlalinin ne zaman meydana geldiğini nasıl belirleyeceğimizi incelemeye hazırız. Bu durumda, uygun olan Label'ın Visible özelliği true olarak ayarlanarak bilgilendirici mesaj görüntülenebilir.

Güncelleştirme Sırasında Eşzamanlılık İhlallerini İşleme

Şimdi ilk olarak toplu güncelleştirme desenini kullanırken eşzamanlılık ihlallerinin nasıl işleneceğini inceleyelim. Toplu güncelleştirme düzeniyle ilgili bu tür ihlaller özel durum DBConcurrencyException oluşturmasına neden olduğundan, güncelleştirme işlemi sırasında bir DBConcurrencyException özel durumun oluşup oluşmadığını belirlemek için ASP.NET sayfamıza kod eklememiz gerekir. Bu durumda, başka bir kullanıcı kaydı düzenlemeye başladığı ve Güncelleştir düğmesine tıklaması arasında aynı verileri değiştirdiği için değişikliklerinin kaydedilmediğini açıklayan bir ileti görüntülememiz gerekir.

bir ASP.NET Sayfasında BLL- ve DAL-Level Özel Durumlarını İşleme öğreticisinde gördüğümüz gibi, bu tür özel durumlar veri Web denetiminin son düzey olay işleyicilerinde algılanabilir ve gizlenebilir. Bu nedenle, GridView'un RowUpdated olayı için bir özel durum oluşturulup oluşturulmadığını denetleen bir DBConcurrencyException olay işleyicisi oluşturmamız gerekir. Bu olay işleyicisine, aşağıdaki olay işleyici kodunda gösterildiği gibi, güncelleme işlemi sırasında oluşturulan herhangi bir özel duruma bir referans aktarılır.

protected void ProductsGrid_RowUpdated(object sender, GridViewUpdatedEventArgs e)
{
    if (e.Exception != null && e.Exception.InnerException != null)
    {
        if (e.Exception.InnerException is System.Data.DBConcurrencyException)
        {
            // Display the warning message and note that the
            // exception has been handled...
            UpdateConflictMessage.Visible = true;
            e.ExceptionHandled = true;
        }
    }
}

Bir DBConcurrencyException özel durum karşısında, bu olay işleyicisi UpdateConflictMessage Etiket denetimini görüntüler ve özel durumun işlendiğini gösterir. Bu kod devreye alındığında, kayıt güncelleştirilirken eşzamanlılık ihlali meydana geldiğinde, kullanıcının değişiklikleri kaybolur, çünkü aynı anda başka bir kullanıcının değişikliklerinin üzerine yazılmış olur. Özellikle, GridView ön düzenleme durumuna döndürülür ve geçerli veritabanı verilerine bağlıdır. Bu, GridView satırını daha önce görünür olmayan diğer kullanıcının değişiklikleriyle güncelleştirir. Buna ek olarak, UpdateConflictMessage Etiket denetimi kullanıcıya ne olduğunu açıklar. Bu olay dizisi Şekil 19'da ayrıntılı olarak yer alır.

Bir Kullanıcının Güncelleştirmeleri Eşzamanlılık İhlali Karşısında Kaybolur

Şekil 19: Eşzamanlılık İhlaliNin Karşısında Kullanıcı Güncelleştirmeleri Kayboldu (Tam boyutlu görüntüyü görüntülemek için tıklayın)

Uyarı

Alternatif olarak, GridView'ı düzenleme öncesi durumuna döndürmek yerine, geçirilen KeepInEditMode nesnesinin GridViewUpdatedEventArgs özelliğini true olarak ayarlayarak GridView'ı düzenleme durumunda bırakabiliriz. Ancak bu yaklaşımı benimserseniz, diğer kullanıcının değerlerini düzenleme arabirimine yüklemek için verileri GridView'a yeniden bağladığınızdan emin olun (yöntemini çağırarak DataBind()). Bu öğreticiyle indirilebilen kodda, RowUpdated olay işleyicisinde bu iki kod satırı açıklama satırı olarak bulunur; GridView'un eşzamanlılık ihlalinden sonra düzenleme modunda kalmasını sağlamak için bu satırların açıklamasını kaldırmanız yeterlidir.

Silerken Eşzamanlılık İhlallerine Yanıt Verme

Veritabanı doğrudan düzeninde eşzamanlılık ihlali karşısında özel durum oluşmaz. Bunun yerine, WHERE yan tümcesi herhangi bir kayıtla eşleşmediğinden veritabanı deyimi hiçbir kaydı etkilemez. BLL'de oluşturulan tüm veri değiştirme yöntemleri, tam olarak bir kaydı etkileyip etkilemediklerini belirten bir Boole değeri döndürebilecek şekilde tasarlanmıştır. Bu nedenle, bir kayıt silinirken eşzamanlılık ihlali oluşup oluşmadığını belirlemek için BLL DeleteProduct yönteminin dönüş değerini inceleyebiliriz.

ObjectDataSource'un son düzey olay işleyicilerinde bir BLL metodunun dönüş değeri, olay işleyicisine geçirilen nesnenin ReturnValue özelliği aracılığıyla ObjectDataSourceStatusEventArgs incelenebilir. yönteminden dönüş değerini belirlemek istediğimizden DeleteProduct ObjectDataSource'un Deleted olayı için bir olay işleyicisi oluşturmamız gerekir. ReturnValue özelliği object türündedir ve bir istisna kaldırıldıysa ve yöntem bir değer döndüremeden önce kesintiye uğradıysa null olabilir. Bu nedenle, önce özelliğin ReturnValuenull olmadığından ve bir Boole değeri olduğundan emin olmamız gerekir. Eğer bu denetim geçerse, DeleteConflictMessageReturnValue ise false Etiket kontrolünü gösteririz. Bu, aşağıdaki kod kullanılarak gerçekleştirilebilir:

protected void ProductsOptimisticConcurrencyDataSource_Deleted(
    object sender, ObjectDataSourceStatusEventArgs e)
{
    if (e.ReturnValue != null && e.ReturnValue is bool)
    {
        bool deleteReturnValue = (bool)e.ReturnValue;
        if (deleteReturnValue == false)
        {
            // No row was deleted, display the warning message
            DeleteConflictMessage.Visible = true;
        }
    }
}

Eşzamanlılık ihlali karşısında kullanıcının silme isteği iptal edilir. GridView yenilenir ve kullanıcının sayfayı yüklediği zaman ile Sil düğmesine tıklaması arasındaki kayıt için yapılan değişiklikler gösterilir. Böyle bir ihlal gerçekleştiğinde DeleteConflictMessage , az önce ne olduğunu açıklayan Etiket gösterilir (bkz. Şekil 20).

Bir Kullanıcının Silinmesi, Eşzamanlılık İhlali Karşısında İptal Edilir

Şekil 20: Eşzamanlılık İhlali Nedeniyle Kullanıcının Silmesi İptal Edildi (Tam boyutlu görüntüyü görüntülemek için tıklayın)

Özet

Eşzamanlılık ihlalleri için fırsatlar, birden çok eşzamanlı kullanıcının verileri güncelleştirmesine veya silmesine izin veren her uygulamada mevcuttur. Bu tür ihlaller göz ardı edilirse, iki kullanıcı aynı verileri aynı anda güncellediğinde, son güncellemeyi yapan kullanıcı "kazanır" ve diğer kullanıcının değişikliklerinin üzerine yazar. Alternatif olarak, geliştiriciler iyimser veya kötümser eşzamanlılık denetimi uygulayabilir. İyimser eşzamanlılık denetimi, eşzamanlılık ihlallerinin seyrek olduğunu varsayar ve yalnızca eşzamanlılık ihlali oluşturacak güncelleştirme veya silme komutuna izin vermemektedir. Kötümser eşzamanlılık denetimi, eşzamanlılık ihlallerinin sık olduğunu varsayar ve yalnızca bir kullanıcının güncelleştirme veya silme komutunu reddetmenin kabul edilemez olduğunu varsayar. Kötümser eşzamanlılık denetimiyle, kaydın güncelleştirilmesi kaydı kilitlemeyi ve böylece diğer kullanıcıların kilitliyken kaydı değiştirmesini veya silmesini engellemeyi içerir.

.NET'te Yazılan DataSet, iyimser eşzamanlılık denetimini desteklemek için işlevsellik sağlar. Özellikle, UPDATE veritabanına verilen ve DELETE deyimleri tablonun tüm sütunlarını içerir, böylece güncelleştirme veya silme işleminin yalnızca kaydın geçerli verileri kullanıcının güncelleştirme veya silme işlemini gerçekleştirirken sahip olduğu özgün verilerle eşleşmesi durumunda gerçekleşmesini sağlar. DAL iyimser eşzamanlılığı destekleyecek şekilde yapılandırıldıktan sonra BLL yöntemlerinin güncelleştirilmiş olması gerekir. Ayrıca, BLL'ye çağrıda bulunan ASP.NET sayfası, ObjectDataSource'un veri Web denetiminden özgün değerleri alıp BLL'ye iletecek şekilde yapılandırılması gerekir.

Bu öğreticide gördüğümüz gibi, ASP.NET bir web uygulamasında iyimser eşzamanlılık denetimi uygulamak için DAL ve BLL'nin güncelleştirilmesi ve ASP.NET sayfasına destek eklenmesi gerekir. Bu eklenen çalışmanın zamanınızın ve çabanızın akıllıca bir yatırımı olup olmadığı uygulamanıza bağlıdır. Sık sık veri güncelleştiren eşzamanlı kullanıcılarınız varsa veya güncelleştirdikleri veriler birbirinden farklıysa, eşzamanlılık denetimi önemli bir sorun değildir. Ancak, sitenizde aynı verilerle çalışan birden çok kullanıcınız varsa eşzamanlılık denetimi, bir kullanıcının güncelleştirmelerinin veya silmelerinin farkında olmadan başka birinin üzerine yazılmasını önlemeye yardımcı olabilir.

Mutlu Programlama!

Yazar Hakkında

Yedi ASP/ASP.NET kitabının yazarı ve 4GuysFromRolla.com kurucusu Scott Mitchell, 1998'den beri Microsoft Web teknolojileriyle çalışmaktadır. Scott bağımsız bir danışman, eğitmen ve yazar olarak çalışır. Son kitabı Sams Teach Yourself ASP.NET 24 Hours 2.0'dır. Ona adresinden mitchell@4GuysFromRolla.comulaşabilirsiniz.