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

tarafından Scott Mitchell

PDF’yi İndir

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 işlemek için iyimser eşzamanlılık denetimi uygulayacağız.

Giriş

Kullanıcıların yalnızca 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 bir başkasının değişikliklerinin üzerine yazma tehdidi yoktur. Ancak, birden çok kullanıcının verileri güncelleştirmesine veya silmesine izin veren 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, en son değişikliklerini işleyen kullanıcı ilk 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ıklar. 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 güncelleştirilebilir alanlarının tümünü ayarlayan bir deyimdir (Jisun yalnızca bir UPDATE alanı güncelleştirmiş olsa bile). ProductName Bu noktada veritabanı bu ürün için "Chai Tea", Beverages kategorisi, tedarikçi Egzotik Sıvılar 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" CategoryID olarak, öğesini ilgili İçecek kategorisi kimliğine ayarlayan bir UPDATE deyimle sonuçlanıyor ve bu şekilde devam ediyor. Jisun'un ürün adında yapılan değişikliklerin üzerine yazıldı. Şekil 1'de bu olay serisi grafik olarak gösterilmektedir.

İ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şiklik Olasılığı Vardır

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

Benzer şekilde, iki kullanıcı bir sayfayı ziyaret ettiğinde, bir kullanıcı başka bir kullanıcı tarafından silindiğinde kaydı güncelleştirmenin ortasında olabilir. 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.

Kullanılabilir üç eşzamanlılık denetimi stratejisi vardır:

  • Hiçbir Şey Yapma - 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 - arada sırada eşzamanlılık çakışmaları olsa da, bu tür çakışmaların büyük çoğunluğunun 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, bu 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 kadarki tüm öğreticilerimiz varsayılan eşzamanlılık çözümleme stratejisini kullandı; yani son yazma işleminin kazanmasına izin verdik. Bu öğreticide iyimser eşzamanlılık denetiminin nasıl uygulandığını inceleyeceğiz.

Not

