İyimser Eşzamanlılık Uygulama (VB)Implementing Optimistic Concurrency (VB)

Scott Mitchell tarafındanby Scott Mitchell

Örnek uygulamayı indirin veya PDF 'yi indirinDownload Sample App or Download PDF

Birden çok kullanıcının verileri düzenlemesine izin veren bir Web uygulaması için, aynı anda iki kullanıcının aynı verileri düzenleyebilmesi riski vardır.For a web application that allows multiple users to edit data, there is the risk that two users may be editing the same data at the same time. Bu öğreticide, bu riski işlemek için iyimser eşzamanlılık denetimi uygulayacağız.In this tutorial we'll implement optimistic concurrency control to handle this risk.

GirişIntroduction

Yalnızca kullanıcıların verileri görüntülemesine izin veren Web uygulamaları veya yalnızca verileri değiştirebilen tek bir kullanıcı içeren kişiler için, iki eşzamanlı kullanıcının başka bir değişikliğin yanlışlıkla üzerine yazılmasına yönelik bir tehdit yoktur.For web applications that only allow users to view data, or for those that include only a single user who can modify data, there's no threat of two concurrent users accidentally overwriting one another's changes. Birden çok kullanıcının verileri güncelleştirmesine veya silmesine izin veren Web uygulamaları için, bir kullanıcının başka bir eşzamanlı kullanıcı ile çakışabilecek değişiklikler söz konusu olabilir.For web applications that allow multiple users to update or delete data, however, there's the potential for one user's modifications to clash with another concurrent user's. Herhangi bir eşzamanlılık ilkesi olmadan, iki kullanıcı aynı anda tek bir kaydı düzenliyorsa, değişiklikleri en son kaydeden Kullanıcı ilk tarafından yapılan değişiklikleri geçersiz kılar.Without any concurrency policy in place, when two users are simultaneously editing a single record, the user who commits her changes last will override the changes made by the first.

Örneğin, Jisun ve Sam olmak üzere iki kullanıcının her ikisi de uygulamamız tarafından bir GridView denetimi aracılığıyla ürünleri güncelleştirmesine ve silmesine izin veren bir sayfayı ziyaret ettiğini düşünün.For example, imagine that two users, Jisun and Sam, were both visiting a page in our application that allowed visitors to update and delete the products through a GridView control. Her ikisi de aynı anda GridView 'daki Düzenle düğmesine tıklayın.Both click the Edit button in the GridView around the same time. Jisun ürün adını "Chai Tea" olarak değiştirir ve Güncelleştir düğmesine tıklar.Jisun changes the product name to "Chai Tea" and clicks the Update button. Net sonuç, Tüm ürünlerin güncelleştirilebilir alanlarını ayarlayan veritabanına gönderilen bir UPDATE deyimidir (jıse yalnızca bir alan, ProductName).The net result is an UPDATE statement that is sent to the database, which sets all of the product's updateable fields (even though Jisun only updated one field, ProductName). Bu noktada, veritabanında "Chai Tea," Kategori Içecek, tedarikçinin Exotic Litids ve bu ürüne yönelik değerler vardır.At this point in time, the database has the values "Chai Tea," the category Beverages, the supplier Exotic Liquids, and so on for this particular product. Ancak, Sam ekranındaki GridView, düzenlenebilir GridView satırındaki ürün adını "Chai" olarak gösterir.However, the GridView on Sam's screen still shows the product name in the editable GridView row as "Chai". Jisun değişikliklerinin işlendiği birkaç saniye sonra, Sam kategoriyi koşullu olarak güncelleştirir ve Güncelleştir ' i tıklatır.A few seconds after Jisun's changes have been committed, Sam updates the category to Condiments and clicks Update. Bu, ürün adını "Chai" olarak ayarlayan veritabanına gönderilen UPDATE bildirimine ve buna karşılık gelen Içecek kategori KIMLIĞINE CategoryID neden olur.This results in an UPDATE statement sent to the database that sets the product name to "Chai," the CategoryID to the corresponding Beverages category ID, and so on. Cisun 'ın ürün adı değişikliklerinin üzerine yazıldı.Jisun's changes to the product name have been overwritten. Şekil 1 Bu olay serisini grafik olarak gösterir.Figure 1 graphically depicts this series of events.

Iki kullanıcı aynı anda bir kaydı güncelleştirirken, bir kullanıcının diğerinin üzerine yazılmasına neden olan değişiklikler When Two Users Simultaneously Update a Record There s Potential for One User 's Changes to Overwrite the Other 's

Şekil 1: Iki kullanıcı aynı anda bir kaydı güncelleştirirken, bir kullanıcının diğerinin üzerine yazılmasına neden olan değişiklikler (tam boyutlu görüntüyü görüntülemek için tıklatın)Figure 1: When Two Users Simultaneously Update a Record There s Potential for One User 's Changes to Overwrite the Other 's (Click to view full-size image)

Benzer şekilde, iki Kullanıcı bir sayfayı ziyaret ederken, bir Kullanıcı başka bir kullanıcı tarafından silindiğinde bir kaydı güncelleştirme sırasında olabilir.Similarly, when two users are visiting a page, one user might be in the midst of updating a record when it is deleted by another user. Ya da bir Kullanıcı bir sayfayı yüklediğinde ve Sil düğmesine tıkladıklarında, başka bir kullanıcı o kaydın içeriğini değiştirmiş olabilir.Or, between when a user loads a page and when they click the Delete button, another user may have modified the contents of that record.

Kullanılabilen üç eşzamanlılık denetim stratejisi vardır:There are three concurrency control strategies available:

  • Hiçbir şey yapma -eşzamanlı kullanıcılar aynı kaydı değiştiriyorsa, son yürütmeye izin ver (varsayılan davranış)Do Nothing -if concurrent users are modifying the same record, let the last commit win (the default behavior)
  • Iyimser eşzamanlılık -Şu anda eşzamanlılık çakışmaları olabileceğinden, bu tür çakışmaların büyük çoğunluğunun ortaya çıkması gerektiğini varsayın; Bu nedenle, bir çakışma ortaya çıkarsa kullanıcıyı, farklı bir kullanıcı aynı verileri değiştirdiği için yaptıkları değişikliklerin kaydedilamayacağını bildirmeniz yeterlidir.Optimistic Concurrency - assume that while there may be concurrency conflicts every now and then, the vast majority of the time such conflicts won't arise; therefore, if a conflict does arise, simply inform the user that their changes can't be saved because another user has modified the same data
  • Kötümser eşzamanlılık -eşzamanlılık çakışmalarının daha fazla olduğunu ve kullanıcıların, başka bir kullanıcının eşzamanlı etkinliği nedeniyle değişiklikleri kaydedilmediğini söylemeyeceği varsayıyoruz. Bu nedenle, bir Kullanıcı bir kaydı güncelleştirmeye başladığında, Kullanıcı değişikliklerini işleene kadar diğer kullanıcıların bu kaydı düzenlemesini veya silmesini önlerPessimistic Concurrency - assume that concurrency conflicts are commonplace and that users won't tolerate being told their changes weren't saved due to another user's concurrent activity; therefore, when one user starts updating a record, lock it, thereby preventing any other users from editing or deleting that record until the user commits their modifications

Bu nedenle, tüm öğreticilerimizin varsayılan eşzamanlılık çözümleme stratejisini kullandık. Yani, son yazma kazanalım.All of our tutorials thus far have used the default concurrency resolution strategy - namely, we've let the last write win. Bu öğreticide, iyimser eşzamanlılık denetimini nasıl uygulayacağınızı inceleyeceğiz.In this tutorial we'll examine how to implement optimistic concurrency control.

Note

Bu öğretici serisinde Kötümser eşzamanlılık örneklerine bakmayacağız.We won't look at pessimistic concurrency examples in this tutorial series. Kötümser eşzamanlılık nadiren kullanılır, çünkü düzgün bir şekilde yeniden bağlanmazlarsa, diğer kullanıcıların veri güncelleştirmesini engelleyebilir.Pessimistic concurrency is rarely used because such locks, if not properly relinquished, can prevent other users from updating data. Örneğin, bir Kullanıcı bir kaydı düzenlenmek üzere kilitlerse ve sonra kilidi açmadan önce bırakırsa, bu kayıt, özgün kullanıcı güncelleştirmeyi döndürünceye ve tamamlanana kadar bu kaydı güncelleştiremeyecektir.For example, if a user locks a record for editing and then leaves for the day before unlocking it, no other user will be able to update that record until the original user returns and completes his update. Bu nedenle, Kötümser eşzamanlılık kullanıldığı durumlarda genellikle, ulaşılırsa kilidi iptal eden bir zaman aşımı vardır.Therefore, in situations where pessimistic concurrency is used, there's typically a timeout that, if reached, cancels the lock. Kullanıcı sipariş işlemini tamamlarken, belirli bir zaman dilimini kısa süreli olarak kilitleyen bilet satış Web siteleri, Kötümser eşzamanlılık denetimine bir örnektir.Ticket sales websites, which lock a particular seating location for short period while the user completes the order process, is an example of pessimistic concurrency control.

1. Adım: Iyimser eşzamanlılık 'ın nasıl uygulandığını belirlemeStep 1: Looking at How Optimistic Concurrency is Implemented

İyimser eşzamanlılık denetimi, güncelleştirilmesi veya silinmekte olan kaydın güncelleştirme ya da silme işlemi başladığında yaptığı gibi aynı değerlere sahip olduğundan emin olarak çalışarak işe yarar.Optimistic concurrency control works by ensuring that the record being updated or deleted has the same values as it did when the updating or deleting process started. Örneğin, düzenlenebilir bir GridView 'da Düzenle düğmesine tıkladığınızda, kaydın değerleri veritabanından okunmalıdır ve metin kutuları ve diğer Web denetimlerinde görüntülenir.For example, when clicking the Edit button in an editable GridView, the record's values are read from the database and displayed in TextBoxes and other Web controls. Bu orijinal değerler GridView tarafından kaydedilir.These original values are saved by the GridView. Daha sonra Kullanıcı, değişiklikleri yaptıktan ve Güncelleştir düğmesine tıkladıktan sonra, özgün değerler ve yeni değerler Iş mantığı katmanına gönderilir ve sonra veri erişim katmanına kapanır.Later, after the user makes her changes and clicks the Update button, the original values plus the new values are sent to the Business Logic Layer, and then down to the Data Access Layer. Veri erişim katmanı, yalnızca kullanıcının düzenlemesini başlattığı orijinal değerler veritabanında hala aynı değerlerle aynıysa kaydı güncelleştirecek bir SQL ifadesini vermelidir.The Data Access Layer must issue a SQL statement that will only update the record if the original values that the user started editing are identical to the values still in the database. Şekil 2 ' de bu olay dizisi gösterilmektedir.Figure 2 depicts this sequence of events.

Güncelleştirme veya silme Işleminin başarılı olması için , özgün değerler geçerli veritabanı değerlerine eşit olmalıdırFor the Update or Delete to Succeed, the Original Values Must Be Equal to the Current Database Values

Şekil 2: güncelleştirme veya silme işleminin başarılı olması Için, özgün değerler geçerli veritabanı değerlerine eşit olmalıdır (tam boyutlu görüntüyü görüntülemek için tıklatın)Figure 2: For the Update or Delete to Succeed, the Original Values Must Be Equal to the Current Database Values (Click to view full-size image)

