Nota
L'accesso a questa pagina richiede l'autorizzazione. È possibile provare ad accedere o modificare le directory.
L'accesso a questa pagina richiede l'autorizzazione. È possibile provare a modificare le directory.
In questo tutorial vedremo come centralizzare le regole aziendali in un Business Logic Layer (BLL) che funge da intermediario per lo scambio di dati tra il livello di presentazione e il DAL.
Introduzione
Il livello di accesso ai dati (DAL) creato nella prima esercitazione separa in modo netto la logica di accesso ai dati dalla logica di presentazione. Tuttavia, sebbene il DAL separi in modo netto i dettagli di accesso ai dati dal livello di presentazione, non applica alcuna regola aziendale che potrebbe essere applicata. Ad esempio, per la nostra applicazione potremmo voler impedire la CategoryID
modifica dei SupplierID
campi o Products
della tabella quando il Discontinued
campo è impostato su 1, oppure potremmo voler applicare le regole di anzianità, vietando le situazioni in cui un dipendente è gestito da qualcuno che è stato assunto dopo di lui. Un altro scenario comune è l'autorizzazione, forse solo gli utenti con un particolare ruolo possono eliminare i prodotti o possono modificare il UnitPrice
valore.
In questo tutorial vedremo come centralizzare queste regole di business in un Business Logic Layer (BLL) 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 questi tutorial implementeremo il BLL come una serie di classi nella nostra App_Code
cartella per semplificare la struttura del progetto. Nella Figura 1 vengono illustrate le relazioni architetturali tra il livello di presentazione, BLL e DAL.
Figura 1: Il BLL separa il livello di presentazione dal livello di accesso ai dati e impone regole di 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 nostro BLL sarà composto da quattro classi, una per ogni TableAdapter nel DAL; ognuna di queste classi BLL disporrà di metodi per il recupero, l'inserimento, l'aggiornamento e l'eliminazione dal rispettivo TableAdapter nel DAL, applicando le regole di business appropriate.
Per separare in modo più netto le classi correlate a DAL e BLL, creiamo due sottocartelle nella App_Code
cartella DAL
e BLL
. È sufficiente fare clic con il pulsante destro del App_Code
mouse sulla cartella in Esplora soluzioni e scegliere Nuova cartella. Dopo aver creato queste due cartelle, spostare il DataSet tipizzato creato nella prima esercitazione nella DAL
sottocartella.
Quindi, crea i quattro file di BLL
classe BLL nella sottocartella. Per fare ciò, fai clic con il pulsante destro del BLL
mouse sulla sottocartella, scegli Aggiungi un nuovo elemento e scegli il modello di classe. Assegna un nome alle quattro classi ProductsBLL
, CategoriesBLL
, SuppliersBLL
, e EmployeesBLL
.
Figura 2: Aggiunta di quattro nuove classi alla App_Code
cartella
Successivamente, aggiungiamo metodi a ciascuna delle classi per eseguire semplicemente il wrapping dei metodi definiti per i TableAdapter dalla prima esercitazione. Per ora, questi metodi chiameranno direttamente il DAL; Torneremo più avanti per aggiungere la logica di business necessaria.
Annotazioni
Se si utilizza Visual Studio Standard Edition o versione successiva, ovvero non si utilizza Visual Web Developer, è possibile progettare le classi in modo visivo utilizzando Progettazione classi. Per altre informazioni su questa nuova funzionalità di Visual Studio, vedere il blog di Progettazione classi .
Per la ProductsBLL
classe dobbiamo aggiungere un totale di sette metodi:
-
GetProducts()
Restituisce tutti i prodotti -
GetProductByProductID(productID)
restituisce il prodotto con l'ID prodotto specificato -
GetProductsByCategoryID(categoryID)
restituisce tutti i prodotti della categoria specificata -
GetProductsBySupplier(supplierID)
Restituisce tutti i prodotti dal fornitore specificato -
AddProduct(productName, supplierID, categoryID, quantityPerUnit, unitPrice, unitsInStock, unitsOnOrder, reorderLevel, discontinued)
inserisce un nuovo prodotto nel database utilizzando i valori passati; Restituisce ilProductID
valore del record appena inserito -
UpdateProduct(productName, supplierID, categoryID, quantityPerUnit, unitPrice, unitsInStock, unitsOnOrder, reorderLevel, discontinued, productID)
aggiorna un prodotto esistente nel database utilizzando i valori passati; restituisceTrue
se è stata aggiornata esattamente una riga,False
altrimenti -
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 in quanto chiamano semplicemente il DAL. Anche se in alcuni scenari potrebbero essere presenti regole di business che devono essere implementate a questo livello (ad esempio regole di autorizzazione basate sull'utente attualmente connesso o sul ruolo a cui l'utente appartiene), lasceremo semplicemente questi metodi as-is. Per questi metodi, quindi, il BLL funge semplicemente da proxy attraverso il quale il livello di presentazione accede ai dati sottostanti dal livello di accesso ai dati.
I AddProduct
metodi and UpdateProduct
prendono entrambi come parametri i valori per i vari campi del prodotto e aggiungono un nuovo prodotto o ne aggiornano uno esistente, rispettivamente. Poiché molte delle colonne della Product
tabella possono accettare NULL
valori (CategoryID
, SupplierID
, e UnitPrice
, per citarne alcuni), i parametri di input per AddProduct
e UpdateProduct
che eseguono il mapping a tali colonne utilizzano tipi nullable. I tipi nullable sono una novità di .NET 2.0 e forniscono una tecnica per indicare se un tipo di valore deve essere Nothing
. Per ulteriori informazioni, fare riferimento al post del blog di Paul VickThe Truth About Nullable Types and VB e alla 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, poiché l'operazione potrebbe non generare una riga interessata. Ad esempio, se lo sviluppatore della pagina chiama DeleteProduct
il passaggio di a ProductID
per un prodotto inesistente, l'istruzione DELETE
emessa nel database non avrà alcun effetto e pertanto il DeleteProduct
metodo restituirà False
.
Si noti che quando si aggiunge un nuovo prodotto o si aggiorna uno esistente, i valori dei campi del prodotto nuovo o modificato vengono presi in considerazione come un elenco di scalari, anziché accettare un'istanza ProductsRow
. Questo approccio è stato scelto perché la ProductsRow
classe deriva dalla classe ADO.NET DataRow
, che non dispone di un costruttore senza parametri predefinito. Per creare una nuova ProductsRow
istanza, dobbiamo prima creare un'istanza ProductsDataTable
e poi invocare il suo NewProductRow()
metodo (cosa che facciamo in AddProduct
). Questa lacuna si manifesta quando si passa all'inserimento e all'aggiornamento dei prodotti utilizzando 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 una, ma non riuscirà a causa della mancanza di un costruttore senza parametri predefinito. Per ulteriori informazioni su questo problema, fare riferimento ai seguenti due post del forum ASP.NET: Aggiornamento di ObjectDataSources con Strongly-Typed DataSet e Problema con ObjectDataSource e Strongly-Typed DataSet.
Successivamente, in both e AddProduct
UpdateProduct
, il codice crea un'istanza ProductsRow
e la popola con i valori appena passati. Quando si assegnano valori a DataColumns di un 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. Sfortunatamente, le classi DataRow fortemente tipizzate generate da Visual Studio non utilizzano tipi nullable. Piuttosto, per indicare che un particolare DataColumn in un DataRow deve corrispondere a un NULL
valore di database, è necessario utilizzare il SetColumnNameNull()
metodo.
In UpdateProduct
prima cosa carichiamo il prodotto per l'aggiornamento utilizzando GetProductByProductID(productID)
. Anche se questo può sembrare un viaggio non necessario nel database, questo viaggio extra si rivelerà utile nelle esercitazioni future che esplorano la concorrenza ottimistica. La concorrenza ottimistica è una tecnica per garantire che due utenti che lavorano contemporaneamente sugli stessi dati non sovrascrivano accidentalmente le modifiche dell'altro. L'acquisizione dell'intero record semplifica anche la creazione di metodi di aggiornamento nel BLL che modificano solo un sottoinsieme delle colonne di DataRow. Quando esploreremo la SuppliersBLL
classe, vedremo un esempio del genere.
Infine, si noti che alla ProductsBLL
classe è applicato l'attributo DataObject (la [System.ComponentModel.DataObject]
sintassi subito prima dell'istruzione class nella parte superiore del file) e che i metodi hanno attributi DataObjectMethodAttribute. L'attributo DataObject
contrassegna la classe come un oggetto adatto per l'associazione a un controllo ObjectDataSource, mentre indica DataObjectMethodAttribute
lo scopo del metodo. Come vedremo nelle esercitazioni future, ObjectDataSource di ASP.NET 2.0 semplifica l'accesso dichiarativo ai dati da una classe. Per filtrare l'elenco delle possibili classi a cui eseguire l'associazione nella procedura guidata di ObjectDataSource, per impostazione predefinita nell'elenco a discesa della procedura guidata vengono visualizzate solo le classi contrassegnate come DataObjects
contrassegnate. La ProductsBLL
classe funzionerà altrettanto bene senza questi attributi, ma la loro aggiunta semplifica l'utilizzo nella procedura guidata di ObjectDataSource.
Aggiunta delle altre classi
Una volta completata la ProductsBLL
classe, dobbiamo ancora aggiungere le classi per lavorare con categorie, fornitori e dipendenti. Prenditi un momento per creare le classi e i metodi seguenti utilizzando 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)
L'unico metodo degno di SuppliersBLL
nota è il metodo della UpdateSupplierAddress
classe. Questo metodo fornisce un'interfaccia per aggiornare solo le informazioni sull'indirizzo del fornitore. Internamente, questo metodo legge l'oggetto per l'oggetto SupplierDataRow
specificato supplierID
(using GetSupplierBySupplierID
), imposta le proprietà correlate all'indirizzo e quindi chiama il SupplierDataTable
metodo 's Update
. Il UpdateSupplierAddress
metodo è il seguente:
<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
Fare riferimento al download di questo articolo per l'implementazione completa delle classi BLL.
Passaggio 2: Accesso ai DataSet tipizzati tramite le classi BLL
Nella prima esercitazione sono stati illustrati esempi di utilizzo diretto del DataSet tipizzato a livello di codice, ma con l'aggiunta delle classi BLL, il livello di presentazione dovrebbe invece funzionare contro il BLL. Nell'esempio AllProducts.aspx
della prima esercitazione, è ProductsTableAdapter
stato usato per associare l'elenco di prodotti a un controllo GridView, come illustrato nel codice seguente:
Dim productsAdapter As New ProductsTableAdapter()
GridView1.DataSource = productsAdapter.GetProducts()
GridView1.DataBind()
Per utilizzare le nuove classi BLL, tutto ciò che deve essere modificato è la prima riga di codice: basta sostituire l'oggetto ProductsTableAdapter
con un ProductBLL
oggetto:
Dim productLogic As New ProductsBLL()
GridView1.DataSource = productLogic.GetProducts()
GridView1.DataBind()
È inoltre possibile accedere alle classi BLL in modo dichiarativo (come il DataSet tipizzato) utilizzando ObjectDataSource. Verrà illustrato in modo più dettagliato ObjectDataSource nelle esercitazioni seguenti.
L'elenco dei prodotti viene visualizzato in un GridView
Figura 3: L'elenco dei prodotti viene visualizzato in un GridView (fare clic per visualizzare l'immagine a dimensione intera)
Passaggio 3: Aggiunta della convalida Field-Level alle classi DataRow
La convalida a livello di campo è un controllo relativo ai 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 avere una lunghezza massima di 40 caratteri - Il
QuantityPerUnit
campo deve avere una lunghezza massima di 20 caratteri - 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 per i ProductName
campi e QuantityPerUnit
viene acquisito dai tipi di dati di tali colonne nella Products
tabella (nvarchar(40)
e nvarchar(20)
, rispettivamente). Se i campi sono obbligatori e facoltativi sono espressi da se la colonna della tabella del database consente NULL
s. Esistono quattro vincoli CHECK che garantiscono che solo i UnitPrice
valori maggiori o uguali a zero possano essere inseriti nelle colonne , UnitsInStock
, UnitsOnOrder
, o ReorderLevel
.
Oltre a far rispettare queste regole nel database, dovrebbero 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 DataColumns di ogni DataTable. Per visualizzare la convalida a livello di campo esistente fornita automaticamente, accedere a DataSet Designer, selezionare un campo da una delle DataTable e quindi passare alla finestra Proprietà. Come illustrato nella Figura 4, l'oggetto QuantityPerUnit
DataColumn ha ProductsDataTable
una lunghezza massima di 20 caratteri e consente NULL
valori. Se tentiamo di impostare la ProductsDataRow
proprietà 's QuantityPerUnit
su un valore stringa più lungo di 20 caratteri, verrà lanciato un an ArgumentException
.
Figura 4: DataColumn fornisce la convalida di base dell'Field-Level (fare clic per visualizzare l'immagine a dimensione intera)
Sfortunatamente, non possiamo specificare i controlli dei limiti, ad esempio il UnitPrice
valore deve essere maggiore o uguale a zero, tramite la 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, gli oggetti DataSet, DataTable e DataRow creati dal DataSet tipizzato possono essere estesi tramite l'utilizzo di classi parziali. Utilizzando questa tecnica possiamo creare un ColumnChanging
gestore di eventi per la ProductsDataTable
classe. Inizia creando una classe nella App_Code
cartella denominata ProductsDataTable.ColumnChanging.vb
.
Figura 5: Aggiunta di una nuova classe alla cartella (App_Code
a dimensione intera)
Creare quindi un gestore eventi per l'evento ColumnChanging
che garantisca che i valori , UnitPrice
, UnitsInStock
e UnitsOnOrder
di ReorderLevel
colonna (se non NULL
) siano maggiori o uguali a zero. Se una colonna di questo tipo non è compresa nell'intervallo, lanciare un ArgumentException
file .
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 aziendali personalizzate alle classi del BLL
Oltre alla convalida a livello di campo, possono essere presenti regole aziendali personalizzate di alto livello che coinvolgono diverse entità o concetti non esprimibili a livello di singola colonna, ad esempio:
- Se un prodotto è fuori produzione, non può
UnitPrice
essere aggiornato - Il paese di residenza di un dipendente deve essere lo stesso del paese di residenza del suo manager
- Un prodotto non può essere interrotto se è l'unico prodotto fornito dal fornitore
Le classi BLL devono contenere controlli per garantire la conformità alle regole aziendali dell'applicazione. Questi controlli possono essere aggiunti direttamente ai metodi a cui si applicano.
Immaginate che le nostre regole aziendali impongano che un prodotto non possa essere contrassegnato come fuori produzione se fosse l'unico prodotto di un determinato fornitore. Ovvero, se il prodotto X fosse l'unico prodotto che abbiamo acquistato dal fornitore Y, non potremmo contrassegnare X come fuori produzione; se, tuttavia, il fornitore Y ci forniva tre prodotti, A, B e C, potevamo contrassegnare tutti questi prodotti come fuori produzione. Una strana regola aziendale, ma le regole aziendali e il buon senso non sono sempre allineati!
Per applicare questa regola aziendale nel UpdateProducts
metodo, iniziamo controllando se Discontinued
è stato impostato su True
e, in caso affermativo, chiamiamo GetProductsBySupplierID
per determinare quanti prodotti abbiamo acquistato dal fornitore di questo prodotto. Se viene acquistato un solo prodotto da questo fornitore, lanciamo un file .ApplicationException
<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 di presentazione
Quando si chiama il BLL dal livello di presentazione, è possibile decidere se tentare di gestire eventuali eccezioni che potrebbero essere generate o lasciarle propagarsi fino a ASP.NET (che genererà l'evento HttpApplication
's Error
). Per gestire un'eccezione quando si lavora con il BLL a livello di codice, è possibile utilizzare un metodo Try... 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 vedremo nelle esercitazioni future, la gestione delle eccezioni che si propagano dal BLL quando si utilizza un controllo Web dati per l'inserimento, l'aggiornamento o l'eliminazione di dati può essere gestita direttamente in un gestore eventi anziché dover eseguire il wrapping del codice in Try...Catch
blocchi.
Riassunto
Un'applicazione ben progettata è realizzata in livelli distinti, ognuno dei quali racchiude un ruolo particolare. Nel primo tutorial di questa serie di articoli abbiamo creato un livello di accesso ai dati utilizzando DataSet tipizzati; in questo tutorial abbiamo costruito un livello di logica di business come una serie di classi nella cartella dell'applicazione App_Code
che chiamano il nostro DAL. Il BLL implementa la logica a livello di campo e a livello di business per la nostra applicazione. Oltre a creare un BLL separato, come abbiamo fatto in questa esercitazione, un'altra opzione consiste nell'estendere i metodi di TableAdapter tramite l'uso di classi parziali. Tuttavia, l'utilizzo di questa tecnica non ci consente di sovrascrivere i metodi esistenti né separa il nostro DAL e il nostro BLL in modo così netto come l'approccio che abbiamo adottato in questo articolo.
Con il DAL e il BLL completi, siamo pronti per iniziare il nostro livello di presentazione. Nell'esercitazione successiva verrà fatta una breve deviazione dagli argomenti relativi all'accesso ai dati e verrà definito un layout di pagina coerente da utilizzare in tutte le esercitazioni.
Buon programmatori!
Informazioni sull'autore
Scott Mitchell, autore di sette libri ASP/ASP.NET e fondatore di 4GuysFromRolla.com, ha lavorato con le tecnologie Web Microsoft dal 1998. Scott lavora come consulente indipendente, formatore e scrittore. Il suo ultimo libro è Sams Teach Yourself ASP.NET 2.0 in 24 ore. Può essere raggiunto a mitchell@4GuysFromRolla.com.
Grazie speciale a
Questa serie di esercitazioni è stata esaminata da molti revisori competenti. I revisori principali di questo tutorial sono stati Liz Shulok, Dennis Patterson, Carlos Santos e Hilton Giesenow. Si è interessati a esaminare i prossimi articoli MSDN? In tal caso, mandami un messaggio a mitchell@4GuysFromRolla.com.