Bu öğretici serisindeki 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, uygun şekilde iptal edilmediği takdirde diğer kullanıcıların verileri güncelleştirmesini engelleyebilir. Örneğin, bir kullanıcı bir kaydı düzenlemek üzere kilitleyip kilidi 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 ulaşılırsa kilidi iptal eden bir zaman aşımı olur. 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ığı uygulamaya yönelik çeşitli yaklaşımlar vardır (bir dizi 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 Türü Belirtilen 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ık etkinleştirildiğinde TableAdapter'ın UPDATE ve DELETE deyimleri, yan tümcedeki WHERE tüm özgün değerlerin karşılaştırmasını içerecek şekilde artırılır. Örneğin aşağıdaki UPDATE deyim, yalnızca geçerli veritabanı değerleri GridView'daki kayıt güncelleştirilirken 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, düzenle @original_UnitPrice düğmesine tıklandığında 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

Not

Bu UPDATE deyim okunabilirlik için basitleştirilmiştir. Uygulamada, UnitPrice yan tümcesinde WHERE denetim daha fazla yer alabilir çünkü UnitPrice s içerebilir NULL ve her zaman False döndürüyorsa NULL = NULL denetleme (bunun yerine kullanmanız IS NULLgerekir).

Farklı bir temel alınan UPDATE deyimi kullanmanın yanı sıra, bir TableAdapter'ın iyimser eşzamanlılık kullanacak şekilde yapılandırılması, veritabanı doğrudan yöntemlerinin imzasını da değiştirir. veri erişim katmanı oluşturma adlı ilk öğreticimizden hatırlayacağınız gibi veritabanı doğrudan yöntemleri, giriş parametreleri olarak skaler değerlerin listesini kabul eden yöntemlerdir (kesin olarak belirlenmiş bir 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ğerler için giriş parametreleri 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 zorunlu tutar), bunun yerine iyimser eşzamanlılık kullanan bir TableAdapter ekleyeceğimiz adlı NorthwindOptimisticConcurrencyyeni bir Products Türlenmiş Veri Kümesi 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ır olduktan 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 Yazılan Veri Kümesi oluşturmak için, klasörün içindeki App_Code klasöre sağ tıklayın DAL 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, bağlantısı kurulacak veritabanını belirtmemiz ve içindeki ayarını Web.configkullanarak NORTHWNDConnectionString aynı Northwind veritabanına bağlanmamız 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, burada da bu seçeneği 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. Ürünün sağlayıcı ve kategori adlarıyla birlikte tüm Product sütunları döndüren özgün DAL'den TableAdapter için Products kullanılan SQL sorgusunun aynısını kullanalım:

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 TablosuAdapter'dan Aynı SQL Sorgusunu Kullanma

Şekil 5: Özgün DAL'deki 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 denetimini 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 gösterir. Özgün DAL'mizde kullandığımız adlandırma kurallarını yansıtmak için GetData olan Return a DataTable deseninin yöntem adını GetProducts olarak 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ım Aracı kesin olarak yazılmış Products bir DataTable ve TableAdapter içerecektir. DataTable'ın Products başlık çubuğuna ProductsOptimisticConcurrencysağ tıklayıp bağlam menüsünden Yeniden Adlandır'ı seçerek yapabileceğiniz DataTable'ı olarak yeniden adlandırmak için biraz zaman ayırın.

Yazılan DataSet'e DataTable ve TableAdapter Eklendi

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

TableAdapter (iyimser eşzamanlılık kullanan) ile Products TableAdapter (olmayan) arasındaki ve DELETE sorguları arasındaki UPDATEProductsOptimisticConcurrency farkları görmek için TableAdapter'a tıklayın ve Özellikler penceresi gidin. DeleteCommand DAL'ın CommandText güncelleştirme veya silmeyle ilgili yöntemleri çağrıldığında ve UpdateCommand özelliklerinin alt özelliklerinde veritabanına gönderilen gerçek SQL söz dizimini görebilirsiniz. ProductsOptimisticConcurrency TableAdapter DELETE için kullanılan deyim şu şekildedir:

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, WHERE TableAdapter deyiminde DELETE iyimser eşzamanlılık kullanan yan tümcesi, tablonun mevcut sütun değerlerinin her biri Product ile GridView 'un (veya DetailsView veya FormView) son doldurulma zamanındaki özgün değerler arasında bir karşılaştırma içerir. , ProductNameve Discontinued dışındaki tüm alanlar değerlere sahip NULL olabileceğinden ProductIDyan tümcedeki WHERE değerleri doğru karşılaştırmak NULL için ek parametreler ve denetimler 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üncelleştirme ve silme bilgilerini sağlayacaktır. Ancak yine de yöntemini TableAdapter'a ProductsOptimisticConcurrency eklememiz GetProductByProductID(productID) gerekir.

Bunu yapmak için TableAdapter'ın başlık çubuğuna (ve GetProducts yöntem adlarının hemen üstündeki Fill 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, geçici bir SQL deyimi kullanarak yöntemini oluşturmayı tercih edin (bkz. Şekil 4). GetProductByProductID(productID) yöntemi belirli bir ürünle ilgili bilgileri döndürdüğünden, bu sorgunun satır döndüren bir SELECT sorgu türü olduğunu belirtin.

Sorgu Türünü

Şekil 9: Sorgu Türünü " satır döndüren"SELECT olarak işaretleyin (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ı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 WHERE Ürün Kaydını Döndürmek için Önceden Yüklenmiş Sorguya Yan Tümce Ekleme (Tam boyutlu görüntüyü görüntülemek için tıklayın)

Son olarak, oluşturulan yöntem adlarını ve GetProductByProductIDolarak FillByProductID 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. Hem AddProduct yöntemi hem UpdateProduct de aşırı yüklemeleri toplu güncelleştirme desenini kullanır ve bir ProductRow örneği TableAdapter'ın Update yöntemine geçirir. DeleteProduct Diğer yandan yöntemi, TableAdapter'ın Delete(productID) yöntemini çağırarak veritabanı doğrudan desenini kullanır.

Yeni ProductsOptimisticConcurrency TableAdapter ile db 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 deyiminin yan tümcesinde WHERE bu ek giriş parametrelerinin DELETE değerlerini kullanır, yalnızca veritabanının geçerli değerleri özgün kayıtlarla eşlerse belirtilen kaydı siler.

Toplu güncelleştirme deseninde 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'mizle çalışmak için yeni bir İş Mantığı Katmanı sınıfı oluşturalım.

adlı bir sınıfı ProductsOptimisticConcurrencyBLLBLL klasörün içindeki App_Code klasöre ekleyin.

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

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

Ardından sınıfına aşağıdaki kodu 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 emreden özniteliğini bulacaksınız System.ComponentModel.DataObject .

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

İyimser Eşzamanlılık ile Db Doğrudan Desenini Kullanarak Bir Ü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. Silme için yeni değer olmadığından yalnızca özgün değerlerin geçirilmesi gerekir. BLL'mizde tüm özgün parametreleri giriş parametreleri olarak kabul etmeliyiz. sınıfındaki yönteminin DeleteProductProductsOptimisticConcurrencyBLL 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;
}

Kullanıcı Sil düğmesine WHERE tıkladığında özgün değerler (GridView'a (veya DetailsView veya FormView) en son yüklenen değerler) veritabanındaki değerlerden farklıysa yan tümcesi hiçbir veritabanı kaydıyla eşleşmez ve hiçbir kayıt etkilenmez. 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 düzeni 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. Yani yöntemi bir DataRow, Update datarows dizisi, DataTable veya Typed DataSet bekler. Özgün değerleri belirtmek için ek giriş parametresi yoktur. DataTable, DataRow'ları için özgün ve değiştirilmiş değerleri takip ettiğinden bu mümkündür. DAL deyimini UPDATE@original_ColumnName verdiği zaman, parametreler DataRow'un özgün değerleriyle doldurulurken @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'ın GetProductByProductID(productID) yöntemini kullanarak geçerli veritabanı ürün bilgilerini bir ProductRow örneğe okuma
  2. 1. Adım'dan örneğe ProductRow yeni değerler atama
  3. TableAdapter'ın Update yöntemini çağırın ve örneğini ProductRow geçirin

Ancak bu adım dizisi, 1. Adımda doldurulan değerlerin doğrudan veritabanından doldurulması nedeniyle iyimser eşzamanlılığı ProductRow 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ı kullanmak için yöntem aşırı yüklemelerini değiştirmemiz UpdateProduct gerekir:

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

1. adım, belirtilen ürün kaydı için tüm geçerli veritabanı değerlerini okur. Bu adım, tüm ürün sütunlarını güncelleştiren aşırı yüklemede UpdateProduct 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 örneğe ProductsOptimisticConcurrencyRow atandıktan sonra, AcceptChanges() geçerli DataRow değerlerini deyimdeki parametrelerde @original_ColumnName kullanılacak özgün değerler olarak işaretleyen yöntemi çağrılır UPDATE . Ardından yeni parametre değerleri ve öğesine atanır ProductsOptimisticConcurrencyRow ve son olarak Update datarow'a geçirilerek yöntemi çağrılır.

Aşağıdaki kod, UpdateProduct tüm ürün veri alanlarını giriş parametreleri olarak kabul eden aşırı yüklemeyi gösterir. Burada gösterilmese de, ProductsOptimisticConcurrencyBLL bu öğreticinin indirmesine dahil edilen sınıf, giriş parametreleri olarak yalnızca ü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 Özgün ve Yeni Değerleri BLL Yöntemlerine 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 klasördeki EditInsertDelete sayfayı OptimisticConcurrency.aspx açın ve Tasarım Aracı bir GridView ekleyerek özelliğini olarak ProductsGridayarlayınID. 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, nesnesini kullanacak ProductsOptimisticConcurrencyBLL ş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 GetProductsaçılan listelerden , UpdateProductve 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 DeleteParametersDeleteProduct yöntemindeki on giriş parametresinin ProductsOptimisticConcurrencyBLL her biri için bir Parameter örnek içerir. Benzer şekilde, UpdateParameters koleksiyonu içindeki UpdateProductgiriş parametrelerinin her biri için bir Parameter ö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ğerler için giriş parametresi adlarını gösterir. Özgün değerleri BLL'ye aktardığımız için bu özelliği kaldırmayın .

Not

özelliğinin OldValuesParameterFormatString değeri, BLL'deki özgün değerleri bekleyen giriş parametresi adlarıyla eşlenmelidir. Bu parametreleri original_productName, vboriginal_supplierID. adlandırdığımız için özellik değerini olarak original_{0}bırakabilirsinizOldValuesParameterFormatString. Ancak, BLL yöntemlerinin giriş parametrelerinde , gibi old_productNameadlar varsa, özelliğini old_{0}olarak güncelleştirmeniz OldValuesParameterFormatStringold_supplierIDgerekir.

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; İyimser eşzamanlılık kullanırken bu seçeneği belirleyin

özelliğini CompareAllValuesolarak ayarlamak ConflictDetection için biraz zaman ayırın.

GridView Ö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, ve ShowDeleteButton her ikisi de olarak trueayarlanmış bir CommandField ShowEditButton 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 kabul edilebilir bir deneyimdir. CategoryID ve SupplierID BoundFields, kullanıcının uygun kategoriyi ve sağlayıcıyı kimlik numaraları olarak girmesini gerektiren TextBoxes olarak işlenir. Sayısal alanlar için biçimlendirme ve ürünün adının sağlandığından ve birim fiyatının, stoktaki birimlerin, siparişteki birimlerin ve yeniden sıralama düzeyi değerlerinin hem uygun 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 özelleştirilebilir. Bu GridView'ı ve düzenleme arabirimini aşağıdaki yollarla değiştirdim:

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

Önceki öğreticilerde bu görevlerin nasıl yerine getirilebilir olduğunu incelediğimizden, burada son bildirim temelli söz dizimini listeleyip uygulamayı uygulama 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 örnekle çok yakınız. Ancak, sürünecek ve bize sorunlara neden olacak birkaç incelik vardır. Buna ek olarak, eşzamanlılık ihlali oluştuğunda kullanıcıyı uyaran bir arabirime ihtiyacımız vardır.

Not

Veri Web denetiminin özgün değerleri ObjectDataSource'a doğru bir ş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.

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

GridView'un yapılandırılma şekliyle ilgili birkaç sorun vardır. ObjectDataSource'un ConflictDetection özelliği (bizim olduğu gibi) olarak ayarlanırsa CompareAllValues , ObjectDataSource'un Update() veya Delete() yöntemleri GridView (veya DetailsView veya FormView) tarafından ç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 deyimlerindeki 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 bir tarayıcıda sayfamızı 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 Listelenir (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 Herhangi Bir FormatException Ürün Sonucunu Silmeye Çalışma (Tam boyutlu görüntüyü görüntülemek için tıklayın)

FormatException, ObjectDataSource özgün UnitPrice değerde okumayı denediğinde oluşturulur. ItemTemplate para birimi ()<%# Bind("UnitPrice", "{0:C}") %> olarak biçimlendirildiğindenUnitPrice, 19,95 ABD doları gibi bir para birimi simgesi içerir. , FormatException ObjectDataSource bu dizeyi ' decimalye dönüştürmeye çalışırken gerçekleşir. Bu sorunu aşmak için bir dizi seçeneğimiz vardır:

  • para birimi biçimlendirmesini ItemTemplateiçinden kaldırın. Yani, kullanmak <%# Bind("UnitPrice", "{0:C}") %>yerine kullanın <%# Bind("UnitPrice") %>. Bunun dezavantajı, fiyatın artık biçimlendirilmemiş olmasıdır.
  • UnitPrice Biçimlendirilmiş para birimini içinde ItemTemplatebir para birimi olarak görüntüleyin, ancak bunu gerçekleştirmek için anahtar sözcüğünü Eval kullanın. Bunun tek yönlü veri bağlama işlemi gerçekleştirdiğini Eval hatırlayın. Özgün değerlerin UnitPrice değerini sağlamamız gerekir, bu nedenle içinde iki yönlü bir veri bağlama deyimine ItemTemplateihtiyacımız vardır, ancak bu özellik olarak ayarlanmış bir Etiket Web denetimine Visiblefalseyerleştirilebilir. ItemTemplate içinde 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>
  • kullanarak <%# Bind("UnitPrice") %>para birimi biçimlendirmesini ItemTemplate'den kaldırın. GridView'un RowDataBound olay işleyicisinde, değerin görüntülendiği Label Web denetimine UnitPrice program aracılığıyla erişin ve özelliğini biçimlendirilmiş sürüme ayarlayın Text .
  • UnitPrice Para birimi olarak biçimlendirilmiş olarak bırakın. GridView'un RowDeleting olay işleyicisinde, kullanarak Decimal.Parsemevcut özgün UnitPrice değeri ($19,95) gerçek ondalık değerle değiştirin. Bir ASP.NET Sayfası öğreticisinde RowUpdatingBLL- ve DAL-Level Özel Durumlarını İşleme öğreticisindeki olay işleyicisinde benzer bir şeyin nasıl gerçekleştirildiğini gördük.

Örneğim için, özelliği biçimlendirilmemiş UnitPrice değere bağlı iki yönlü veriler olan Text gizli bir Etiket 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 UpdateProduct yöntemini çağırmaya çalıştığında bir InvalidOperationException 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)

Özel durumun iletisine baktığımızda, ObjectDataSource'un ve original_SupplierName giriş parametrelerini içeren original_CategoryName bir BLL DeleteProduct yöntemini çağırmak istediği açıktır. Bunun nedeni, ve SupplierID TemplateFields için s değerlerinin CategoryID şu anda ve SupplierName veri alanlarıyla birlikte iki yönlü Bind deyimleri içermesidirCategoryName.ItemTemplate Bunun yerine ve SupplierID veri alanlarıyla CategoryID deyimler eklememiz Bind gerekir. Bunu yapmak için, mevcut Bind deyimlerini deyimlerle Eval değiştirin ve ardından aşağıda gösterildiği gibi iki yönlü veri bağlama kullanarak ve SupplierID veri alanlarına bağlı CategoryID olan gizli Etiket denetimleri Text ekleyin:

<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ı nasıl doğrulayacağımıza bakacağız. Ancak şimdilik, tek bir kullanıcı için güncelleştirme ve silmenin beklendiği gibi çalıştığından emin olmak için birkaç kaydı güncelleştirmeyi ve silmeyi denemeniz birkaç dakika sürer.

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'ı yeni ürün adı olarak "Chai Tea" ile ön düzenleme durumuna döndürmelidir.

Ancak diğer tarayıcı penceresi örneğinde TextBox ürün adı hala "Chai" olarak görünüyor. Bu ikinci tarayıcı penceresinde öğesini olarak 25.00güncelleştirinUnitPrice. İyimser eşzamanlılık desteği olmadan, ikinci tarayıcı örneğindeki güncelleştirmeye tıklanması ürün adını "Chai" olarak değiştirir ve böylece ilk tarayıcı örneği tarafından yapılan değişikliklerin üzerine yazılır. Ancak iyimser eşzamanlılık uygulandığında, ikinci tarayıcı örneğinde Güncelleştir düğmesine tıklanması DBConcurrencyException ile sonuçlanmıştır.

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'nin toplu güncelleştirme deseni kullanıldığında oluşturulur. Veritabanı doğrudan 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ı düzenleme öncesi durumuna döndür. 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ıklanması üzerine sayfa geri gönderir, GridView ObjectDataSource'un Delete() yöntemini çağırır ve ObjectDataSource, sınıfın ProductsOptimisticConcurrencyBLLDeleteProduct yöntemine çağrı yaparak özgün değerleri geçirir. İkinci tarayıcı örneğinin özgün ProductName değeri "Chai Tea"dır ve veritabanındaki geçerli ProductName değerle eşleşmez. Bu nedenle DELETE , veritabanında yan tümcesinin karşılediği kayıt olmadığından, veritabanına WHERE verilen deyim sıfır satırı etkiler. 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" olarak listelenmiştir (ilk tarayıcı örneği tarafından yapılan ürün adı değişikliği). Kullanıcı Sil düğmesine yeniden tıklarsa, GridView'un özgün ProductName değeri ("Chai") 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 desenini kullanırken kullanıcıya özel durumun nitty-gritty ayrıntılarını DBConcurrencyException göstermek istemiyoruz. Kullanıcılar komutu başarısız olduğundan veritabanı doğrudan desenini kullanma davranışı biraz kafa karıştırıcıdır, ancak bunun nedeninin kesin bir göstergesi yoktur.

Bu iki sorunu gidermek için, sayfada güncelleştirme veya silme işleminin 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. Veritabanı doğrudan yöntemi için BLL yönteminin dönüş değerini inceleyebilir ( true bir satır etkilendiyse, false aksi takdirde) ve gerektiğinde bilgilendirsel bir ileti görüntüleyebiliriz.

6. Adım: Bilgilendirici İletiler Ekleme ve Eşzamanlılık İhlali Karşısında Görüntüleme

Eşzamanlılık ihlali oluştuğunda, sergilenen davranış DAL'nin toplu güncelleştirmesinin mi yoksa VERITABANı doğrudan deseninin mi kullanıldığına bağlıdır. Öğreticimizde, güncelleştirme için kullanılan toplu güncelleştirme deseni ve silme için kullanılan veritabanı doğrudan deseni ile birlikte 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 olarak falseayarlayın; bu, özelliklerinin program aracılığıyla olarak ayarlandığı truebelirli sayfa ziyaretleri dışında her sayfa ziyaretinde gizlenmelerine Visible 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." />

, ve TextEnabledViewStateözelliklerini ayarlamaya Visibleek olarak, özelliğini Warningolarak da ayarladımCssClass. Bu özellik, Label'ların 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ı inceleme öğreticisinde Styles.css'ye tanımlandı ve eklendi.

Bu Etiketleri ekledikten sonra Visual Studio'daki Tasarım Aracı Ş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 Etiket Web denetimleri hazır olduğunda, bir eşzamanlılık ihlalinin ne zaman oluştuğunun nasıl belirleneceğini incelemeye hazırız. Bu noktada, bilgilendirici iletiyi görüntüleyen uygun Label'ın Visible özelliği olarak trueayarlanabilir.

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şlendiğini inceleyelim. Toplu güncelleştirme düzeniyle ilgili bu tür ihlaller özel DBConcurrencyException durum 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ğinden, kullanıcıya 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, veri Web denetiminin son düzey olay işleyicilerinde bu tür özel durumlar algılanabilir ve gizlenebilir. Bu nedenle, GridView'un RowUpdated olayı için bir özel durum oluşturulup oluşturulmadığını denetleye bir DBConcurrencyException olay işleyicisi oluşturmamız gerekir. Bu olay işleyicisi, aşağıdaki olay işleyici kodunda gösterildiği gibi güncelleştirme işlemi sırasında oluşturulan herhangi bir özel duruma bir başvuru geçirilir:

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 Etiket denetimini görüntüler UpdateConflictMessage ve özel durumun işlendiğini gösterir. Bu kod uygulandığında, bir kayıt güncelleştirilirken eşzamanlılık ihlali oluştuğunda, kullanıcının değişiklikleri kaybolur, çünkü aynı anda başka bir kullanıcının değişikliklerinin üzerine yazılırdı. Özellikle, GridView ön düzenleme durumuna döndürülür ve geçerli veritabanı verilerine bağlanı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. Ayrıca, UpdateConflictMessage Etiket denetimi kullanıcıya az önce ne olduğunu açıklar. Bu olay dizisi Şekil 19'da ayrıntılı olarak anlatılır.

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

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

Not

Alternatif olarak, GridView'u düzenleme öncesi durumuna döndürmek yerine, geçirilen nesnenin özelliğini true olarak ayarlayarak KeepInEditMode GridView'u GridViewUpdatedEventArgs düzenleme durumunda bırakabiliriz. Ancak bu yaklaşımı benimsediğinizde, diğer kullanıcının değerlerinin düzenleme arabirimine yüklenmesi için verileri GridView'a yeniden bağlamayı (yöntemini çağırarak DataBind() ) emin olun. Bu öğreticiyle indirilebilen kod, olay işleyicisinde RowUpdated şu iki kod satırına açıklama satırı eklenmiştir; GridView'un eşzamanlılık ihlalinden sonra düzenleme modunda kalması için bu kod satırlarını açıklamadan çıkarmanız yeterlidir.

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

Veritabanı doğrudan deseni ile eşzamanlılık ihlaline karşı oluşturulan bir özel durum yoktur. 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ürecekler şekilde tasarlanmıştır. Bu nedenle, bir kayıt silinirken eşzamanlılık ihlalinin oluşup oluşmadığını belirlemek için BLL'nin DeleteProduct yönteminin dönüş değerini inceleyebiliriz.

Bir BLL yönteminin dönüş değeri, olay işleyicisine geçirilen nesnenin ObjectDataSourceStatusEventArgs özelliği aracılığıyla ReturnValue ObjectDataSource'un son düzey olay işleyicilerinde incelenebilir. yönteminden DeleteProduct dönüş değerini belirlemekle ilgilendiğimiz için ObjectDataSource'un Deleted olayı için bir olay işleyicisi oluşturmamız gerekir. ReturnValue özelliği türündedir object ve bir özel durum oluşturulduysa ve yöntem bir değer döndürmeden önce kesildiyse olabilirnull. Bu nedenle, önce özelliğin ReturnValue bir Boole değeri olmadığından null ve olmadığından emin olmalıyız. Bu denetimin başarılı olduğunu varsayarsak, etiketi ReturnValuefalseise Etiket denetimini gösteririzDeleteConflictMessage. 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 durumunda kullanıcının silme isteği iptal edilir. GridView yenilenir ve bu kayıt için kullanıcının sayfayı yüklediği zaman ile Sil düğmesine tıklaması arasında gerçekleşen 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).

