Creazione di un livello per la logica di business (VB)
In questa esercitazione verrà illustrato come centralizzare le regole aziendali in un livello BLL (Business Logic Layer) che funge da intermediario per lo scambio di dati tra il livello di presentazione e il DAL.
Introduzione
Il livello di accesso ai dati creato nella prima esercitazione separa in modo pulito la logica di accesso ai dati dalla logica di presentazione. Tuttavia, mentre dal dal punto di vista pulito separa i dettagli di accesso ai dati dal livello di presentazione, non applica regole aziendali che possono essere applicate. Ad esempio, per l'applicazione potrebbe essere consigliabile impedire la CategoryID
modifica dei campi o SupplierID
dei campi della Products
tabella quando il Discontinued
campo è impostato su 1 oppure potrebbe essere necessario applicare regole di seniorità, impedendo situazioni in cui un dipendente viene gestito da un dipendente che è stato assunto dopo di essi. Un altro scenario comune è l'autorizzazione forse solo gli utenti in un determinato ruolo possono eliminare i prodotti o modificare il UnitPrice
valore.
In questa esercitazione verrà illustrato come centralizzare queste regole business in un livello BLL (Business Logic Layer) che funge da intermediario per lo scambio di dati tra il livello di presentazione e il DAL. In un'applicazione reale, il BLL deve essere implementato come progetto di libreria di classi separato; Tuttavia, per queste esercitazioni si implementerà il BLL come serie di classi nella App_Code
cartella per semplificare la struttura del progetto. La figura 1 illustra le relazioni architetturali tra il livello di presentazione, BLL e DAL.
Figura 1: BLL separa il livello di presentazione dal livello di accesso ai dati e impone regole business
Anziché creare classi separate per implementare la logica di business, è possibile inserire in alternativa questa logica direttamente nel DataSet tipizzato con classi parziali. Per un esempio di creazione ed estensione di un DataSet tipizzato, fare riferimento alla prima esercitazione.
Passaggio 1: Creazione delle classi BLL
Il BLL sarà composto da quattro classi, una per ogni TableAdapter nel DAL; ognuna di queste classi BLL avrà metodi per il recupero, l'inserimento, l'aggiornamento e l'eliminazione dai rispettivi TableAdapter nel DAL, applicando le regole business appropriate.
Per separare in modo più pulito le classi correlate a DAL e BLL, creare due sottocartelle nella App_Code
cartella DAL
e BLL
. Fare semplicemente clic con il pulsante destro del mouse sulla App_Code
cartella nella Esplora soluzioni e scegliere Nuova cartella. Dopo aver creato queste due cartelle, spostare l'oggetto DataSet tipizzato creato nella prima esercitazione nella DAL
sottocartella.
Creare quindi i quattro file di classe BLL nella BLL
sottocartella. A questo scopo, fare clic con il pulsante destro del mouse sulla BLL
sottocartella, scegliere Aggiungi un nuovo elemento e scegliere il modello di classe. Assegnare un nome alle quattro classi ProductsBLL
, CategoriesBLL
, SuppliersBLL
e EmployeesBLL
.
Figura 2: Aggiungere quattro nuove classi alla App_Code
cartella
Aggiungere quindi metodi a ognuna delle classi per eseguire semplicemente il wrapping dei metodi definiti per TableAdapters dalla prima esercitazione. Per il momento, questi metodi chiameranno direttamente nel DAL; verrà restituito in un secondo momento per aggiungere qualsiasi logica di business necessaria.
Nota
Se si usa Visual Studio Standard Edition o versione successiva(ovvero, non si usa Visual Web Developer), è possibile progettare le classi in modo visivo usando la classe Designer. Per altre informazioni su questa nuova funzionalità in Visual Studio, vedere il blog classe Designer.
Per la ProductsBLL
classe è necessario aggiungere un totale di sette metodi:
GetProducts()
restituisce tutti i prodottiGetProductByProductID(productID)
restituisce il prodotto con l'ID prodotto specificatoGetProductsByCategoryID(categoryID)
restituisce tutti i prodotti dalla categoria specificataGetProductsBySupplier(supplierID)
restituisce tutti i prodotti dal fornitore specificatoAddProduct(productName, supplierID, categoryID, quantityPerUnit, unitPrice, unitsInStock, unitsOnOrder, reorderLevel, discontinued)
inserisce un nuovo prodotto nel database usando i valori passati; restituisce ilProductID
valore del record appena inseritoUpdateProduct(productName, supplierID, categoryID, quantityPerUnit, unitPrice, unitsInStock, unitsOnOrder, reorderLevel, discontinued, productID)
aggiorna un prodotto esistente nel database usando i valori passati; restituisceTrue
se è stata aggiornata esattamente una riga, in caso contrario,False
DeleteProduct(productID)
elimina il prodotto specificato dal database
ProductsBLL.vb
Imports NorthwindTableAdapters
<System.ComponentModel.DataObject()> _
Public Class ProductsBLL
Private _productsAdapter As ProductsTableAdapter = Nothing
Protected ReadOnly Property Adapter() As ProductsTableAdapter
Get
If _productsAdapter Is Nothing Then
_productsAdapter = New ProductsTableAdapter()
End If
Return _productsAdapter
End Get
End Property
<System.ComponentModel.DataObjectMethodAttribute _
(System.ComponentModel.DataObjectMethodType.Select, True)> _
Public Function GetProducts() As Northwind.ProductsDataTable
Return Adapter.GetProducts()
End Function
<System.ComponentModel.DataObjectMethodAttribute _
(System.ComponentModel.DataObjectMethodType.Select, False)> _
Public Function GetProductByProductID(ByVal productID As Integer) _
As Northwind.ProductsDataTable
Return Adapter.GetProductByProductID(productID)
End Function
<System.ComponentModel.DataObjectMethodAttribute _
(System.ComponentModel.DataObjectMethodType.Select, False)> _
Public Function GetProductsByCategoryID(ByVal categoryID As Integer) _
As Northwind.ProductsDataTable
Return Adapter.GetProductsByCategoryID(categoryID)
End Function
<System.ComponentModel.DataObjectMethodAttribute _
(System.ComponentModel.DataObjectMethodType.Select, False)> _
Public Function GetProductsBySupplierID(ByVal supplierID As Integer) _
As Northwind.ProductsDataTable
Return Adapter.GetProductsBySupplierID(supplierID)
End Function
<System.ComponentModel.DataObjectMethodAttribute _
(System.ComponentModel.DataObjectMethodType.Insert, True)> _
Public Function AddProduct( _
productName As String, supplierID As Nullable(Of Integer), _
categoryID As Nullable(Of Integer), quantityPerUnit As String, _
unitPrice As Nullable(Of Decimal), unitsInStock As Nullable(Of Short), _
unitsOnOrder As Nullable(Of Short), reorderLevel As Nullable(Of Short), _
discontinued As Boolean) _
As Boolean
Dim products As New Northwind.ProductsDataTable()
Dim product As Northwind.ProductsRow = products.NewProductsRow()
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
products.AddProductsRow(product)
Dim rowsAffected As Integer = Adapter.Update(products)
Return rowsAffected = 1
End Function
<System.ComponentModel.DataObjectMethodAttribute _
(System.ComponentModel.DataObjectMethodType.Update, True)> _
Public Function UpdateProduct(_
productName As String, supplierID As Nullable(Of Integer), _
categoryID As Nullable(Of Integer), quantityPerUnit As String, _
unitPrice As Nullable(Of Decimal), unitsInStock As Nullable(Of Short), _
unitsOnOrder As Nullable(Of Short), reorderLevel As Nullable(Of Short), _
discontinued As Boolean, productID As Integer) _
As Boolean
Dim products As Northwind.ProductsDataTable = _
Adapter.GetProductByProductID(productID)
If products.Count = 0 Then
Return False
End If
Dim product as Northwind.ProductsRow = products(0)
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
Dim rowsAffected As Integer = Adapter.Update(product)
Return rowsAffected = 1
End Function
<System.ComponentModel.DataObjectMethodAttribute _
(System.ComponentModel.DataObjectMethodType.Delete, True)> _
Public Function DeleteProduct(ByVal productID As Integer) As Boolean
Dim rowsAffected As Integer = Adapter.Delete(productID)
Return rowsAffected = 1
End Function
End Class
I metodi che restituiscono semplicemente i dati GetProducts
, GetProductByProductID
, GetProductsByCategoryID
e GetProductBySuppliersID
sono abbastanza semplici come semplicemente chiamano nel DAL. Anche se in alcuni scenari potrebbero essere implementate regole aziendali che devono essere implementate a questo livello (ad esempio le regole di autorizzazione basate sull'utente attualmente connesso o sul ruolo a cui appartiene l'utente), si lascerà semplicemente questi metodi come è. Per questi metodi, il BLL funge semplicemente da proxy tramite il quale il livello di presentazione accede ai dati sottostanti dal livello di accesso ai dati.
I AddProduct
metodi e UpdateProduct
accettano entrambi come parametri i valori per i vari campi del prodotto e aggiungono rispettivamente un nuovo prodotto o aggiornano uno esistente. Poiché molte delle Product
colonne della tabella possono accettare valori (CategoryID
, SupplierID
, e UnitPrice
, per denominare NULL
alcuni), tali parametri di input per AddProduct
e UpdateProduct
che vengono mappati a tali colonne usano tipi nullable. I tipi nullable sono nuovi a .NET 2.0 e forniscono una tecnica per indicare se un tipo di valore deve invece essere Nothing
. Per altre informazioni, vedere la voce di blog di Paul VickThe Truth About Nullable Types e VB e la documentazione tecnica per la struttura Nullable .
Tutti e tre i metodi restituiscono un valore booleano che indica se una riga è stata inserita, aggiornata o eliminata perché l'operazione potrebbe non comportare una riga interessata. Ad esempio, se lo sviluppatore di pagine chiama DeleteProduct
un oggetto per un ProductID
prodotto non esistente, l'istruzione DELETE
rilasciata al database non avrà alcun effetto e pertanto il DeleteProduct
metodo restituirà False
.
Si noti che quando si aggiunge un nuovo prodotto o si aggiorna un oggetto esistente, si accettano i valori di campo del prodotto nuovo o modificato come elenco di scalari anziché accettare un'istanza ProductsRow
. Questo approccio è stato scelto perché la ProductsRow
classe deriva dalla classe ADO.NET DataRow
, che non ha un costruttore senza parametri predefinito. Per creare una nuova ProductsRow
istanza, è prima necessario creare un'istanza ProductsDataTable
e quindi richiamare il NewProductRow()
relativo metodo (che viene eseguito in AddProduct
). Questo problema si rivolge all'inserimento e all'aggiornamento dei prodotti usando ObjectDataSource. In breve, ObjectDataSource tenterà di creare un'istanza dei parametri di input. Se il metodo BLL prevede un'istanza ProductsRow
, ObjectDataSource tenterà di crearne uno, ma avrà esito negativo a causa della mancanza di un costruttore senza parametri predefinito. Per altre informazioni su questo problema, vedere i due post seguenti ASP.NET Forum: Aggiornamento di ObjectDataSources con Strongly-Typed DataSet e Problema con ObjectDataSource e Strongly-Typed DataSet.
Successivamente, sia in AddProduct
e UpdateProduct
, il codice crea un'istanza ProductsRow
e lo popola con i valori appena passati. Quando si assegnano valori a DataColumns di un oggetto DataRow possono verificarsi vari controlli di convalida a livello di campo. Pertanto, l'inserimento manuale dei valori passati in un DataRow consente di garantire la validità dei dati passati al metodo BLL. Purtroppo le classi DataRow fortemente tipizzate generate da Visual Studio non usano tipi nullable. Invece, per indicare che un determinato DataColumn in un DataRow deve corrispondere a un NULL
valore del database che è necessario usare il SetColumnNameNull()
metodo .
Nel UpdateProduct
primo caricamento nel prodotto per aggiornare l'uso di GetProductByProductID(productID)
. Anche se questo potrebbe sembrare un viaggio non necessario nel database, questo viaggio aggiuntivo sarà utile nelle esercitazioni future che esplorano la concorrenza ottimistica. La concorrenza ottimistica è una tecnica per garantire che due utenti che lavorano simultaneamente sugli stessi dati non sovrascrivono accidentalmente le modifiche di un altro. Il recupero dell'intero record semplifica anche la creazione di metodi di aggiornamento nel BLL che modificano solo un subset delle colonne di DataRow. Quando si esplora la SuppliersBLL
classe verrà visualizzato un esempio simile.
Si noti infine che la ProductsBLL
classe ha l'attributo DataObject applicato a esso (la [System.ComponentModel.DataObject]
sintassi prima dell'istruzione di classe nella parte superiore del file) e i metodi hanno attributi DataObjectMethodAttribute. L'attributo DataObject
contrassegna la classe come oggetto adatto per l'associazione a un controllo ObjectDataSource, mentre indica DataObjectMethodAttribute
lo scopo del metodo. Come vedremo nelle esercitazioni future, ASP.NET 2.0 ObjectDataSource semplifica l'accesso dichiarativo ai dati da una classe. Per filtrare l'elenco delle classi possibili da associare nella procedura guidata di ObjectDataSource, per impostazione predefinita solo le classi contrassegnate come DataObjects
vengono visualizzate nell'elenco a discesa della procedura guidata. La ProductsBLL
classe funzionerà anche senza questi attributi, ma aggiungendoli semplifica l'uso nella procedura guidata di ObjectDataSource.
Aggiunta delle altre classi
Con il completamento della ProductsBLL
classe, è comunque necessario aggiungere le classi per lavorare con categorie, fornitori e dipendenti. Per creare le classi e i metodi seguenti, usare i concetti dell'esempio precedente:
CategoriesBLL.cs
GetCategories()
GetCategoryByCategoryID(categoryID)
SuppliersBLL.cs
GetSuppliers()
GetSupplierBySupplierID(supplierID)
GetSuppliersByCountry(country)
UpdateSupplierAddress(supplierID, address, city, country)
EmployeesBLL.cs
GetEmployees()
GetEmployeeByEmployeeID(employeeID)
GetEmployeesByManager(managerID)
Il metodo che vale la pena notare è il SuppliersBLL
metodo della UpdateSupplierAddress
classe. Questo metodo fornisce un'interfaccia per l'aggiornamento solo delle informazioni sull'indirizzo del fornitore. Internamente, questo metodo legge nell'oggetto per l'oggetto SupplierDataRow
specificato supplierID
(usando GetSupplierBySupplierID
), imposta le relative proprietà correlate all'indirizzo e quindi chiama il SupplierDataTable
metodo nel metodo 's Update
. Il UpdateSupplierAddress
metodo segue:
<System.ComponentModel.DataObjectMethodAttribute _
(System.ComponentModel.DataObjectMethodType.Update, True)> _
Public Function UpdateSupplierAddress(ByVal supplierID As Integer, _
ByVal address As String, ByVal city As String, ByVal country As String) _
As Boolean
Dim suppliers As Northwind.SuppliersDataTable = _
Adapter.GetSupplierBySupplierID(supplierID)
If suppliers.Count = 0 Then
Return False
Else
Dim supplier As Northwind.SuppliersRow = suppliers(0)
If address Is Nothing Then
supplier.SetAddressNull()
Else
supplier.Address = address
End If
If city Is Nothing Then
supplier.SetCityNull()
Else
supplier.City = city
End If
If country Is Nothing Then
supplier.SetCountryNull()
Else
supplier.Country = country
End If
Dim rowsAffected As Integer = Adapter.Update(supplier)
Return rowsAffected = 1
End If
End Function
Vedere il download di questo articolo per l'implementazione completa delle classi BLL.
Passaggio 2: Accesso ai set di dati tipiti tramite le classi BLL
Nella prima esercitazione sono stati illustrati esempi di utilizzo diretto con Il set di dati tipizzato a livello di codice, ma con l'aggiunta delle classi BLL, il livello di presentazione deve funzionare rispetto al BLL. Nell'esempio AllProducts.aspx
della prima esercitazione, l'oggetto ProductsTableAdapter
è stato usato per associare l'elenco di prodotti a un oggetto GridView, come illustrato nel codice seguente:
Dim productsAdapter As New ProductsTableAdapter()
GridView1.DataSource = productsAdapter.GetProducts()
GridView1.DataBind()
Per usare le nuove classi BLL, tutto ciò che deve essere modificato è la prima riga di codice semplicemente sostituire l'oggetto ProductsTableAdapter
con un ProductBLL
oggetto:
Dim productLogic As New ProductsBLL()
GridView1.DataSource = productLogic.GetProducts()
GridView1.DataBind()
Le classi BLL possono anche essere accessibili in modo dichiarativo (come può il DataSet tipizzato) usando ObjectDataSource. Verranno descritti in dettaglio ObjectDataSource nelle esercitazioni seguenti.
Figura 3: l'elenco dei prodotti viene visualizzato in Un controllo GridView (Fare clic per visualizzare l'immagine a dimensioni complete)
Passaggio 3: Aggiunta della convalida Field-Level alle classi DataRow
La convalida a livello di campo è verifica che riguarda i valori delle proprietà degli oggetti business durante l'inserimento o l'aggiornamento. Alcune regole di convalida a livello di campo per i prodotti includono:
- Il
ProductName
campo deve essere di 40 caratteri o minore in lunghezza - Il
QuantityPerUnit
campo deve essere di 20 caratteri o minore in lunghezza - I
ProductID
campi ,ProductName
eDiscontinued
sono obbligatori, ma tutti gli altri campi sono facoltativi - I
UnitPrice
campi , ,UnitsInStock
UnitsOnOrder
eReorderLevel
devono essere maggiori o uguali a zero
Queste regole possono e devono essere espresse a livello di database. Il limite di caratteri nei ProductName
campi e QuantityPerUnit
viene acquisito rispettivamente dai tipi di dati di tali colonne nella Products
tabella (nvarchar(40)
e nvarchar(20)
, ). Se i campi sono obbligatori e facoltativi sono espressi da se la colonna della tabella di database consente NULL
s. Esistono quattro vincoli di controllo che garantiscono che solo i valori maggiori o uguali a zero possano renderli nelle UnitPrice
colonne , , UnitsInStock
UnitsOnOrder
o ReorderLevel
.
Oltre a applicare queste regole al database, devono essere applicate anche a livello di DataSet. In effetti, la lunghezza del campo e se un valore è obbligatorio o facoltativo sono già acquisiti per il set di DataTable di DataColumns. Per visualizzare automaticamente la convalida a livello di campo esistente, passare all'Designer DataSet, selezionare un campo da una delle tabelle dati e quindi passare alla Finestra Proprietà. Come illustrato nella figura 4, DataColumn QuantityPerUnit
in ProductsDataTable
ha una lunghezza massima di 20 caratteri e consente NULL
valori. Se si tenta di impostare la ProductsDataRow
proprietà 's QuantityPerUnit
su un valore di stringa più di 20 caratteri, verrà generato un ArgumentException
valore di stringa.
Figura 4: DataColumn fornisce la convalida di base Field-Level (fare clic per visualizzare l'immagine full-size)
Sfortunatamente, non è possibile specificare i controlli dei limiti, ad esempio il valore deve essere maggiore o uguale a zero, attraverso la UnitPrice
Finestra Proprietà. Per fornire questo tipo di convalida a livello di campo, è necessario creare un gestore eventi per l'evento ColumnChanging di DataTable. Come accennato nell'esercitazione precedente, è possibile estendere gli oggetti DataSet, DataTables e DataRow creati da DataSet tipizzati tramite l'uso di classi parziali. Usando questa tecnica è possibile creare un ColumnChanging
gestore eventi per la ProductsDataTable
classe. Iniziare creando una classe nella App_Code
cartella denominata ProductsDataTable.ColumnChanging.vb
.
Figura 5: Aggiungere una nuova classe alla cartella (fare clic per visualizzare l'immagineApp_Code
full-size)
Creare quindi un gestore eventi per l'evento ColumnChanging
che garantisce che i UnitPrice
valori di colonna , , UnitsInStock
UnitsOnOrder
e ReorderLevel
(se non NULL
) siano maggiori o uguali a zero. Se una colonna di questo tipo non è compreso nell'intervallo, generare un ArgumentException
oggetto .
ProductsDataTable.ColumnChanging.vb
Imports System.data
Partial Public Class Northwind
Partial Public Class ProductsDataTable
Public Overrides Sub BeginInit()
AddHandler Me.ColumnChanging, AddressOf ValidateColumn
End Sub
Sub ValidateColumn(sender As Object, e As DataColumnChangeEventArgs)
If e.Column.Equals(Me.UnitPriceColumn) Then
If Not Convert.IsDBNull(e.ProposedValue) AndAlso _
CType(e.ProposedValue, Decimal) < 0 Then
Throw New ArgumentException( _
"UnitPrice cannot be less than zero", "UnitPrice")
End If
ElseIf e.Column.Equals(Me.UnitsInStockColumn) OrElse _
e.Column.Equals(Me.UnitsOnOrderColumn) OrElse _
e.Column.Equals(Me.ReorderLevelColumn) Then
If Not Convert.IsDBNull(e.ProposedValue) AndAlso _
CType(e.ProposedValue, Short) < 0 Then
Throw New ArgumentException(String.Format( _
"{0} cannot be less than zero", e.Column.ColumnName), _
e.Column.ColumnName)
End If
End If
End Sub
End Class
End Class
Passaggio 4: Aggiunta di regole business personalizzate alle classi BLL
Oltre alla convalida a livello di campo, è possibile che siano presenti regole business personalizzate di alto livello che coinvolgono entità o concetti diversi non esprimebili a livello di colonna singola, ad esempio:
- Se un prodotto viene interrotto, non è possibile aggiornarlo
UnitPrice
- Il paese di residenza di un dipendente deve essere lo stesso del paese di residenza del responsabile
- Un prodotto non può essere interrotto se è l'unico prodotto fornito dal fornitore
Le classi BLL devono contenere controlli per garantire l'conformità alle regole aziendali dell'applicazione. Questi controlli possono essere aggiunti direttamente ai metodi a cui si applicano.
Si supponga che le nostre regole aziendali determinino che un prodotto non poteva essere contrassegnato come interrotto se fosse l'unico prodotto di un determinato fornitore. Vale a dire, se il prodotto X era l'unico prodotto acquistato dal fornitore Y, non potevamo contrassegnare X come interrotto; se, tuttavia, il fornitore Y ci ha fornito tre prodotti, A, B e C, allora potremmo contrassegnare qualsiasi e tutti questi come interrotti. Una regola di business strana, ma le regole aziendali e il senso comune non sono sempre allineati!
Per applicare questa regola aziendale nel UpdateProducts
metodo che si inizierà controllando se Discontinued
è stato impostato su True
e, in caso affermativo, si chiamerà GetProductsBySupplierID
per determinare il numero di prodotti acquistati dal fornitore di questo prodotto. Se viene acquistato un solo prodotto da questo fornitore, viene generato un ApplicationException
oggetto .
<System.ComponentModel.DataObjectMethodAttribute_
(System.ComponentModel.DataObjectMethodType.Update, True)> _
Public Function UpdateProduct( _
productName As String, supplierID As Nullable(Of Integer), _
categoryID As Nullable(Of Integer), quantityPerUnit As String, _
unitPrice As Nullable(Of Decimal), unitsInStock As Nullable(Of Short), _
unitsOnOrder As Nullable(Of Short), reorderLevel As Nullable(Of Short), _
discontinued As Boolean, productID As Integer) _
As Boolean
Dim products As Northwind.ProductsDataTable = _
Adapter.GetProductByProductID(productID)
If products.Count = 0 Then
Return False
End If
Dim product As Northwind.ProductsRow = products(0)
If discontinued Then
Dim productsBySupplier As Northwind.ProductsDataTable = _
Adapter.GetProductsBySupplierID(product.SupplierID)
If productsBySupplier.Count = 1 Then
Throw New ApplicationException( _
"You cannot mark a product as discontinued if it is " & _
"the only product purchased from a supplier")
End If
End If
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
Dim rowsAffected As Integer = Adapter.Update(product)
Return rowsAffected = 1
End Function
Risposta agli errori di convalida nel livello presentazione
Quando si chiama il BLL dal livello di presentazione, è possibile decidere se tentare di gestire eventuali eccezioni che potrebbero essere generate o lasciarle bollere fino a ASP.NET (che genererà l'evento HttpApplication
's).Error
Per gestire un'eccezione quando si usa BLL a livello di codice, è possibile usare un oggetto Try... Blocco Catch , come illustrato nell'esempio seguente:
Dim productLogic As New ProductsBLL()
Try
productLogic.UpdateProduct("Scotts Tea", 1, 1, Nothing, _
-14, 10, Nothing, Nothing, False, 1)
Catch ae As ArgumentException
Response.Write("There was a problem: " & ae.Message)
End Try
Come si vedrà nelle esercitazioni future, la gestione delle eccezioni che si spostano dall'BLL quando si usa un controllo Web dati per l'inserimento, l'aggiornamento o l'eliminazione dei dati possono essere gestiti direttamente in un gestore eventi anziché dover eseguire il wrapping del codice in Try...Catch
blocchi.
Riepilogo
Un'applicazione ben progettata viene creata in livelli distinti, ognuno dei quali incapsula un ruolo specifico. Nella prima esercitazione di questa serie di articoli è stato creato un livello di accesso ai dati usando Tipid DataSets; in questa esercitazione è stato creato un livello di logica di business come una serie di classi nella cartella dell'applicazione App_Code
che chiamano il servizio nel servizio di distribuzione. BLL implementa la logica a livello di campo e di business per l'applicazione. Oltre a creare un BLL separato, come illustrato in questa esercitazione, un'altra opzione consiste nell'estendere i metodi di TableAdapters tramite l'uso di classi parziali. Tuttavia, l'uso di questa tecnica non consente di eseguire l'override dei metodi esistenti né di separare il servizio DAL e il BLL in modo pulito come l'approccio adottato in questo articolo.
Con il completamento di DAL e BLL, è possibile iniziare a usare il livello di presentazione. Nell'esercitazione successiva si esaminerà una breve deviazione dagli argomenti di accesso ai dati e si definirà un layout di pagina coerente da usare in tutte le esercitazioni.
Programmazione felice!
Informazioni sull'autore
Scott Mitchell, autore di sette libri ASP/ASP.NET e fondatore di 4GuysFromRolla.com, ha lavorato con le tecnologie Microsoft Web dal 1998. Scott lavora come consulente indipendente, allenatore e scrittore. Il suo ultimo libro è Sams Teach Yourself ASP.NET 2,0 in 24 Ore. Può essere raggiunto a mitchell@4GuysFromRolla.com. o tramite il suo blog, che può essere trovato in http://ScottOnWriting.NET.
Grazie speciali
Questa serie di esercitazioni è stata esaminata da molti revisori utili. I revisori principali per questa esercitazione sono stati Liz Shulok, Dennis Patterson, Carlos Santos e Hilton Giesenow. Interessati a esaminare i prossimi articoli MSDN? In tal caso, lasciami una riga in mitchell@4GuysFromRolla.com.