İ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'ın Iyimser eşzamanlılık güncelleştirme mantığı ).There are various approaches to implementing optimistic concurrency (see Peter A. Bromberg's Optimistic Concurrency Updating Logic for a brief look at a number of options). ADO.NET türü belirtilmiş veri kümesi, yalnızca bir onay kutusu ile yapılandırılabilecek bir uygulama sağlar.The ADO.NET Typed DataSet provides one implementation that can be configured with just the tick of a checkbox. Türü belirtilmiş veri kümesindeki bir TableAdapter için iyimser eşzamanlılık sağlamak, TableAdapter 'ın UPDATE ve DELETE deyimlerini, WHERE yan tümcesindeki tüm özgün değerleri bir karşılaştırmayı içerecek şekilde genişlettiğini sağlar.Enabling optimistic concurrency for a TableAdapter in the Typed DataSet augments the TableAdapter's UPDATE and DELETE statements to include a comparison of all of the original values in the WHERE clause. Örneğin, aşağıdaki UPDATE deyimleriyle, bir ürünün adını ve fiyatını yalnızca geçerli veritabanı değerleri GridView 'daki kayıt güncelleştirilirken başlangıçta alınan değerlere eşitse günceller.The following UPDATE statement, for example, updates the name and price of a product only if the current database values are equal to the values that were originally retrieved when updating the record in the GridView. @ProductName ve @UnitPrice parametreleri Kullanıcı tarafından girilen yeni değerleri içerir, ancak Düzenle düğmesine tıklandığında @original_ProductName ve @original_UnitPrice ilk olarak GridView 'a yüklenmiş olan değerleri içerir:The @ProductName and @UnitPrice parameters contain the new values entered by the user, whereas @original_ProductName and @original_UnitPrice contain the values that were originally loaded into the GridView when the Edit button was clicked:

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

Note

Bu UPDATE deyimleri okunabilirlik için basitleştirildi.This UPDATE statement has been simplified for readability. Uygulamada, UnitPrice NULL s 'yi UnitPrice ve NULL = NULL her zaman yanlış döndürüp döndürdüğünü kontrol WHERE (Bunun yerine IS NULLkullanmanız gerekir) yan tümcesindeki denetimi daha fazla yer alabilir.In practice, the UnitPrice check in the WHERE clause would be more involved since UnitPrice can contain NULL s and checking if NULL = NULL always returns False (instead you must use IS NULL).

Farklı bir temel UPDATE deyimin kullanılmasına ek olarak, bir TableAdapter 'ı iyimser eşzamanlılık kullanacak şekilde yapılandırmak, VERITABANı doğrudan yöntemlerinin imzasını da değiştirir.In addition to using a different underlying UPDATE statement, configuring a TableAdapter to use optimistic concurrency also modifies the signature of its DB direct methods. İlk öğreticimize geri çekin, bir veri erişim katmanı oluşturun, bu veritabanı doğrudan yöntemleri, bir skalar değer listesini giriş parametresi olarak kabul eden (kesin türü belirtilmiş bir DataRow veya DataTable örneği yerine).Recall from our first tutorial, Creating a Data Access Layer, that DB direct methods were those that accepts a list of scalar values as input parameters (rather than a strongly-typed DataRow or DataTable instance). İyimser eşzamanlılık kullanılırken, VERITABANı doğrudan Update() ve Delete() yöntemleri özgün değerler için giriş parametrelerini de içerir.When using optimistic concurrency, the DB direct Update() and Delete() methods include input parameters for the original values as well. Ayrıca, Batch güncelleştirme modelini (skaler değerler yerine DataRow ve DataTable kabul eden Update() yöntemi aşırı yüklemeleri) kullanmak için BLL içindeki kod de değiştirilmelidir.Moreover, the code in the BLL for using the batch update pattern (the Update() method overloads that accept DataRows and DataTables rather than scalar values) must be changed as well.

Mevcut DAL TableAdapters, iyimser eşzamanlılık kullanacak şekilde genişletmekten (BLL 'yi uyum sağlayacak şekilde değiştirmek için), bunun yerine iyimser eşzamanlılık kullanan bir Products TableAdapter ekleyeceğiz NorthwindOptimisticConcurrencyadlı yeni bir türü belirtilmiş veri kümesi oluşturalım.Rather than extend our existing DAL's TableAdapters to use optimistic concurrency (which would necessitate changing the BLL to accommodate), let's instead create a new Typed DataSet named NorthwindOptimisticConcurrency, to which we'll add a Products TableAdapter that uses optimistic concurrency. Bunun ardından, iyimser eşzamanlılık DALı desteklemek için uygun değişikliklere sahip ProductsOptimisticConcurrencyBLL bir Iş mantığı katman sınıfı oluşturacağız.Following that, we'll create a ProductsOptimisticConcurrencyBLL Business Logic Layer class that has the appropriate modifications to support the optimistic concurrency DAL. Bu ön hazırlıkları başlattık eklendikten sonra, ASP.NET sayfasını oluşturmak için hazırız.Once this groundwork has been laid, we'll be ready to create the ASP.NET page.

2. Adım: Iyimser eşzamanlılık destekleyen bir veri erişim katmanı oluşturmaStep 2: Creating a Data Access Layer That Supports Optimistic Concurrency

Yeni bir türü belirtilmiş veri kümesi oluşturmak için, App_Code klasörünün içindeki DAL klasörüne sağ tıklayın ve NorthwindOptimisticConcurrencyadlı yeni bir veri kümesi ekleyin.To create a new Typed DataSet, right-click on the DAL folder within the App_Code folder and add a new DataSet named NorthwindOptimisticConcurrency. İlk öğreticide gördüğünüz gibi, bunu yapmak, türü belirtilmiş veri kümesine yeni bir TableAdapter ekleyerek TableAdapter Yapılandırma Sihirbazı ' nı otomatik olarak başlatıyor.As we saw in the first tutorial, doing so will add a new TableAdapter to the Typed DataSet, automatically launching the TableAdapter Configuration Wizard. İlk ekranda, Web.config``NORTHWNDConnectionString ayarını kullanarak, aynı Northwind veritabanına bağlanacak şekilde bağlanılacak veritabanını belirtmemiz istenir.In the first screen, we're prompted to specify the database to connect to - connect to the same Northwind database using the NORTHWNDConnectionString setting from Web.config.

Aynı Northwind veritabanına bağlanmak Connect to the Same Northwind Database

Şekil 3: aynı Northwind veritabanına bağlanın (tam boyutlu görüntüyü görüntülemek için tıklayın)Figure 3: Connect to the Same Northwind Database (Click to view full-size image)

Daha sonra, verilerin nasıl sorgulanabileceğini, örneğin geçici bir SQL ifadesini, yeni bir saklı yordamı veya mevcut bir saklı yordamı kullanarak yapmanız istenir.Next, we are prompted as to how to query the data: through an ad-hoc SQL statement, a new stored procedure, or an existing stored procedure. Özgün DAL 'imizde geçici SQL sorguları kullandığımızdan, burada da bu seçeneği kullanın.Since we used ad-hoc SQL queries in our original DAL, use this option here as well.

geçici bir SQL Ifadesini kullanarak alınacak verileri belirtinSpecify the Data to Retrieve Using an Ad-Hoc SQL Statement

Şekil 4: GEÇICI bir SQL Ifadesini kullanarak alınacak verileri belirtin (tam boyutlu görüntüyü görüntülemek için tıklayın)Figure 4: Specify the Data to Retrieve Using an Ad-Hoc SQL Statement (Click to view full-size image)

Aşağıdaki ekranda, ürün bilgilerini almak için kullanılacak SQL sorgusunu girin.On the following screen, enter the SQL query to use to retrieve the product information. Özgün Dalımızda Products TableAdapter için kullanılan aynı SQL sorgusunu kullanalım ve bu, ürünün tedarikçisiyle ve kategori adlarıyla birlikte tüm Product sütunlarını döndüren bir.Let's use the exact same SQL query used for the Products TableAdapter from our original DAL, which returns all of the Product columns along with the product's supplier and category names:

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 'daki ürünler TableAdapter ' dan aynı SQL sorgusunu kullanınUse the Same SQL Query from the Products TableAdapter in the Original DAL

Şekil 5: özgün DAL Içinde Products TableAdapter 'TAN aynı SQL sorgusunu kullanın (tam boyutlu görüntüyü görüntülemek için tıklayın)Figure 5: Use the Same SQL Query from the Products TableAdapter in the Original DAL (Click to view full-size image)

Sonraki ekrana geçmeden önce Gelişmiş Seçenekler düğmesine tıklayın.Before moving onto the next screen, click the Advanced Options button. Bu TableAdapter 'ın iyimser eşzamanlılık denetimi kullanmasını sağlamak için, "iyimser eşzamanlılık kullan" onay kutusunu işaretlemeniz yeterlidir.To have this TableAdapter employ optimistic concurrency control, simply check the "Use optimistic concurrency" checkbox.

iyimser eşzamanlılık denetimini etkinleştirmek "iyimser eşzamanlılık" onay kutusunu kullanınEnable Optimistic Concurrency Control by Checking the "Use optimistic concurrency" CheckBox

Şekil 6: "Iyimser eşzamanlılık kullan" onay kutusunu Işaretleyerek Iyimser eşzamanlılık denetimini etkinleştirin (tam boyutlu görüntüyü görüntülemek için tıklayın)Figure 6: Enable Optimistic Concurrency Control by Checking the "Use optimistic concurrency" CheckBox (Click to view full-size image)

Son olarak, TableAdapter 'ın her ikisi de bir DataTable doldurdukları ve DataTable döndüren veri erişim düzenlerini kullanması gerektiğini belirtir; Ayrıca, VERITABANı doğrudan yöntemlerinin oluşturulması gerektiğini de belirtir.Lastly, indicate that the TableAdapter should use the data access patterns that both fill a DataTable and return a DataTable; also indicate that the DB direct methods should be created. İlk düzenimizde kullandığımız adlandırma kurallarını yansıtmak için, GetData ' dan GetProducts öğesine bir DataTable deseninin döndürdüğü yöntem adını değiştirin.Change the method name for the Return a DataTable pattern from GetData to GetProducts, so as to mirror the naming conventions we used in our original DAL.

TableAdapter 'ın tüm veri erişim desenlerini kullanmasına Have the TableAdapter Utilize All Data Access Patterns

Şekil 7: TableAdapter 'ın tüm veri erişim desenlerini kullanmasını sağlamak (tam boyutlu görüntüyü görüntülemek için tıklayın)Figure 7: Have the TableAdapter Utilize All Data Access Patterns (Click to view full-size image)

Sihirbazı tamamladıktan sonra, veri kümesi Tasarımcısı kesin türü belirtilmiş bir DataTable ve TableAdapter Products içerecektir.After completing the wizard, the DataSet Designer will include a strongly-typed Products DataTable and TableAdapter. DataTable 'ın başlık çubuğuna sağ tıklayıp bağlam menüsünden Yeniden Adlandır ' ı seçerek, DataTable 'ın Products ProductsOptimisticConcurrencyolarak yeniden adlandırılması için bir dakikanızı ayırın.Take a moment to rename the DataTable from Products to ProductsOptimisticConcurrency, which you can do by right-clicking on the DataTable's title bar and choosing Rename from the context menu.

Türü belirtilmiş veri kümesine bir DataTable ve TableAdapter eklendiA DataTable and TableAdapter Have Been Added to the Typed DataSet

Şekil 8: yazılan veri kümesine bir DataTable ve TableAdapter eklendi (tam boyutlu görüntüyü görüntülemek için tıklayın)Figure 8: A DataTable and TableAdapter Have Been Added to the Typed DataSet (Click to view full-size image)

ProductsOptimisticConcurrency TableAdapter (iyimser eşzamanlılık kullanan) ve Products TableAdapter (bu değil) arasındaki UPDATE ve DELETE sorguları arasındaki farkları görmek için TableAdapter 'a tıklayın ve Özellikler penceresi gidin.To see the differences between the UPDATE and DELETE queries between the ProductsOptimisticConcurrency TableAdapter (which uses optimistic concurrency) and the Products TableAdapter (which doesn't), click on the TableAdapter and go to the Properties window. DeleteCommand ve UpdateCommand özellikleri ' CommandText alt özellikleri ' nde, DAL güncelleştirmesi veya silme ile ilgili Yöntemler çağrıldığında veritabanına gönderilen gerçek SQL sözdizimini görebilirsiniz.In the DeleteCommand and UpdateCommand properties' CommandText subproperties you can see the actual SQL syntax that is sent to the database when the DAL's update or delete-related methods are invoked. ProductsOptimisticConcurrency TableAdapter için kullanılan DELETE deyimidir:For the ProductsOptimisticConcurrency TableAdapter the DELETE statement used is:

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))

Yani, özgün DAL olan ürün TableAdapter 'ı için DELETE bildiri çok basittir:Whereas the DELETE statement for the Product TableAdapter in our original DAL is the much simpler:

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