Eşzamanlılık İhlali Karşısında Kullanıcı Silme İptal Edildi

Şekil 20: Eşzamanlılık İhlaliNin Karşısında Kullanıcı Silme İ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 olanak tanıyan her uygulamada mevcuttur. Bu tür ihlaller dikkate alınmazsa, iki kullanıcı aynı anda aynı verileri güncelleştirdiğinde, son yazmada "kazanır" yazan diğer kullanıcının değişikliklerinin üzerine yazılır. 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 bir güncelleştirme veya silme komutuna izin vermemektedir. Kötümser eşzamanlılık denetimi, eşzamanlılık ihlallerinin sık 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 bir kaydın güncelleştirilmesi, kaydın kilitlenmesini ve böylece diğer kullanıcıların kilitliyken kaydı değiştirmesini veya silmesini önler.

.NET'te Yazılan Veri Kümesi, 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. Buna ek olarak, BLL'ye çağrıda bulunan ASP.NET sayfası, ObjectDataSource'un özgün değerleri veri Web denetiminden 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. Verileri güncelleştiren eş zamanlı kullanıcılarınız nadiren varsa veya güncelleştirdikleri veriler birbirinden farklıysa, eşzamanlılık denetimi önemli bir sorun değildir. Ancak, sitenizde aynı verilerle düzenli olarak birden çok kullanıcı çalışıyorsa 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ışmaktadır. Son kitabı Sams Teach Yourself ASP.NET 24 Hours 2.0'dır. Adresine adresinden veya adresinden ulaşabileceğiniz http://ScottOnWriting.NETblogu aracılığıyla ulaşabilirsinizmitchell@4GuysFromRolla.com.