İyimser Eşzamanlılık Uygulama (C#)
tarafından Scott Mitchell
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.
Ş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.
Ş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 NULL
gerekir).
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ı NorthwindOptimisticConcurrency
yeni 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ı NorthwindOptimisticConcurrency
yeni 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.config
kullanarak NORTHWNDConnectionString
aynı Northwind veritabanına bağlanmamız istenir.
Ş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.
Ş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
Ş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.
Ş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 ProductsOptimisticConcurrency
sağ 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.
Ş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 UPDATE
ProductsOptimisticConcurrency
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. , ProductName
ve Discontinued
dışındaki tüm alanlar değerlere sahip NULL
olabileceğinden ProductID
yan 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.
Ş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 = @ProductID
içerecek şekilde genişletin.
Ş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 GetProductByProductID
olarak FillByProductID
değiştirin.
Ş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
, ReorderLevel
ve 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ı ProductsOptimisticConcurrencyBLL
BLL
klasörün içindeki App_Code
klasöre ekleyin.
Ş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 ProductsOptimisticConcurrencyBLL
bir örneğine ProductsOptimisticConcurrencyTableAdapter
hızlı erişim sağlar ve özgün BLL sınıflarımızda (ProductsBLL
, CategoriesBLL
vb.) 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 DeleteProduct
ProductsOptimisticConcurrencyBLL
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:
- TableAdapter'ın
GetProductByProductID(productID)
yöntemini kullanarak geçerli veritabanı ürün bilgilerini birProductRow
örneğe okuma - 1. Adım'dan örneğe
ProductRow
yeni değerler atama - TableAdapter'ın
Update
yöntemini çağırın ve örneğiniProductRow
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:
- TableAdapter'ın
GetProductByProductID(productID)
yöntemini kullanarak geçerli veritabanı ürün bilgilerini birProductsOptimisticConcurrencyRow
örneğe okuma - 1. Adım'dan örneğe özgün değerleri
ProductsOptimisticConcurrencyRow
atama ProductsOptimisticConcurrencyRow
DataRow'aAcceptChanges()
geçerli değerlerinin "özgün" değerler olduğunu belirten örneğin yöntemini çağırın- Yeni değerleri örneğe
ProductsOptimisticConcurrencyRow
atama - TableAdapter'ın
Update
yöntemini çağırın ve örneğiniProductsOptimisticConcurrencyRow
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 ProductsGrid
ayarlayınID
. GridView'un akıllı etiketinden adlı ProductsOptimisticConcurrencyDataSource
yeni 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.
Ş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 GetProducts
açılan listelerden , 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 DeleteParameters
DeleteProduct
yöntemindeki on giriş parametresinin ProductsOptimisticConcurrencyBLL
her biri için bir Parameter
örnek içerir. Benzer şekilde, UpdateParameters
koleksiyonu içindeki UpdateProduct
giriş 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_productName
adlar varsa, özelliğini old_{0}
olarak güncelleştirmeniz OldValuesParameterFormatString
old_supplierID
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öndermezCompareAllValues
- özgün değerleri BLL yöntemlerine gönderir; İyimser eşzamanlılık kullanırken bu seçeneği belirleyin
özelliğini CompareAllValues
olarak 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 true
ayarlanmış 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
, veCategoryName
BoundFields kaldırıldıProductName
BoundField bir TemplateField'e dönüştürüldü ve RequiredFieldValidation denetimi eklendi.CategoryID
veSupplierID
BoundField'leri TemplateFields'e dönüştürdü ve düzenleme arabirimini TextBoxes yerine DropDownLists kullanacak şekilde ayarladı. Bu TemplateField'lerdeItemTemplates
CategoryName
veSupplierName
veri alanları görüntülenir.UnitPrice
, ,UnitsInStock
,UnitsOnOrder
veReorderLevel
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.
Ş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.
Ş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 ' decimal
ye 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
ItemTemplate
iç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çindeItemTemplate
bir 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ğiniEval
hatırlayın. Özgün değerlerinUnitPrice
değerini sağlamamız gerekir, bu nedenle içinde iki yönlü bir veri bağlama deyimineItemTemplate
ihtiyacımız vardır, ancak bu özellik olarak ayarlanmış bir Etiket Web denetimineVisible
false
yerleş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çimlendirmesiniItemTemplate
'den kaldırın. GridView'unRowDataBound
olay işleyicisinde, değerin görüntülendiği Label Web denetimineUnitPrice
program aracılığıyla erişin ve özelliğini biçimlendirilmiş sürüme ayarlayınText
. UnitPrice
Para birimi olarak biçimlendirilmiş olarak bırakın. GridView'unRowDeleting
olay işleyicisinde, kullanarakDecimal.Parse
mevcut özgünUnitPrice
değeri ($19,95) gerçek ondalık değerle değiştirin. Bir ASP.NET Sayfası öğreticisindeRowUpdating
BLL- 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.
Ş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.00
gü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.
Ş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 ProductsOptimisticConcurrencyBLL
DeleteProduct
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 false
ayarlayın; bu, özelliklerinin program aracılığıyla olarak ayarlandığı true
belirli 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 Text
EnabledViewState
özelliklerini ayarlamaya Visible
ek olarak, özelliğini Warning
olarak 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.
Ş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 true
ayarlanabilir.
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.
Ş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 ReturnValue
false
ise 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).
Ş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.
Geri Bildirim
https://aka.ms/ContentUserFeedback.
Çok yakında: 2024 boyunca, içerik için geri bildirim mekanizması olarak GitHub Sorunları’nı kullanımdan kaldıracak ve yeni bir geri bildirim sistemiyle değiştireceğiz. Daha fazla bilgi için bkz.Gönderin ve geri bildirimi görüntüleyin