Gördüğünüz gibi, iyimser eşzamanlılık kullanan TableAdapter için DELETE deyimindeki WHERE yan tümcesi, her bir Product tablosunun mevcut sütun değerleri ile GridView (veya DetailsView ya da FormView) son doldurulduğu zaman arasında bir karşılaştırma içerir.As you can see, the WHERE clause in the DELETE statement for the TableAdapter that uses optimistic concurrency includes a comparison between each of the Product table's existing column values and the original values at the time the GridView (or DetailsView or FormView) was last populated. ProductID, ProductNameve Discontinued dışındaki tüm alanlar NULL değerlere sahip olduğundan, NULL yan tümcesindeki WHERE değerlerini doğru bir şekilde karşılaştırmak için ek parametreler ve denetimler dahil edilir.Since all fields other than ProductID, ProductName, and Discontinued can have NULL values, additional parameters and checks are included to correctly compare NULL values in the WHERE clause.

ASP.NET sayfamız yalnızca ürün bilgilerini güncelleştirme ve silme sağlayacak şekilde, bu öğretici için iyimser eşzamanlılık özellikli veri kümesine ek bir DataTable ekliyoruz.We won't be adding any additional DataTables to the optimistic concurrency-enabled DataSet for this tutorial, as our ASP.NET page will only provide updating and deleting product information. Ancak, yine de ProductsOptimisticConcurrency TableAdapter 'a GetProductByProductID(productID) yöntemini eklememiz gerekir.However, we do still need to add the GetProductByProductID(productID) method to the ProductsOptimisticConcurrency TableAdapter.

Bunu gerçekleştirmek için, TableAdapter 'ın başlık çubuğuna sağ tıklayın (Fill ve GetProducts yöntem adlarının üzerindeki alan) ve bağlam menüsünden sorgu Ekle ' yi seçin.To accomplish this, right-click on the TableAdapter's title bar (the area right above the Fill and GetProducts method names) and choose Add Query from the context menu. Bu, TableAdapter sorgu Yapılandırma Sihirbazı 'nı başlatır.This will launch the TableAdapter Query Configuration Wizard. TableAdapter 'ın ilk yapılandırmasında olduğu gibi, geçici bir SQL ifadesini kullanarak GetProductByProductID(productID) yöntemi oluşturmayı tercih edin (bkz. Şekil 4).As with our TableAdapter's initial configuration, opt to create the GetProductByProductID(productID) method using an ad-hoc SQL statement (see Figure 4). GetProductByProductID(productID) yöntemi belirli bir ürünle ilgili bilgiler döndürdüğünden, bu sorgunun satırları döndüren SELECT bir sorgu türü olduğunu gösterir.Since the GetProductByProductID(productID) method returns information about a particular product, indicate that this query is a SELECT query type that returns rows.

sorgu türünü, satırları döndüren "bir SELECT olarak Işaretleyin"Mark the Query Type as a "SELECT which returns rows"

Şekil 9: sorgu türünü "satırları döndürenSELECT" olarak işaretleyin (tam boyutlu görüntüyü görüntülemek için tıklayın)Figure 9: Mark the Query Type as a "SELECT which returns rows" (Click to view full-size image)

Bir sonraki ekranda, TableAdapter 'ın varsayılan sorgusu önceden yüklenmiş olarak SQL sorgusunun kullanılması istenir.On the next screen we're prompted for the SQL query to use, with the TableAdapter's default query pre-loaded. Şekil 10 ' da gösterildiği gibi, yan tümce WHERE ProductID = @ProductIDdahil etmek için mevcut sorguyu açın.Augment the existing query to include the clause WHERE ProductID = @ProductID, as shown in Figure 10.

belirli bir ürün kaydını döndürmek için önceden yüklenmiş sorguya bir WHERE yan tümcesi ekleyinAdd a WHERE Clause to the Pre-Loaded Query to Return a Specific Product Record

Şekil 10: belirli bir ürün kaydını döndürmek Için önceden yüklenmiş sorguya bir WHERE yan tümcesi ekleyin (tam boyutlu görüntüyü görüntülemek için tıklayın)Figure 10: Add a WHERE Clause to the Pre-Loaded Query to Return a Specific Product Record (Click to view full-size image)

Son olarak, oluşturulan yöntem adlarını FillByProductID ve GetProductByProductIDolarak değiştirin.Finally, change the generated method names to FillByProductID and GetProductByProductID.

, bu yöntemleri Fillbyproductıd ve GetProductByProductID olarak yeniden adlandırınRename the Methods to FillByProductID and GetProductByProductID

Ş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)Figure 11: Rename the Methods to FillByProductID and GetProductByProductID (Click to view full-size image)

Bu sihirbaz tamamlandığına göre TableAdapter artık verileri almak için iki yöntem içerir: GetProducts(), Tüm ürünleri döndürür; ve, belirtilen ürünü döndüren GetProductByProductID(productID).With this wizard complete, the TableAdapter now contains two methods for retrieving data: GetProducts(), which returns all products; and GetProductByProductID(productID), which returns the specified product.

3. Adım: Iyimser eşzamanlılık özellikli DAL için Iş mantığı katmanı oluşturmaStep 3: Creating a Business Logic Layer for the Optimistic Concurrency-Enabled DAL

Mevcut ProductsBLL sınıfımızın hem Batch Update hem de DB Direct desenlerini kullanma örnekleri vardır.Our existing ProductsBLL class has examples of using both the batch update and DB direct patterns. AddProduct yöntemi ve UpdateProduct aşırı yüklemeleri her ikisi de Batch güncelleştirme modelini kullanarak, bir ProductRow örneğini TableAdapter Update yöntemine geçirerek kullanır.The AddProduct method and UpdateProduct overloads both use the batch update pattern, passing in a ProductRow instance to the TableAdapter's Update method. Diğer taraftan DeleteProduct yöntemi, TableAdapter 'ın Delete(productID) yöntemini çağırarak VERITABANı doğrudan modelini kullanır.The DeleteProduct method, on the other hand, uses the DB direct pattern, calling the TableAdapter's Delete(productID) method.

Yeni ProductsOptimisticConcurrency TableAdapter ile, VERITABANı doğrudan yöntemleri artık özgün değerlerin de geçirilmesini gerektirir.With the new ProductsOptimisticConcurrency TableAdapter, the DB direct methods now require that the original values also be passed in. Örneğin Delete yöntemi artık on giriş parametresi beklemektedir: özgün ProductID, ProductName, SupplierID, CategoryID, QuantityPerUnit, UnitPrice, UnitsInStock, UnitsOnOrder, ReorderLevelve Discontinued.For example, the Delete method now expects ten input parameters: the original ProductID, ProductName, SupplierID, CategoryID, QuantityPerUnit, UnitPrice, UnitsInStock, UnitsOnOrder, ReorderLevel, and Discontinued. Veritabanına gönderilen DELETE deyimin WHERE yan tümcesindeki bu ek giriş parametrelerinin değerlerini kullanır, yalnızca veritabanının geçerli değerleri özgün olanlara eşleniyorsa belirtilen kaydı silinir.It uses these additional input parameters' values in WHERE clause of the DELETE statement sent to the database, only deleting the specified record if the database's current values map up to the original ones.

TableAdapter 'ın Batch güncelleştirme düzeninde kullanılan Update yöntemi için yöntem imzası değişmediğinden, özgün ve yeni değerleri kaydetmek için gereken kod.While the method signature for the TableAdapter's Update method used in the batch update pattern hasn't changed, the code needed to record the original and new values has. Bu nedenle, mevcut ProductsBLL sınıfımızla iyimser eşzamanlılık özellikli DAL kullanımını denemek yerine yeni DAL ile çalışmaya yönelik yeni bir Iş mantığı katman sınıfı oluşturalım.Therefore, rather than attempt to use the optimistic concurrency-enabled DAL with our existing ProductsBLL class, let's create a new Business Logic Layer class for working with our new DAL.

App_Code klasörü içindeki BLL klasöre ProductsOptimisticConcurrencyBLL adlı bir sınıf ekleyin.Add a class named ProductsOptimisticConcurrencyBLL to the BLL folder within the App_Code folder.

ProductsOptimisticConcurrencyBLL sınıfını BLL klasörüne ekleme

Şekil 12: ProductsOptimisticConcurrencyBLL sınıfını BLL klasörüne eklemeFigure 12: Add the ProductsOptimisticConcurrencyBLL Class to the BLL Folder

Sonra, ProductsOptimisticConcurrencyBLL sınıfına aşağıdaki kodu ekleyin:Next, add the following code to the ProductsOptimisticConcurrencyBLL class:

Imports NorthwindOptimisticConcurrencyTableAdapters
<System.ComponentModel.DataObject()> _
Public Class ProductsOptimisticConcurrencyBLL
    Private _productsAdapter As ProductsOptimisticConcurrencyTableAdapter = Nothing
    Protected ReadOnly Property Adapter() As ProductsOptimisticConcurrencyTableAdapter
        Get
            If _productsAdapter Is Nothing Then
                _productsAdapter = New ProductsOptimisticConcurrencyTableAdapter()
            End If
            Return _productsAdapter
        End Get
    End Property
    <System.ComponentModel.DataObjectMethodAttribute _
    (System.ComponentModel.DataObjectMethodType.Select, True)> _
    Public Function GetProducts() As _
        NorthwindOptimisticConcurrency.ProductsOptimisticConcurrencyDataTable
        Return Adapter.GetProducts()
    End Function
End Class

Sınıf bildiriminin başlangıcı üzerindeki using NorthwindOptimisticConcurrencyTableAdapters ifadesini aklınızda edin.Note the using NorthwindOptimisticConcurrencyTableAdapters statement above the start of the class declaration. NorthwindOptimisticConcurrencyTableAdapters ad alanı, DAL yöntemlerini sağlayan ProductsOptimisticConcurrencyTableAdapter sınıfını içerir.The NorthwindOptimisticConcurrencyTableAdapters namespace contains the ProductsOptimisticConcurrencyTableAdapter class, which provides the DAL's methods. Ayrıca, Sınıf bildiriminden önce, Visual Studio 'Yu ObjectDataSource sihirbazının açılan listesine bu sınıfı dahil etmek için yönlendiren System.ComponentModel.DataObject özniteliğini bulacaksınız.Also before the class declaration you'll find the System.ComponentModel.DataObject attribute, which instructs Visual Studio to include this class in the ObjectDataSource wizard's drop-down list.

ProductsOptimisticConcurrencyBLL``Adapter özelliği, ProductsOptimisticConcurrencyTableAdapter sınıfının bir örneğine hızlı erişim sağlar ve orijinal BLL sınıflarımızda (ProductsBLL, CategoriesBLL, vb.) kullanılan kalıbı izler.The ProductsOptimisticConcurrencyBLL's Adapter property provides quick access to an instance of the ProductsOptimisticConcurrencyTableAdapter class, and follows the pattern used in our original BLL classes (ProductsBLL, CategoriesBLL, and so on). Son olarak, GetProducts() yöntemi yalnızca DAL GetProducts() yöntemine çağrı ve veritabanındaki her ürün kaydı için bir ProductsOptimisticConcurrencyRow örneğiyle doldurulmuş bir ProductsOptimisticConcurrencyDataTable nesnesi döndürüyor.Finally, the GetProducts() method simply calls down into the DAL's GetProducts() method and returns a ProductsOptimisticConcurrencyDataTable object populated with a ProductsOptimisticConcurrencyRow instance for each product record in the database.

Iyimser eşzamanlılık ile VERITABANı doğrudan modelini kullanarak bir ürünü silmeDeleting a Product Using the DB Direct Pattern with Optimistic Concurrency

İyimser eşzamanlılık kullanan bir DAL için VERITABANı doğrudan modelini kullanırken, yöntemlerin yeni ve özgün değerleri geçirilmesi gerekir.When using the DB direct pattern against a DAL that uses optimistic concurrency, the methods must be passed the new and original values. Silme için yeni değer yoktur, bu nedenle yalnızca özgün değerler geçirilmesi gerekir.For deleting, there are no new values, so only the original values need be passed in. BLL 'de, tüm özgün parametreleri giriş parametresi olarak kabul etmemiz gerekir.In our BLL, then, we must accept all of the original parameters as input parameters. ProductsOptimisticConcurrencyBLL sınıfında DeleteProduct yöntemine DB Direct metodunu kullanalım.Let's have the DeleteProduct method in the ProductsOptimisticConcurrencyBLL class use the DB direct method. Bu, bu yöntemin tüm on ürün verileri alanlarını giriş parametreleri olarak yapması ve bunları aşağıdaki kodda gösterildiği gibi DAL 'e iletmesinin gerektiği anlamına gelir:This means that this method needs to take in all ten product data fields as input parameters, and pass these to the DAL, as shown in the following code:

<System.ComponentModel.DataObjectMethodAttribute _
(System.ComponentModel.DataObjectMethodType.Delete, True)> _
Public Function DeleteProduct( _
    ByVal original_productID As Integer, ByVal original_productName As String, _
    ByVal original_supplierID As Nullable(Of Integer), _
    ByVal original_categoryID As Nullable(Of Integer), _
    ByVal original_quantityPerUnit As String, _
    ByVal original_unitPrice As Nullable(Of Decimal), _
    ByVal original_unitsInStock As Nullable(Of Short), _
    ByVal original_unitsOnOrder As Nullable(Of Short), _
    ByVal original_reorderLevel As Nullable(Of Short), _
    ByVal original_discontinued As Boolean) _
    As Boolean
    Dim rowsAffected As Integer = 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
End Function

Özgün değerler-GridView 'a (veya DetailsView ya da FormView) en son yüklenen değerler, Kullanıcı Sil düğmesine tıkladığında veritabanındaki değerlerden farklıysa WHERE yan tümcesi hiçbir veritabanı kaydıyla eşleşmez ve hiçbir kayıt etkilenmez.If the original values - those values that were last loaded into the GridView (or DetailsView or FormView) - differ from the values in the database when the user clicks the Delete button the WHERE clause won't match up with any database record and no records will be affected. Bu nedenle, TableAdapter 'ın Delete yöntemi 0 döndürecek ve BLL 'nin DeleteProduct yöntemi falsedöndürecek.Hence, the TableAdapter's Delete method will return 0 and the BLL's DeleteProduct method will return false.

Toplu güncelleştirme modelini Iyimser eşzamanlılık ile kullanarak bir ürünü güncelleştirmeUpdating a Product Using the Batch Update Pattern with Optimistic Concurrency

Daha önce belirtildiği gibi, toplu güncelleştirme deseninin Update yöntemi, iyimser eşzamanlılık kullanılıp kullanılmadığından bağımsız olarak aynı yöntem imzasına sahiptir.As noted earlier, the TableAdapter's Update method for the batch update pattern has the same method signature regardless of whether or not optimistic concurrency is employed. Yani Update yöntemi bir DataRow, bir DataRow dizisi, bir DataTable veya türü belirtilmiş veri kümesi bekler.Namely, the Update method expects a DataRow, an array of DataRows, a DataTable, or a Typed DataSet. Özgün değerleri belirtmek için ek giriş parametresi yoktur.There are no additional input parameters for specifying the original values. DataTable, DataRow 'lar için özgün ve değiştirilmiş değerleri takip ettiğinden, bu mümkündür.This is possible because the DataTable keeps track of the original and modified values for its DataRow(s). DAL UPDATE ifadesini yaparken, @original_ColumnName parametreleri DataRow 'ın özgün değerleriyle doldurulur, ancak @ColumnName parametreleri DataRow tarafından değiştirilen değerlerle doldurulur.When the DAL issues its UPDATE statement, the @original_ColumnName parameters are populated with the DataRow's original values, whereas the @ColumnName parameters are populated with the DataRow's modified values.

ProductsBLL sınıfında (orijinal, iyimser eşzamanlılık olmayan eşzamanlılık DAL kullanır) ürün bilgilerini güncelleştirmek için Batch güncelleştirme modelini kullanırken, kodumuz aşağıdaki olay sırasını gerçekleştirir:In the ProductsBLL class (which uses our original, non-optimistic concurrency DAL), when using the batch update pattern to update product information our code performs the following sequence of events:

  1. TableAdapter 'ın GetProductByProductID(productID) metodunu kullanarak geçerli veritabanı ürün bilgilerini bir ProductRow örneğine okuyunRead the current database product information into a ProductRow instance using the TableAdapter's GetProductByProductID(productID) method
  2. 1. adımdaki yeni değerleri ProductRow örneğine atayınAssign the new values to the ProductRow instance from Step 1
  3. TableAdapter 'ın Update yöntemini çağırın, ProductRow örneğini geçirerekCall the TableAdapter's Update method, passing in the ProductRow instance

Ancak, adım 1 ' de doldurulmuş ProductRow, doğrudan veritabanından doldurulduğundan, DataRow tarafından kullanılan özgün değerler veritabanında mevcut olan ve Düzen işleminin başlangıcında GridView 'a bağlanılanlar dışında, bu adım dizisi, iyimser eşzamanlılık 'yi doğru şekilde desteklemez.This sequence of steps, however, won't correctly support optimistic concurrency because the ProductRow populated in Step 1 is populated directly from the database, meaning that the original values used by the DataRow are those that currently exist in the database, and not those that were bound to the GridView at the start of the editing process. Bunun yerine, iyimser eşzamanlılık özellikli bir DAL kullanırken aşağıdaki adımları kullanmak için UpdateProduct yöntemi yüklerini değiştirmemiz gerekir:Instead, when using an optimistic concurrency-enabled DAL, we need to alter the UpdateProduct method overloads to use the following steps:

  1. TableAdapter 'ın GetProductByProductID(productID) metodunu kullanarak geçerli veritabanı ürün bilgilerini bir ProductsOptimisticConcurrencyRow örneğine okuyunRead the current database product information into a ProductsOptimisticConcurrencyRow instance using the TableAdapter's GetProductByProductID(productID) method
  2. Özgün değerleri 1. adımdaki ProductsOptimisticConcurrencyRow örneğine atayınAssign the original values to the ProductsOptimisticConcurrencyRow instance from Step 1
  3. ProductsOptimisticConcurrencyRow örneğinin AcceptChanges() yöntemini çağırın, bu, DataRow öğesine geçerli değerlerinin "orijinal" olduğunu bildirirCall the ProductsOptimisticConcurrencyRow instance's AcceptChanges() method, which instructs the DataRow that its current values are the "original" ones
  4. Yeni değerleri ProductsOptimisticConcurrencyRow örneğine ataAssign the new values to the ProductsOptimisticConcurrencyRow instance
  5. TableAdapter 'ın Update yöntemini çağırın, ProductsOptimisticConcurrencyRow örneğini geçirerekCall the TableAdapter's Update method, passing in the ProductsOptimisticConcurrencyRow instance

1. adım, belirtilen ürün kaydı için geçerli veritabanı değerlerinin tümünde okur.Step 1 reads in all of the current database values for the specified product record. Bu UpdateProduct adım, Tüm ürün sütunlarını (Bu değerlerin 2. adımdaki üzerine yazıldığı gibi) güncelleştiren, ancak sütun değerlerinin yalnızca bir alt kümesinin giriş parametresi olarak geçirildiği bu aşırı yüklemeler için gereklidir.This step is superfluous in the UpdateProduct overload that updates all of the product columns (as these values are overwritten in Step 2), but is essential for those overloads where only a subset of the column values are passed in as input parameters. Özgün değerler ProductsOptimisticConcurrencyRow örneğine atandıktan sonra, AcceptChanges() yöntemi çağrılır, bu, geçerli DataRow değerlerini UPDATE deyimindeki @original_ColumnName parametrelerinde kullanılacak orijinal değerler olarak işaretler.Once the original values have been assigned to the ProductsOptimisticConcurrencyRow instance, the AcceptChanges() method is called, which marks the current DataRow values as the original values to be used in the @original_ColumnName parameters in the UPDATE statement. Ardından, yeni parametre değerleri ProductsOptimisticConcurrencyRow atanır ve son olarak, Update yöntemi çağırılır, DataRow içinde geçer.Next, the new parameter values are assigned to the ProductsOptimisticConcurrencyRow and, finally, the Update method is invoked, passing in the DataRow.

Aşağıdaki kod, tüm ürün verileri alanlarını giriş parametresi olarak kabul eden UpdateProduct aşırı yüklemeyi gösterir.The following code shows the UpdateProduct overload that accepts all product data fields as input parameters. Burada gösterilmemiş olsa da, bu öğreticide bulunan ProductsOptimisticConcurrencyBLL sınıfı, yalnızca ürünün adını ve fiyatını giriş parametresi olarak kabul eden bir UpdateProduct aşırı yüklemesi içerir.While not shown here, the ProductsOptimisticConcurrencyBLL class included in the download for this tutorial also contains an UpdateProduct overload that accepts just the product's name and price as input parameters.

Protected Sub AssignAllProductValues( _
    ByVal product As NorthwindOptimisticConcurrency.ProductsOptimisticConcurrencyRow, _
    ByVal productName As String, ByVal supplierID As Nullable(Of Integer), _
    ByVal categoryID As Nullable(Of Integer), ByVal quantityPerUnit As String, _
    ByVal unitPrice As Nullable(Of Decimal), ByVal unitsInStock As Nullable(Of Short), _
    ByVal unitsOnOrder As Nullable(Of Short), ByVal reorderLevel As Nullable(Of Short), _
    ByVal discontinued As Boolean)
    product.ProductName = productName
    If Not supplierID.HasValue Then
        product.SetSupplierIDNull()
    Else
        product.SupplierID = supplierID.Value
    End If
    If Not categoryID.HasValue Then
        product.SetCategoryIDNull()
    Else
        product.CategoryID = categoryID.Value
    End If
    If quantityPerUnit Is Nothing Then
        product.SetQuantityPerUnitNull()
    Else
        product.QuantityPerUnit = quantityPerUnit
    End If
    If Not unitPrice.HasValue Then
        product.SetUnitPriceNull()
    Else
        product.UnitPrice = unitPrice.Value
    End If
    If Not unitsInStock.HasValue Then
        product.SetUnitsInStockNull()
    Else
        product.UnitsInStock = unitsInStock.Value
    End If
    If Not unitsOnOrder.HasValue Then
        product.SetUnitsOnOrderNull()
    Else
        product.UnitsOnOrder = unitsOnOrder.Value
    End If
    If Not reorderLevel.HasValue Then
        product.SetReorderLevelNull()
    Else
        product.ReorderLevel = reorderLevel.Value
    End If
    product.Discontinued = discontinued
End Sub
<System.ComponentModel.DataObjectMethodAttribute( _
System.ComponentModel.DataObjectMethodType.Update, True)> _
Public Function UpdateProduct(
    ByVal productName As String, ByVal supplierID As Nullable(Of Integer), _
    ByVal categoryID As Nullable(Of Integer), ByVal quantityPerUnit As String, _
    ByVal unitPrice As Nullable(Of Decimal), ByVal unitsInStock As Nullable(Of Short), _
    ByVal unitsOnOrder As Nullable(Of Short), ByVal reorderLevel As Nullable(Of Short), _
    ByVal discontinued As Boolean, ByVal productID As Integer, _
    _
    ByVal original_productName As String, _
    ByVal original_supplierID As Nullable(Of Integer), _
    ByVal original_categoryID As Nullable(Of Integer), _
    ByVal original_quantityPerUnit As String, _
    ByVal original_unitPrice As Nullable(Of Decimal), _
    ByVal original_unitsInStock As Nullable(Of Short), _
    ByVal original_unitsOnOrder As Nullable(Of Short), _
    ByVal original_reorderLevel As Nullable(Of Short), _
    ByVal original_discontinued As Boolean, _
    ByVal original_productID As Integer) _
    As Boolean
    'STEP 1: Read in the current database product information
    Dim products As _
        NorthwindOptimisticConcurrency.ProductsOptimisticConcurrencyDataTable = _
        Adapter.GetProductByProductID(original_productID)
    If products.Count = 0 Then
        ' no matching record found, return false
        Return False
    End If
    Dim product As _
        NorthwindOptimisticConcurrency.ProductsOptimisticConcurrencyRow = 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
    Dim rowsAffected As Integer = Adapter.Update(product)
    ' Return true if precisely one row was updated, otherwise false
    Return rowsAffected = 1
End Function

4. Adım: özgün ve yeni değerleri ASP.NET sayfasından BLL yöntemlerine geçirmeStep 4: Passing the Original and New Values From the ASP.NET Page to the BLL Methods

DAL ve BLL ile birlikte her şey, sistemde yerleşik olarak bulunan iyimser eşzamanlılık mantığını kullanan bir ASP.NET sayfası oluşturmaktır.With the DAL and BLL complete, all that remains is to create an ASP.NET page that can utilize the optimistic concurrency logic built in to the system. Özellikle, veri Web denetimi (GridView, DetailsView veya FormView) orijinal değerlerini hatırlamaları gerekir ve ObjectDataSource, her iki değer kümesini de Iş mantığı katmanına iletmelidir.Specifically, the data Web control (the GridView, DetailsView, or FormView) must remember its original values and the ObjectDataSource must pass both sets of values to the Business Logic Layer. Ayrıca, ASP.NET sayfası eşzamanlılık ihlallerini düzgün şekilde işleyecek şekilde yapılandırılmalıdır.Furthermore, the ASP.NET page must be configured to gracefully handle concurrency violations.

OptimisticConcurrency.aspx sayfasını EditInsertDelete klasöründen açıp tasarımcıya bir GridView ekleyerek ID özelliğini ProductsGridolarak ayarlayarak başlayın.Start by opening the OptimisticConcurrency.aspx page in the EditInsertDelete folder and adding a GridView to the Designer, setting its ID property to ProductsGrid. GridView 'un akıllı etiketinden ProductsOptimisticConcurrencyDataSourceadlı yeni bir ObjectDataSource oluşturmayı tercih edin.From the GridView's smart tag, opt to create a new ObjectDataSource named ProductsOptimisticConcurrencyDataSource. Bu ObjectDataSource 'un iyimser eşzamanlılık destekleyen DAL kullanmasını istiyoruz, ProductsOptimisticConcurrencyBLL nesnesini kullanacak şekilde yapılandırın.Since we want this ObjectDataSource to use the DAL that supports optimistic concurrency, configure it to use the ProductsOptimisticConcurrencyBLL object.

ObjectDataSource, ProductsOptimisticConcurrencyBLL nesnesini kullanırHave the ObjectDataSource Use the ProductsOptimisticConcurrencyBLL Object

Şekil 13: ObjectDataSource 'un ProductsOptimisticConcurrencyBLL nesnesini kullanmasını sağlamak (tam boyutlu görüntüyü görüntülemek için tıklayın)Figure 13: Have the ObjectDataSource Use the ProductsOptimisticConcurrencyBLL Object (Click to view full-size image)

Sihirbazdaki açılan listelerden GetProducts, UpdateProductve DeleteProduct yöntemlerini seçin.Choose the GetProducts, UpdateProduct, and DeleteProduct methods from drop-down lists in the wizard. UpdateProduct yöntemi için, ürünün tüm veri alanlarını kabul eden aşırı yüklemeyi kullanın.For the UpdateProduct method, use the overload that accepts all of the product's data fields.

ObjectDataSource denetiminin özelliklerini yapılandırmaConfiguring the ObjectDataSource Control's Properties

Sihirbazı tamamladıktan sonra, ObjectDataSource 'un bildirim temelli biçimlendirme aşağıdaki gibi görünmelidir:After completing the wizard, the ObjectDataSource's declarative markup should look like the following:

<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 DeleteParameters koleksiyonu, ProductsOptimisticConcurrencyBLL sınıfının DeleteProduct yöntemindeki her on giriş parametresi için bir Parameter örneği içerir.As you can see, the DeleteParameters collection contains a Parameter instance for each of the ten input parameters in the ProductsOptimisticConcurrencyBLL class's DeleteProduct method. Benzer şekilde, UpdateParameters koleksiyonu UpdateProductgiriş parametrelerinin her biri için bir Parameter örneği içerir.Likewise, the UpdateParameters collection contains a Parameter instance for each of the input parameters in UpdateProduct.

Veri değişikliğini içeren önceki öğreticiler için, bu özellik BLL yönteminin, eski (veya orijinal) değerlerinin ve yeni değerlerin geçirilmesini beklediğini gösterdiği için bu noktada ObjectDataSource 'un OldValuesParameterFormatString özelliğini kaldırdık.For those previous tutorials that involved data modification, we'd remove the ObjectDataSource's OldValuesParameterFormatString property at this point, since this property indicates that the BLL method expects the old (or original) values to be passed in as well as the new values. Ayrıca, bu özellik değeri özgün değerler için giriş parametresi adlarını belirtir.Furthermore, this property value indicates the input parameter names for the original values. Özgün değerleri BLL 'ye geçirdiğimiz için, bu özelliği kaldırmayın.Since we are passing in the original values into the BLL, do not remove this property.

Note

OldValuesParameterFormatString özelliğinin değeri, özgün değerleri bekleyen BLL içindeki giriş parametresi adlarıyla eşleşmelidir.The value of the OldValuesParameterFormatString property must map to the input parameter names in the BLL that expect the original values. Bu parametreleri original_productName, original_supplierIDvb. adlandırdığımız için OldValuesParameterFormatString özellik değerini original_{0}olarak bırakabilirsiniz.Since we named these parameters original_productName, original_supplierID, and so on, you can leave the OldValuesParameterFormatString property value as original_{0}. Ancak, BLL yöntemlerinin giriş parametrelerinin old_productName, old_supplierIDvb. gibi adları varsa OldValuesParameterFormatString özelliğini old_{0}olarak güncelleştirmeniz gerekir.If, however, the BLL methods' input parameters had names like old_productName, old_supplierID, and so on, you'd need to update the OldValuesParameterFormatString property to old_{0}.

ObjectDataSource 'un özgün değerleri BLL yöntemlerine doğru şekilde geçirmesi için yapılması gereken bir son özellik ayarı vardır.There's one final property setting that needs to be made in order for the ObjectDataSource to correctly pass the original values to the BLL methods. ObjectDataSource, iki değerden birineatanabilecek bir ConflictDetection özelliğine sahiptir:The ObjectDataSource has a ConflictDetection property that can be assigned to one of two values:

  • OverwriteChanges-varsayılan değer; özgün değerleri BLL metotlarının orijinal giriş parametrelerine göndermezOverwriteChanges - the default value; does not send the original values to the BLL methods' original input parameters
  • CompareAllValues-özgün değerleri BLL yöntemlerine gönderir; iyimser eşzamanlılık kullanılırken bu seçeneği belirleyinCompareAllValues - does send the original values to the BLL methods; choose this option when using optimistic concurrency

ConflictDetection özelliğini CompareAllValuesolarak ayarlamak için bir dakikanızı ayırın.Take a moment to set the ConflictDetection property to CompareAllValues.

GridView 'un özelliklerini ve alanlarını yapılandırmaConfiguring the GridView's Properties and Fields

ObjectDataSource özelliklerinin düzgün şekilde yapılandırıldığından, GridView 'u ayarlamaya dikkat çekelim.With the ObjectDataSource's properties properly configured, let's turn our attention to setting up the GridView. İlk olarak, GridView 'un Düzenle ve silmeyi desteklemesini istediğinizden, GridView 'un akıllı etiketindeki Düzenle 'yi etkinleştir ve onay kutularını silmeyi etkinleştir ' e tıklayın.First, since we want the GridView to support editing and deleting, click the Enable Editing and Enable Deleting checkboxes from the GridView's smart tag. Bu, ShowEditButton ve ShowDeleteButton her ikisi de trueolarak ayarlanan bir CommandField ekler.This will add a CommandField whose ShowEditButton and ShowDeleteButton are both set to true.

ProductsOptimisticConcurrencyDataSource ObjectDataSource 'a bağlandığında, GridView her bir ürünün veri alanı için bir alan içerir.When bound to the ProductsOptimisticConcurrencyDataSource ObjectDataSource, the GridView contains a field for each of the product's data fields. Böyle bir GridView düzenlenebilir olsa da, Kullanıcı deneyimi herhangi bir şeydir ancak kabul edilebilir.While such a GridView can be edited, the user experience is anything but acceptable. CategoryID ve SupplierID BoundFields, Kullanıcı tarafından KIMLIK numarası olarak uygun kategori ve tedarikçiyi girmesini gerektiren metin kutuları olarak işlenir.The CategoryID and SupplierID BoundFields will render as TextBoxes, requiring the user to enter the appropriate category and supplier as ID numbers. Sayısal alanlar için biçimlendirme olmaz ve ürün adının sağlandığından ve birim fiyatının, stoktaki birimlerin, siparişteki birimlerin ve yeniden sipariş düzeyi değerlerinin her ikisi de doğru sayısal değerler olduğundan ve daha büyük veya eşit olduğundan emin olmak için doğrulama denetimi yoktur sıfır olarak.There will be no formatting for the numeric fields and no validation controls to ensure that the product's name has been supplied and that the unit price, units in stock, units on order, and reorder level values are both proper numeric values and are greater than or equal to zero.

Düzen denetimleri ekleme ve arabirim ekleme ve veri değişikliği arabirimi öğreticilerini özelleştirme konusunda anlatıldığı gibi, Kullanıcı arabirimi, Boundfields alanları templatefields ile değiştirilerek özelleştirilebilir.As we discussed in the Adding Validation Controls to the Editing and Inserting Interfaces and Customizing the Data Modification Interface tutorials, the user interface can be customized by replacing the BoundFields with TemplateFields. Bu GridView ve Editing arabirimini aşağıdaki yollarla değiştirdim:I've modified this GridView and its editing interface in the following ways:

  • ProductID, SupplierNameve CategoryName BoundFields kaldırıldıRemoved the ProductID, SupplierName, and CategoryName BoundFields
  • BoundField ProductName bir TemplateField 'a dönüştürüldü ve bir RequiredFieldValidation denetimi ekledi.Converted the ProductName BoundField to a TemplateField and added a RequiredFieldValidation control.
  • CategoryID ve SupplierID BoundFields alanlarını TemplateFields 'e dönüştürüyordu ve metin kutuları yerine DropDownLists kullanmak için Düzenle arabirimini ayarladı.Converted the CategoryID and SupplierID BoundFields to TemplateFields, and adjusted the editing interface to use DropDownLists rather than TextBoxes. Bu TemplateFields ' ItemTemplates, CategoryName ve SupplierName veri alanları görüntülenir.In these TemplateFields' ItemTemplates, the CategoryName and SupplierName data fields are displayed.
  • UnitPrice, UnitsInStock, UnitsOnOrderve ReorderLevel BoundFields alanlarını TemplateFields 'e dönüştürülüp CompareValidator denetimleri ekledi.Converted the UnitPrice, UnitsInStock, UnitsOnOrder, and ReorderLevel BoundFields to TemplateFields and added CompareValidator controls.

Önceki öğreticilerde bu görevleri nasıl gerçekleştireceğinizi zaten incelediğimiz için, son bildirime dayalı sözdizimini burada listem ve uygulamayı uygulama olarak bırakmalısınız.Since we've already examined how to accomplish these tasks in previous tutorials, I'll just list the final declarative syntax here and leave the implementation as practice.

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

Tam olarak çalışan bir örneğe sahip olmanın çok yakınlıyız.We're very close to having a fully-working example. Ancak, bir yandan sorunları ortaya çıkarabilir ve sorun yaşalacak birkaç alt tzellikleri vardır.However, there are a few subtleties that will creep up and cause us problems. Buna ek olarak, bir eşzamanlılık ihlali oluştuğunda kullanıcıyı uyaran bir arabirim hala gereklidir.Additionally, we still need some interface that alerts the user when a concurrency violation has occurred.

Note

Bir veri Web denetiminin özgün değerleri ObjectDataSource 'a doğru bir şekilde geçirmesi için (daha sonra BLL 'ye geçirilir), GridView 'un EnableViewState özelliğinin true (varsayılan) olarak ayarlanması çok önemlidir.In order for a data Web control to correctly pass the original values to the ObjectDataSource (which are then passed to the BLL), it's vital that the GridView's EnableViewState property is set to true (the default). Görünüm durumunu devre dışı bırakırsanız, geri gönderme sırasında özgün değerler kaybedilir.If you disable view state, the original values are lost on postback.

Doğru özgün değerleri ObjectDataSource 'a geçirmePassing the Correct Original Values to the ObjectDataSource

GridView 'un yapılandırıldığı şekilde birkaç sorun vardır.There are a couple of problems with the way the GridView has been configured. ObjectDataSource 'un ConflictDetection özelliği CompareAllValues (bizde olduğu gibi) olarak ayarlandıysa, ObjectDataSource 'un Update() veya Delete() yöntemleri GridView (veya DetailsView ya da FormView) tarafından çağrıldığında, ObjectDataSource GridView 'in özgün değerlerini uygun Parameter örneklerine kopyalamaya çalışır.If the ObjectDataSource's ConflictDetection property is set to CompareAllValues (as is ours), when the ObjectDataSource's Update() or Delete() methods are invoked by the GridView (or DetailsView or FormView), the ObjectDataSource attempts to copy the GridView's original values into its appropriate Parameter instances. Bu işlemin grafik gösterimi için Şekil 2 ' ye geri bakın.Refer back to Figure 2 for a graphical representation of this process.

Özellikle, GridView 'un özgün değerleri, veriler GridView 'a her bağlandığında iki yönlü veri bağlama deyimlerine atanır.Specifically, the GridView's original values are assigned the values in the two-way databinding statements each time the data is bound to the GridView. Bu nedenle, gerekli orijinal değerlerin hepsi iki yönlü veri bağlama aracılığıyla yakalandığından ve dönüştürülebilir bir biçimde sağlandıklarından önemlidir.Therefore, it's essential that the required original values all are captured via two-way databinding and that they are provided in a convertible format.

Bunun ne kadar önemli olduğunu görmek için, sayfamızı tarayıcıda ziyaret etmek için bir dakikanızı ayırın.To see why this is important, take a moment to visit our page in a browser. GridView, beklendiği gibi her bir ürünü, en soldaki sütundaki Düzenle ve Sil düğmesiyle listeler.As expected, the GridView lists each product with an Edit and Delete button in the leftmost column.

ürünlerin bir GridView içinde listelenmesiThe Products are Listed in a GridView

Şekil 14: Ürünler GridView 'da listelenir (tam boyutlu görüntüyü görüntülemek için tıklayın)Figure 14: The Products are Listed in a GridView (Click to view full-size image)

Herhangi bir ürün için Sil düğmesine tıklarsanız bir FormatException oluşturulur.If you click the Delete button for any product, a FormatException is thrown.

bir FormatException içinde herhangi bir ürün sonucunu silmeye çalışırken Attempting to Delete Any Product Results in a FormatException

Şekil 15: bir FormatException herhangi bir ürün sonucunu silmeye çalışırken (tam boyutlu görüntüyü görüntülemek için tıklatın)Figure 15: Attempting to Delete Any Product Results in a FormatException (Click to view full-size image)

FormatException, ObjectDataSource özgün UnitPrice değerinde okumayı denediğinde tetiklenir.The FormatException is raised when the ObjectDataSource attempts to read in the original UnitPrice value. ItemTemplate bir para birimi (<%# Bind("UnitPrice", "{0:C}") %>) olarak biçimlendirilen UnitPrice sahip olduğundan, $19,95 gibi bir para birimi simgesi içerir.Since the ItemTemplate has the UnitPrice formatted as a currency (<%# Bind("UnitPrice", "{0:C}") %>), it includes a currency symbol, like $19.95. FormatException, ObjectDataSource bu dizeyi bir decimaldönüştürmeye çalıştığı için oluşur.The FormatException occurs as the ObjectDataSource attempts to convert this string into a decimal. Bu sorunu aşmak için birkaç seçenek vardır:To circumvent this problem, we have a number of options:

  • ItemTemplatepara birimi biçimlendirmesini kaldırın.Remove the currency formatting from the ItemTemplate. Diğer bir deyişle, <%# Bind("UnitPrice", "{0:C}") %>kullanmak yerine <%# Bind("UnitPrice") %>kullanmanız yeterlidir.That is, instead of using <%# Bind("UnitPrice", "{0:C}") %>, simply use <%# Bind("UnitPrice") %>. Bunun aşağı tarafı, fiyatın artık biçimlendirilmemesi durumunda olur.The downside of this is that the price is no longer formatted.
  • ItemTemplatebir para birimi olarak biçimlendirilen UnitPrice görüntüleyin, ancak bunu gerçekleştirmek için Eval anahtar sözcüğünü kullanın.Display the UnitPrice formatted as a currency in the ItemTemplate, but use the Eval keyword to accomplish this. Eval tek yönlü veri bağlamayı gerçekleştirdiğini hatırlayın.Recall that Eval performs one-way databinding. Yine de özgün değerler için UnitPrice değeri sağlamanız gerekir, bu nedenle ItemTemplateiki yönlü bir veri bağlama bildirimine ihtiyacımız var, ancak bu, Visible özelliği falseolarak ayarlanmış bir etiket Web denetimine yerleştirilebilecek.We still need to provide the UnitPrice value for the original values, so we'll still need a two-way databinding statement in the ItemTemplate, but this can be placed in a Label Web control whose Visible property is set to false. ItemTemplate 'te aşağıdaki biçimlendirmeyi kullanabiliriz:We could use the following markup in the ItemTemplate:
<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>
  • <%# Bind("UnitPrice") %>kullanarak ItemTemplatepara birimi biçimlendirmesini kaldırın.Remove the currency formatting from the ItemTemplate, using <%# Bind("UnitPrice") %>. GridView 'ın RowDataBound olay işleyicisinde, UnitPrice değerinin görüntülendiği etiketli Web denetimine programlı bir şekilde erişin ve Text özelliğini biçimlendirilen sürüm olarak ayarlayın.In the GridView's RowDataBound event handler, programmatically access the Label Web control within which the UnitPrice value is displayed and set its Text property to the formatted version.
  • UnitPrice para birimi olarak biçimlendirilen şekilde bırakın.Leave the UnitPrice formatted as a currency. GridView 'un RowDeleting olay işleyicisinde, var olan özgün UnitPrice değerini ($19,95) Decimal.Parsekullanarak gerçek bir ondalık değer ile değiştirin.In the GridView's RowDeleting event handler, replace the existing original UnitPrice value ($19.95) with an actual decimal value using Decimal.Parse. Bir ASP.NET sayfa öğreticisindeki BLL ve dal düzeyi özel durumları işleme RowUpdating olay işleyicisinde benzer bir şeyi nasıl gerçekleştireceğinizi gördük.We saw how to accomplish something similar in the RowUpdating event handler in the Handling BLL- and DAL-Level Exceptions in an ASP.NET Page tutorial.

Örneğimin, Text özelliği biçimlendirilmemiş UnitPrice değerine göre iki yönlü veri olan bir gizli etiket Web denetimi eklemek için ikinci yaklaşımla devam ediyorum.For my example I chose to go with the second approach, adding a hidden Label Web control whose Text property is two-way data bound to the unformatted UnitPrice value.

Bu sorunu çözdikten sonra, herhangi bir ürün için Sil düğmesine tıklayarak yeniden deneyin.After solving this problem, try clicking the Delete button for any product again. Bu kez, ObjectDataSource BLL 'nin UpdateProduct yöntemini çağırmayı denediğinde bir InvalidOperationException alırsınız.This time you'll get an InvalidOperationException when the ObjectDataSource attempts to invoke the BLL's UpdateProduct method.

ObjectDataSource, göndermek Istediği giriş parametrelerine sahip bir yöntem bulamıyorThe ObjectDataSource Cannot Find a Method with the Input Parameters it Wants to Send

Şekil 16: ObjectDataSource, göndermek Istediği giriş parametrelerine sahip bir yöntem bulamıyor (tam boyutlu görüntüyü görüntülemek için tıklayın)Figure 16: The ObjectDataSource Cannot Find a Method with the Input Parameters it Wants to Send (Click to view full-size image)

Özel durumun iletisine baktığınızda, ObjectDataSource 'un original_CategoryName ve original_SupplierName giriş parametrelerini içeren bir BLL DeleteProduct yöntemi çağırmak istemektedir.Looking at the exception's message, it's clear that the ObjectDataSource wants to invoke a BLL DeleteProduct method that includes original_CategoryName and original_SupplierName input parameters. Bunun nedeni, CategoryID ve SupplierID TemplateFields için ItemTemplate, şu anda CategoryName ve SupplierName veri alanlarıyla iki yönlü bağlama deyimleri içereceğinden oluşur.This is because the ItemTemplate s for the CategoryID and SupplierID TemplateFields currently contain two-way Bind statements with the CategoryName and SupplierName data fields. Bunun yerine, CategoryID ve SupplierID veri alanlarıyla Bind deyimleri dahil etmemiz gerekir.Instead, we need to include Bind statements with the CategoryID and SupplierID data fields. Bunu gerçekleştirmek için, var olan bind deyimlerini Eval deyimleriyle değiştirin ve ardından Text özellikleri CategoryID ve SupplierID veri alanlarına bağlı olan gizli etiket denetimleri aşağıda gösterildiği gibi iki yönlü veri bağlamayı kullanarak ekleyin:To accomplish this, replace the existing Bind statements with Eval statements, and then add hidden Label controls whose Text properties are bound to the CategoryID and SupplierID data fields using two-way databinding, as shown below:

<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!With these changes, we are now able to successfully delete and edit product information! 5. adımda eşzamanlılık ihlallerinin algılanmakta olduğunu doğrulama bölümüne bakacağız.In Step 5 we'll look at how to verify that concurrency violations are being detected. Ancak şimdilik, tek bir kullanıcı için güncelleştirme ve silme işlemlerinin beklendiği gibi çalıştığından emin olmak için birkaç kaydı güncelleştirmeyi ve silmeyi denemek üzere birkaç dakikanızı yararlanın.But for now, take a few minutes to try updating and deleting a few records to ensure that updating and deleting for a single user works as expected.

5. Adım: Iyimser eşzamanlılık desteğini test etmeStep 5: Testing the Optimistic Concurrency Support

Eşzamanlılık ihlallerinin algılanmakta olduğunu doğrulamak için (verilerin üzerine yazılmakta olması yerine), bu sayfada iki tarayıcı penceresi açmanız gerekir.In order to verify that concurrency violations are being detected (rather than resulting in data being blindly overwritten), we need to open two browser windows to this page. Her iki tarayıcı örneğinde de, Chai için Düzenle düğmesine tıklayın.In both browser instances, click on the Edit button for Chai. Ardından, tarayıcıların yalnızca birinde adı "Chai Tea" olarak değiştirin ve Güncelleştir ' e tıklayın.Then, in just one of the browsers, change the name to "Chai Tea" and click Update. Güncelleştirme başarılı olmalıdır ve yeni ürün adı olarak "Chai Tea" ile GridView 'u önceden düzenlenen durumuna döndürmelidir.The update should succeed and return the GridView to its pre-editing state, with "Chai Tea" as the new product name.

Öte yandan, diğer tarayıcı pencere örneğinde, ürün adı metin kutusu hala "Chai" gösterir.In the other browser window instance, however, the product name TextBox still shows "Chai". Bu ikinci tarayıcı penceresinde UnitPrice 25.00güncelleştirin.In this second browser window, update the UnitPrice to 25.00. İyimser eşzamanlılık desteği olmadan ikinci tarayıcı örneğinde Güncelleştir ' e tıklamak, ürün adını "Chai" olarak değiştirecek ve böylece ilk tarayıcı örneği tarafından yapılan değişikliklerin üzerine yazılmasına neden olur.Without optimistic concurrency support, clicking update in the second browser instance would change the product name back to "Chai", thereby overwriting the changes made by the first browser instance. Ancak, iyimser eşzamanlılık istihdam ile ikinci tarayıcı örneğindeki Güncelleştir düğmesine tıklamak bir DBConcurrencyExceptionile sonuçlanır.With optimistic concurrency employed, however, clicking the Update button in the second browser instance results in a DBConcurrencyException.

bir eşzamanlılık Ihlali algılandığında, bir DBConcurrencyException oluşturulurWhen a Concurrency Violation is Detected, a DBConcurrencyException is Thrown

Şekil 17: bir eşzamanlılık ihlali algılandığında, bir DBConcurrencyException oluşturulur (tam boyutlu görüntüyü görüntülemek için tıklatın)Figure 17: When a Concurrency Violation is Detected, a DBConcurrencyException is Thrown (Click to view full-size image)

DBConcurrencyException yalnızca DAL toplu iş güncelleştirme deseninin kullanıldığı durumlarda oluşturulur.The DBConcurrencyException is only thrown when the DAL's batch update pattern is utilized. VERITABANı doğrudan stili özel durum oluşturmaz, yalnızca hiçbir satırın etkilenmediğini belirtir.The DB direct pattern does not raise an exception, it merely indicates that no rows were affected. Bunu göstermek için, hem tarayıcı örneklerinin hem de kendi düzenlenme durumuna döndürün.To illustrate this, return both browser instances' GridView to their pre-editing state. Ardından, ilk tarayıcı örneğinde Düzenle düğmesine tıklayın ve ürün adını "Chai Tea" iken "Chai" olarak değiştirin ve Güncelleştir ' e tıklayın.Next, in the first browser instance, click the Edit button and change the product name from "Chai Tea" back to "Chai" and click Update. İkinci tarayıcı penceresinde, Chai için Sil düğmesine tıklayın.In the second browser window, click the Delete button for Chai.

Sil ' e tıklandıktan sonra, sayfa geri gönderilerek, GridView, ObjectDataSource 'un Delete() yöntemini çağırır ve ObjectDataSource, ProductsOptimisticConcurrencyBLL sınıfının DeleteProduct yöntemine çağırarak özgün değerleri de geçirir.Upon clicking Delete, the page posts back, the GridView invokes the ObjectDataSource's Delete() method, and the ObjectDataSource calls down into the ProductsOptimisticConcurrencyBLL class's DeleteProduct method, passing along the original values. İkinci tarayıcı örneği için özgün ProductName değeri, veritabanındaki geçerli ProductName değeriyle eşleşmeyen "Chai Tea" dır.The original ProductName value for the second browser instance is "Chai Tea", which doesn't match up with the current ProductName value in the database. Bu nedenle, veritabanında WHERE yan tümcesinin karşıladığından bir kayıt bulunmadığından veritabanına verilen DELETE deyimi sıfır satırı etkiler.Therefore the DELETE statement issued to the database affects zero rows since there's no record in the database that the WHERE clause satisfies. DeleteProduct yöntemi false döndürür ve ObjectDataSource 'un verileri GridView 'a yeniden bağlanır.The DeleteProduct method returns false and the ObjectDataSource's data is rebound to the GridView.

Son kullanıcının perspektifinden, ikinci tarayıcı penceresinde Chai Tea için Sil düğmesine tıkladığınızda, ekranın Flash 'a ve geri alındıktan sonra, artık "Chai" olarak listelense de (ilk tarayıcı tarafından yapılan ürün adı değişikliği). örnek).From the end user's perspective, clicking on the Delete button for Chai Tea in the second browser window caused the screen to flash and, upon coming back, the product is still there, although now it's listed as "Chai" (the product name change made by the first browser instance). Kullanıcı Sil düğmesine yeniden tıkladığında, GridView 'un özgün ProductName değeri ("Chai") artık veritabanındaki değerle eşleştirirken silme işlemi başarılı olur.If the user clicks the Delete button again, the Delete will succeed, as the GridView's original ProductName value ("Chai") now matches up with the value in the database.

Her iki durumda da, Kullanıcı deneyimi ideal bir deneyimdir.In both of these cases, the user experience is far from ideal. Batch güncelleştirme modelini kullanırken DBConcurrencyException özel durumunun Nitty-Gritty ayrıntılarını kullanıcıya göstermek istemezsiniz.We clearly don't want to show the user the nitty-gritty details of the DBConcurrencyException exception when using the batch update pattern. Ve VERITABANı doğrudan düzeninin kullanılması, Kullanıcı komutu başarısız olduğu için biraz kafa karıştırıcı, ancak neden kesin bir belirtilemedi.And the behavior when using the DB direct pattern is somewhat confusing as the users command failed, but there was no precise indication of why.

Bu iki sorunu gidermek için, sayfada bir güncelleştirme ya da silmenin neden başarısız olduğuna ilişkin bir açıklama sağlayan başlık Web denetimleri oluşturuyoruz.To remedy these two issues, we can create Label Web controls on the page that provide an explanation to why an update or delete failed. Batch güncelleştirme düzeninde, GridView 'un düzey sonrası olay işleyicide bir DBConcurrencyException özel durumun oluşup oluşmadığını tespit edebilir ve uyarı etiketini gerektiği gibi görüntüleyebilirsiniz.For the batch update pattern, we can determine whether or not a DBConcurrencyException exception occurred in the GridView's post-level event handler, displaying the warning label as needed. VERITABANı doğrudan yönteminde, BLL yönteminin dönüş değerini inceleyebilirsiniz (bir satır etkilenirse true, aksi takdirde false) ve gerektiğinde bilgilendirici bir ileti görüntüleyebilirsiniz.For the DB direct method, we can examine the return value of the BLL method (which is true if one row was affected, false otherwise) and display an informational message as needed.

6. Adım: bilgilendirici Iletiler ekleme ve bunları eşzamanlılık Ihlalinin Yüzinde görüntülemeStep 6: Adding Informational Messages and Displaying Them in the Face of a Concurrency Violation

Bir eşzamanlılık ihlali gerçekleştiğinde, bu davranış, DAL 'ın Batch güncelleştirmesi veya DB doğrudan deseninin kullanılıp kullanılmadığına bağlıdır.When a concurrency violation occurs, the behavior exhibited depends on whether the DAL's batch update or DB direct pattern was used. Öğreticimiz, güncelleştirme için kullanılan Batch güncelleştirme düzeni ve silme için kullanılan VERITABANı doğrudan deseni ile her iki deseni de kullanmaktadır.Our tutorial uses both patterns, with the batch update pattern being used for updating and the DB direct pattern used for deleting. Başlamak için sayfamıza, verileri silmeye veya güncelleştirmeye çalışırken bir eşzamanlılık ihlalinin oluştuğunu açıklayan iki etiketli Web denetimi ekleyelim.To get started, let's add two Label Web controls to our page that explain that a concurrency violation occurred when attempting to delete or update data. Etiket denetiminin Visible ve EnableViewState özelliklerini false; olarak ayarlayın. Bu, Visible özelliğinin program aracılığıyla trueolarak ayarlandığı belirli sayfa ziyaretlerinin dışında, bu kişilerin her sayfada gizli olmasına neden olur.Set the Label control's Visible and EnableViewState properties to false; this will cause them to be hidden on each page visit except for those particular page visits where their Visible property is programmatically set to true.

<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." />

Visible, EnabledViewStateve Text özelliklerini ayarlamaya ek olarak, CssClass özelliğini Warningolarak ayarlıyorum, bu da etiketin büyük, kırmızı, italik ve kalın yazı tipinde görüntülenmesine neden olur.In addition to setting their Visible, EnabledViewState, and Text properties, I've also set the CssClass property to Warning, which causes the Label's to be displayed in a large, red, italic, bold font. Bu CSS Warning sınıfı tanımlanmış ve stillerle eklendi. css, ekleme, güncelleştirme ve silme öğreticisi Ile Ilişkili olayları İnceleme .This CSS Warning class was defined and added to Styles.css back in the Examining the Events Associated with Inserting, Updating, and Deleting tutorial.

Bu Etiketler eklendikten sonra, Visual Studio 'daki tasarımcı Şekil 18 ' e benzer görünmelidir.After adding these Labels, the Designer in Visual Studio should look similar to Figure 18.

Sayfaya Iki Etiket denetimi eklendiTwo Label Controls Have Been Added to the Page

Şekil 18: sayfaya Iki Etiket denetimi eklendi (tam boyutlu görüntüyü görüntülemek için tıklayın)Figure 18: Two Label Controls Have Been Added to the Page (Click to view full-size image)

Bu etiketli Web denetimleriyle birlikte, bir eşzamanlılık ihlalinin ne zaman gerçekleştiğini belirleme hazırız. bu noktada, uygun etiketin Visible özelliği trueolarak ayarlanabilir ve bilgilendirici ileti görüntülenir.With these Label Web controls in place, we're ready to examine how to determine when a concurrency violation has occurred, at which point the appropriate Label's Visible property can be set to true, displaying the informational message.

Güncelleştirme sırasında eşzamanlılık Ihlallerini işlemeHandling Concurrency Violations When Updating

Toplu güncelleştirme modelini kullanırken eşzamanlılık ihlallerinin nasıl işleneceğini göz atalım.Let's first look at how to handle concurrency violations when using the batch update pattern. Batch Update düzenine sahip bu tür ihlaller DBConcurrencyException bir özel durumun oluşturulmasına neden olduğundan, güncelleştirme işlemi sırasında bir DBConcurrencyException özel durumun oluşup oluşmadığını öğrenmek için ASP.NET sayfamıza kod eklememiz gerekir.Since such violations with the batch update pattern cause a DBConcurrencyException exception to be thrown, we need to add code to our ASP.NET page to determine whether a DBConcurrencyException exception occurred during the update process. Bu durumda, başka bir kullanıcı kayıt düzenlendikleri sırada ve Güncelleştir düğmesine tıklandığı zaman arasında aynı verileri değiştirdiği için, kullanıcının yaptıkları değişiklikleri kaydetmediğini açıklayan bir ileti görüntüleriz.If so, we should display a message to the user explaining that their changes were not saved because another user had modified the same data between when they started editing the record and when they clicked the Update button.

Bir ASP.NET sayfa öğreticisindeki BLL ve dal düzeyi özel durumları işleme gördük. bu tür özel durumlar, veri Web denetiminin düzey sonrası olay işleyicilerinde tespit edilebilir ve gizlenebilir.As we saw in the Handling BLL- and DAL-Level Exceptions in an ASP.NET Page tutorial, such exceptions can be detected and suppressed in the data Web control's post-level event handlers. Bu nedenle, bir DBConcurrencyException özel durumunun oluşturulmuş olduğunu denetleyen GridView 'un RowUpdated olayı için bir olay işleyicisi oluşturuyoruz.Therefore, we need to create an event handler for the GridView's RowUpdated event that checks if a DBConcurrencyException exception has been thrown. Bu olay işleyicisine aşağıdaki olay işleyicisi kodunda gösterildiği gibi, güncelleştirme işlemi sırasında oluşturulan tüm özel durumlar için bir başvuru geçirilir:This event handler is passed a reference to any exception that was raised during the updating process, as shown in the event handler code below:

Protected Sub ProductsGrid_RowUpdated _
        (ByVal sender As Object, ByVal e As GridViewUpdatedEventArgs) _
        Handles ProductsGrid.RowUpdated
    If e.Exception IsNot Nothing AndAlso e.Exception.InnerException IsNot Nothing Then
        If TypeOf e.Exception.InnerException Is System.Data.DBConcurrencyException Then
            ' Display the warning message and note that the exception has
            ' been handled...
            UpdateConflictMessage.Visible = True
            e.ExceptionHandled = True
        End If
    End If
End Sub

DBConcurrencyException bir özel durum durumunda bu olay işleyicisi UpdateConflictMessage etiketi denetimini görüntüler ve özel durumun işlendiğini gösterir.In the face of a DBConcurrencyException exception, this event handler displays the UpdateConflictMessage Label control and indicates that the exception has been handled. Bu kod yerinde olduğunda, bir kaydı güncelleştirirken bir eşzamanlılık ihlali meydana geldiğinde, başka bir kullanıcının değişikliklerinin aynı anda üzerine yazıldıklarından, kullanıcının değişiklikleri kaybedilir.With this code in place, when a concurrency violation occurs when updating a record, the user's changes are lost, since they would have overwritten another user's modifications at the same time. Özellikle GridView, önceden düzenlenen durumuna döndürülür ve geçerli veritabanı verilerine bağlanır.In particular, the GridView is returned to its pre-editing state and bound to the current database data. Bu, GridView satırını daha önce görünmeyen diğer kullanıcının değişiklikleriyle güncelleştirir.This will update the GridView row with the other user's changes, which were previously not visible. Ayrıca, UpdateConflictMessage etiket denetimi kullanıcıya ne olduğunu açıklayacak.Additionally, the UpdateConflictMessage Label control will explain to the user what just happened. Bu olay sırası Şekil 19 ' da ayrıntılı olarak açıklanmıştır.This sequence of events is detailed in Figure 19.

bir Kullanıcı güncelleştirmelerinin bir eşzamanlılık Ihlali durumunda kaybolmasıA User s Updates are Lost in the Face of a Concurrency Violation

Şekil 19: bir Kullanıcı güncelleştirmelerinin bir eşzamanlılık Ihlali tarafında kaybolması (tam boyutlu görüntüyü görüntülemek için tıklatın)Figure 19: A User s Updates are Lost in the Face of a Concurrency Violation (Click to view full-size image)

Note

Alternatif olarak, GridView 'u önceden düzenlenen durumuna döndürmek yerine, geçirilen GridViewUpdatedEventArgs nesnesinin KeepInEditMode özelliğini true olarak ayarlayarak GridView 'u kendi düzen durumunda bırakabiliriz.Alternatively, rather than returning the GridView to the pre-editing state, we could leave the GridView in its editing state by setting the KeepInEditMode property of the passed-in GridViewUpdatedEventArgs object to true. Ancak, bu yaklaşımı kullanırsanız, diğer kullanıcının değerlerinin düzen arabirimine yüklenmesi için verileri GridView 'a yeniden bağlamak (DataBind() yöntemini çağırarak) gerekir.If you take this approach, however, be certain to rebind the data to the GridView (by invoking its DataBind() method) so that the other user's values are loaded into the editing interface. Bu öğreticiyle indirilebileceğiniz kod, RowUpdated olay işleyicisindeki bu iki kod satırını açıklama satırı ' na sahiptir; bir eşzamanlılık ihlalinden sonra GridView 'un düzenleme modunda kalmasını sağlamak için bu kod satırlarının açıklamasını basitçe kaldırın.The code available for download with this tutorial has these two lines of code in the RowUpdated event handler commented out; simply uncomment these lines of code to have the GridView remain in edit mode after a concurrency violation.

Silinirken eşzamanlılık Ihlallerine yanıt vermeResponding to Concurrency Violations When Deleting

VERITABANı doğrudan düzeniyle, eşzamanlılık ihlali durumunda bir özel durum ortaya çıkar.With the DB direct pattern, there is no exception raised in the face of a concurrency violation. Bunun yerine, WHERE yan tümcesi hiçbir kayıtla eşleşmediğinden database deyimi yalnızca kaydı etkilemez.Instead, the database statement simply affects no records, as the WHERE clause does not match with any record. BLL 'de oluşturulan tüm veri değiştirme yöntemleri, tam olarak bir kaydın etkilenip etkilenmediğini belirten bir Boole değeri döndürecek şekilde tasarlanmıştır.All of the data modification methods created in the BLL have been designed such that they return a Boolean value indicating whether or not they affected precisely one record. Bu nedenle, bir kayıt silinirken eşzamanlılık ihlalinin oluşup oluşmadığını öğrenmek için BLL 'nin DeleteProduct yönteminin dönüş değerini inceleyebilirsiniz.Therefore, to determine if a concurrency violation occurred when deleting a record, we can examine the return value of the BLL's DeleteProduct method.

Bir BLL yöntemi için dönüş değeri, ObjectDataSource 'un, olay işleyicisine geçirilen ObjectDataSourceStatusEventArgs nesnesinin ReturnValue özelliği aracılığıyla kendi düzey sonrası olay işleyicilerinde incelenebilir.The return value for a BLL method can be examined in the ObjectDataSource's post-level event handlers through the ReturnValue property of the ObjectDataSourceStatusEventArgs object passed into the event handler. DeleteProduct yönteminden dönüş değerini belirlemede ilgilendiğimiz için, ObjectDataSource 'un Deleted olayı için bir olay işleyicisi oluşturuyoruz.Since we are interested in determining the return value from the DeleteProduct method, we need to create an event handler for the ObjectDataSource's Deleted event. ReturnValue özelliği object türündedir ve bir özel durum harekete geçirilir ve yöntem bir değer döndürebilmesi için kesintiye uğrarsa null olabilir.The ReturnValue property is of type object and can be null if an exception was raised and the method was interrupted before it could return a value. Bu nedenle, öncelikle ReturnValue özelliğinin null olmadığından ve bir Boolean değer olduğundan emin olunması gerekir.Therefore, we should first ensure that the ReturnValue property is not null and is a Boolean value. Bu denetimin başarılı olduğu varsayıldığında, ReturnValue false``DeleteConflictMessage etiket denetimini gösteririz.Assuming this check passes, we show the DeleteConflictMessage Label control if the ReturnValue is false. Bu, aşağıdaki kod kullanılarak gerçekleştirilebilir:This can be accomplished by using the following code:

Protected Sub ProductsOptimisticConcurrencyDataSource_Deleted _
        (ByVal sender As Object, ByVal e As ObjectDataSourceStatusEventArgs) _
        Handles ProductsOptimisticConcurrencyDataSource.Deleted
    If e.ReturnValue IsNot Nothing AndAlso TypeOf e.ReturnValue Is Boolean Then
        Dim deleteReturnValue As Boolean = CType(e.ReturnValue, Boolean)
        If deleteReturnValue = False Then
            ' No row was deleted, display the warning message
            DeleteConflictMessage.Visible = True
        End If
    End If
End Sub

Eşzamanlılık ihlali durumunda kullanıcının silme isteği iptal edilir.In the face of a concurrency violation, the user's delete request is canceled. GridView yenilenir, bu kayıt için, kullanıcının sayfayı yüklediği zamanla ve Sil düğmesine tıkladıklarında oluşan değişiklikler gösteriliyor.The GridView is refreshed, showing the changes that occurred for that record between the time the user loaded the page and when he clicked the Delete button. Böyle bir ihlal transpires, ne olduğunu açıklayan DeleteConflictMessage etiketi gösterilir (bkz. Şekil 20).When such a violation transpires, the DeleteConflictMessage Label is shown, explaining what just happened (see Figure 20).

bir Kullanıcı silme işlemi eşzamanlılık Ihlali durumunda Iptal edilirA User s Delete is Canceled in the Face of a Concurrency Violation

Şekil 20: bir Kullanıcı silme Işlemi eşzamanlılık Ihlali durumunda iptal edilir (tam boyutlu görüntüyü görüntülemek için tıklayın)Figure 20: A User s Delete is Canceled in the Face of a Concurrency Violation (Click to view full-size image)

ÖzetSummary

Birden çok, eşzamanlı kullanıcının verileri güncelleştirmesine veya silmesine izin veren her uygulamada eşzamanlılık ihlallerinin fırsatları vardır.Opportunities for concurrency violations exist in every application that allows multiple, concurrent users to update or delete data. Bu tür ihlaller ' de hesaba katılmaz, iki kullanıcı aynı anda aynı verileri güncelleştirirse, diğer kullanıcının değişiklik değişikliklerinin üzerine yazılır.If such violations are not accounted for, when two users simultaneously update the same data whoever gets in the last write "wins," overwriting the other user's changes changes. Alternatif olarak, geliştiriciler iyimser veya Kötümser eşzamanlılık denetimi uygulayabilir.Alternatively, developers can implement either optimistic or pessimistic concurrency control. İyimser eşzamanlılık denetimi eşzamanlılık ihlallerinin seyrek olduğunu varsayar ve bir eşzamanlılık ihlali oluşturacak bir güncelleştirme ya da silme komutuna izin vermez.Optimistic concurrency control assumes that concurrency violations are infrequent and simply disallows an update or delete command that would constitute a concurrency violation. Kötümser eşzamanlılık denetimi eşzamanlılık ihlallerinin sık olduğunu varsayar ve tek bir kullanıcının Update ya da Delete komutunun kabul edilebilir olmadığından emin olur.Pessimistic concurrency control assumes that concurrency violations are frequent and simply rejecting one user's update or delete command is not acceptable. Kötümser eşzamanlılık denetimiyle bir kaydın güncelleştirilmesi, kilitlenirken diğer kullanıcıların kaydı değiştirmesini veya silmesini önler.With pessimistic concurrency control, updating a record involves locking it, thereby preventing any other users from modifying or deleting the record while it is locked.

.NET 'teki türü belirtilmiş veri kümesi, iyimser eşzamanlılık denetimini desteklemek için işlevsellik sağlar.The Typed DataSet in .NET provides functionality for supporting optimistic concurrency control. Özellikle, veritabanına verilen UPDATE ve DELETE deyimleri tüm tablo 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 gerçekleştirirken sahip olduğu özgün verilerle eşleşiyorsa meydana gelir.In particular, the UPDATE and DELETE statements issued to the database include all of the table's columns, thereby ensuring that the update or delete will only occur if the record's current data matches with the original data the user had when performing their update or delete. DAL, iyimser eşzamanlılığı destekleyecek şekilde yapılandırıldıktan sonra BLL yöntemlerinin güncellenmesi gerekir.Once the DAL has been configured to support optimistic concurrency, the BLL methods need to be updated. Ayrıca, BLL içine geri çağıran ASP.NET sayfası, ObjectDataSource, veri Web denetiminden özgün değerleri alıp onları BLL 'ye geçirmeden önce yapılandırılmalıdır.Additionally, the ASP.NET page that calls down into the BLL must be configured such that the ObjectDataSource retrieves the original values from its data Web control and passes them down into the BLL.

Bu öğreticide gördüğümüz gibi, bir ASP.NET Web uygulamasında iyimser eşzamanlılık denetimini uygulamak, DAL ve BLL 'yi güncelleştirmeyi ve ASP.NET sayfasına destek eklemeyi içerir.As we saw in this tutorial, implementing optimistic concurrency control in an ASP.NET web application involves updating the DAL and BLL and adding support in the ASP.NET page. Bu eklenen iş, sizin zaman ve çabalarınızın bir temelinde yatırım yapıp uygulamadığınıza bağlıdır.Whether or not this added work is a wise investment of your time and effort depends on your application. Eşzamanlı kullanıcılarınızın verileri güncelliyor veya güncelleştirdikleri veriler diğerinden farklıysa eşzamanlılık denetimi bir temel sorun değildir.If you infrequently have concurrent users updating data, or the data they are updating is different from one another, then concurrency control is not a key issue. Ancak, sitenizde aynı verilerle çalışan birden fazla Kullanıcı düzenli olarak varsa, eşzamanlılık denetimi bir kullanıcının güncelleştirmelerinin veya silinmesinden başka birinin üzerine yazılmasına neden olacak şekilde, bir kullanıcının güncelleştirme veya silme yapılmasını önlemeye yardımcı olabilir.If, however, you routinely have multiple users on your site working with the same data, concurrency control can help prevent one user's updates or deletes from unwittingly overwriting another's.

Programlamanın kutlu olsun!Happy Programming!

Yazar hakkındaAbout the Author

4GuysFromRolla.com 'in, Scott Mitchell, yedi ASP/ASP. net books ve 'in yazarı, 1998 sürümünden bu yana Microsoft Web teknolojileriyle çalışmaktadır.Scott Mitchell, author of seven ASP/ASP.NET books and founder of 4GuysFromRolla.com, has been working with Microsoft Web technologies since 1998. Scott bağımsız danışman, Trainer ve yazıcı olarak çalışıyor.Scott works as an independent consultant, trainer, and writer. En son kitabı, 24 saat içinde ASP.NET 2,0 kendi kendinize eğitimister.His latest book is Sams Teach Yourself ASP.NET 2.0 in 24 Hours. mitchell@4GuysFromRolla.comadresinden erişilebilir .He can be reached at mitchell@4GuysFromRolla.com. ya da blog aracılığıyla http://ScottOnWriting.NETbulabilirsiniz.or via his blog, which can be found at http://ScottOnWriting.NET.