Creazione di un livello per la logica di business (C#)
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
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.cs
using System;
using System.Data;
using System.Configuration;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Web.UI.HtmlControls;
using NorthwindTableAdapters;
[System.ComponentModel.DataObject]
public class ProductsBLL
{
private ProductsTableAdapter _productsAdapter = null;
protected ProductsTableAdapter Adapter
{
get {
if (_productsAdapter == null)
_productsAdapter = new ProductsTableAdapter();
return _productsAdapter;
}
}
[System.ComponentModel.DataObjectMethodAttribute
(System.ComponentModel.DataObjectMethodType.Select, true)]
public Northwind.ProductsDataTable GetProducts()
{
return Adapter.GetProducts();
}
[System.ComponentModel.DataObjectMethodAttribute
(System.ComponentModel.DataObjectMethodType.Select, false)]
public Northwind.ProductsDataTable GetProductByProductID(int productID)
{
return Adapter.GetProductByProductID(productID);
}
[System.ComponentModel.DataObjectMethodAttribute
(System.ComponentModel.DataObjectMethodType.Select, false)]
public Northwind.ProductsDataTable GetProductsByCategoryID(int categoryID)
{
return Adapter.GetProductsByCategoryID(categoryID);
}
[System.ComponentModel.DataObjectMethodAttribute
(System.ComponentModel.DataObjectMethodType.Select, false)]
public Northwind.ProductsDataTable GetProductsBySupplierID(int supplierID)
{
return Adapter.GetProductsBySupplierID(supplierID);
}
[System.ComponentModel.DataObjectMethodAttribute
(System.ComponentModel.DataObjectMethodType.Insert, true)]
public bool AddProduct(string productName, int? supplierID, int? categoryID,
string quantityPerUnit, decimal? unitPrice, short? unitsInStock,
short? unitsOnOrder, short? reorderLevel, bool discontinued)
{
// Create a new ProductRow instance
Northwind.ProductsDataTable products = new Northwind.ProductsDataTable();
Northwind.ProductsRow product = products.NewProductsRow();
product.ProductName = productName;
if (supplierID == null) product.SetSupplierIDNull();
else product.SupplierID = supplierID.Value;
if (categoryID == null) product.SetCategoryIDNull();
else product.CategoryID = categoryID.Value;
if (quantityPerUnit == null) product.SetQuantityPerUnitNull();
else product.QuantityPerUnit = quantityPerUnit;
if (unitPrice == null) product.SetUnitPriceNull();
else product.UnitPrice = unitPrice.Value;
if (unitsInStock == null) product.SetUnitsInStockNull();
else product.UnitsInStock = unitsInStock.Value;
if (unitsOnOrder == null) product.SetUnitsOnOrderNull();
else product.UnitsOnOrder = unitsOnOrder.Value;
if (reorderLevel == null) product.SetReorderLevelNull();
else product.ReorderLevel = reorderLevel.Value;
product.Discontinued = discontinued;
// Add the new product
products.AddProductsRow(product);
int rowsAffected = Adapter.Update(products);
// Return true if precisely one row was inserted,
// otherwise false
return rowsAffected == 1;
}
[System.ComponentModel.DataObjectMethodAttribute
(System.ComponentModel.DataObjectMethodType.Update, true)]
public bool UpdateProduct(string productName, int? supplierID, int? categoryID,
string quantityPerUnit, decimal? unitPrice, short? unitsInStock,
short? unitsOnOrder, short? reorderLevel, bool discontinued, int productID)
{
Northwind.ProductsDataTable products = Adapter.GetProductByProductID(productID);
if (products.Count == 0)
// no matching record found, return false
return false;
Northwind.ProductsRow product = products[0];
product.ProductName = productName;
if (supplierID == null) product.SetSupplierIDNull();
else product.SupplierID = supplierID.Value;
if (categoryID == null) product.SetCategoryIDNull();
else product.CategoryID = categoryID.Value;
if (quantityPerUnit == null) product.SetQuantityPerUnitNull();
else product.QuantityPerUnit = quantityPerUnit;
if (unitPrice == null) product.SetUnitPriceNull();
else product.UnitPrice = unitPrice.Value;
if (unitsInStock == null) product.SetUnitsInStockNull();
else product.UnitsInStock = unitsInStock.Value;
if (unitsOnOrder == null) product.SetUnitsOnOrderNull();
else product.UnitsOnOrder = unitsOnOrder.Value;
if (reorderLevel == null) product.SetReorderLevelNull();
else product.ReorderLevel = reorderLevel.Value;
product.Discontinued = discontinued;
// Update the product record
int rowsAffected = Adapter.Update(product);
// Return true if precisely one row was updated,
// otherwise false
return rowsAffected == 1;
}
[System.ComponentModel.DataObjectMethodAttribute
(System.ComponentModel.DataObjectMethodType.Delete, true)]
public bool DeleteProduct(int productID)
{
int rowsAffected = Adapter.Delete(productID);
// Return true if precisely one row was deleted,
// otherwise false
return rowsAffected == 1;
}
}
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 null
. In C# è possibile contrassegnare un tipo di valore come tipo nullable aggiungendo ?
dopo il tipo (ad esempio int? x;
). Per altre informazioni, vedere la sezione Tipi nullable nella Guida per programmatori C# .
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 bool UpdateSupplierAddress
(int supplierID, string address, string city, string country)
{
Northwind.SuppliersDataTable suppliers =
Adapter.GetSupplierBySupplierID(supplierID);
if (suppliers.Count == 0)
// no matching record found, return false
return false;
else
{
Northwind.SuppliersRow supplier = suppliers[0];
if (address == null) supplier.SetAddressNull();
else supplier.Address = address;
if (city == null) supplier.SetCityNull();
else supplier.City = city;
if (country == null) supplier.SetCountryNull();
else supplier.Country = country;
// Update the supplier Address-related information
int rowsAffected = Adapter.Update(supplier);
// Return true if precisely one row was updated,
// otherwise false
return rowsAffected == 1;
}
}
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:
ProductsTableAdapter productsAdapter = 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:
ProductsBLL productLogic = 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)
). Indica se i campi sono obbligatori e facoltativi sono espressi da se la colonna della tabella di database è consentita NULL
. Esistono quattro vincoli CHECK che assicurano 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, è necessario applicare anche a livello di DataSet. Infatti, 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 alla Designer DataSet, selezionare un campo da una delle tabelle DataTable 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à di su QuantityPerUnit
un valore stringa più lungo di 20 caratteri, verrà generata un'eccezione ArgumentException
.
Figura 4: DataColumn fornisce la convalida Field-Level di base (fare clic per visualizzare l'immagine a dimensione intera)
Sfortunatamente, non è possibile specificare controlli dei limiti, ad esempio il UnitPrice
valore deve essere maggiore o uguale a zero, tramite il 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 . Per iniziare, creare una classe nella App_Code
cartella denominata ProductsDataTable.ColumnChanging.cs
.
Figura 5: Aggiungere una nuova classe alla App_Code
cartella (fare clic per visualizzare l'immagine a dimensione intera)
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'eccezione ArgumentException
.
ProductsDataTable.ColumnChanging.cs
public partial class Northwind
{
public partial class ProductsDataTable
{
public override void BeginInit()
{
this.ColumnChanging += ValidateColumn;
}
void ValidateColumn(object sender,
DataColumnChangeEventArgs e)
{
if(e.Column.Equals(this.UnitPriceColumn))
{
if(!Convert.IsDBNull(e.ProposedValue) &&
(decimal)e.ProposedValue < 0)
{
throw new ArgumentException(
"UnitPrice cannot be less than zero", "UnitPrice");
}
}
else if (e.Column.Equals(this.UnitsInStockColumn) ||
e.Column.Equals(this.UnitsOnOrderColumn) ||
e.Column.Equals(this.ReorderLevelColumn))
{
if (!Convert.IsDBNull(e.ProposedValue) &&
(short)e.ProposedValue < 0)
{
throw new ArgumentException(string.Format(
"{0} cannot be less than zero", e.Column.ColumnName),
e.Column.ColumnName);
}
}
}
}
}
Passaggio 4: Aggiunta di regole business personalizzate alle classi BLL
Oltre alla convalida a livello di campo, possono essere 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 non è più disponibile,
UnitPrice
non può essere aggiornato - Il paese di residenza di un dipendente deve essere uguale al 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 la conformità alle regole business dell'applicazione. Questi controlli possono essere aggiunti direttamente ai metodi a cui si applicano.
Si supponga che le nostre regole di business impongono che un prodotto non possa essere contrassegnato come interrotto se fosse l'unico prodotto di un determinato fornitore. Cioè, 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 sospesi. Una regola di business strana, ma le regole di business e il buon senso non sono sempre allineate!
Per applicare questa regola di business nel UpdateProducts
metodo si inizierà controllando se Discontinued
è stato impostato su true
e, in tal caso, 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 generata un'eccezione ApplicationException
.
public bool UpdateProduct(string productName, int? supplierID, int? categoryID,
string quantityPerUnit, decimal? unitPrice, short? unitsInStock,
short? unitsOnOrder, short? reorderLevel, bool discontinued, int productID)
{
Northwind.ProductsDataTable products = Adapter.GetProductByProductID(productID);
if (products.Count == 0)
// no matching record found, return false
return false;
Northwind.ProductsRow product = products[0];
// Business rule check - cannot discontinue
// a product that is supplied by only
// one supplier
if (discontinued)
{
// Get the products we buy from this supplier
Northwind.ProductsDataTable productsBySupplier =
Adapter.GetProductsBySupplierID(product.SupplierID);
if (productsBySupplier.Count == 1)
// this is the only product we buy from this supplier
throw new ApplicationException(
"You cannot mark a product as discontinued if it is the only
product purchased from a supplier");
}
product.ProductName = productName;
if (supplierID == null) product.SetSupplierIDNull();
else product.SupplierID = supplierID.Value;
if (categoryID == null) product.SetCategoryIDNull();
else product.CategoryID = categoryID.Value;
if (quantityPerUnit == null) product.SetQuantityPerUnitNull();
else product.QuantityPerUnit = quantityPerUnit;
if (unitPrice == null) product.SetUnitPriceNull();
else product.UnitPrice = unitPrice.Value;
if (unitsInStock == null) product.SetUnitsInStockNull();
else product.UnitsInStock = unitsInStock.Value;
if (unitsOnOrder == null) product.SetUnitsOnOrderNull();
else product.UnitsOnOrder = unitsOnOrder.Value;
if (reorderLevel == null) product.SetReorderLevelNull();
else product.ReorderLevel = reorderLevel.Value;
product.Discontinued = discontinued;
// Update the product record
int rowsAffected = Adapter.Update(product);
// Return true if precisely one row was updated,
// otherwise false
return rowsAffected == 1;
}
Risposta agli errori di convalida nel livello presentazione
Quando si chiama il BLL dal livello presentazione, è possibile decidere se tentare di gestire eventuali eccezioni che potrebbero essere generate o lasciare che vengano visualizzate fino a ASP.NET (che genererà l'evento HttpApplication
).Error
Per gestire un'eccezione quando si usa BLL a livello di codice, è possibile usare un tentativo... blocco catch , come illustrato nell'esempio seguente:
ProductsBLL productLogic = new ProductsBLL();
// Update information for ProductID 1
try
{
// This will fail since we are attempting to use a
// UnitPrice value less than 0.
productLogic.UpdateProduct(
"Scott s Tea", 1, 1, null, -14m, 10, null, null, false, 1);
}
catch (ArgumentException ae)
{
Response.Write("There was a problem: " + ae.Message);
}
Come si vedrà nelle esercitazioni future, la gestione delle eccezioni che si eseguono dal BLL quando si usa un controllo Web dati per l'inserimento, l'aggiornamento o l'eliminazione dei dati può essere gestita 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 particolare ruolo. Nella prima esercitazione di questa serie di articoli è stato creato un livello di accesso ai dati usando dataset tipizzato; In questa esercitazione è stato creato un livello di logica di business come una serie di classi nella cartella dell'applicazione App_Code
che chiamano dal dal. Il BLL implementa la logica a livello di campo e a livello di business per l'applicazione. Oltre a creare un BLL separato, come è stato fatto 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 dal e BLL come l'approccio adottato in questo articolo.
Dopo aver completato DAL e BLL, è possibile iniziare a usare il livello di presentazione. Nell'esercitazione successiva verrà eseguita una breve deviazione dagli argomenti di accesso ai dati e verrà definito un layout di pagina coerente da usare in tutte le esercitazioni.
Buon programmatori!
Informazioni sull'autore
Scott Mitchell, autore di sette libri ASP/ASP.NET e fondatore di 4GuysFromRolla.com, lavora 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 all'indirizzo mitchell@4GuysFromRolla.com. o tramite il suo blog, disponibile all'indirizzo http://ScottOnWriting.NET.
Grazie speciale a
Questa serie di esercitazioni è stata esaminata da molti revisori utili. I revisori principali di questa esercitazione sono stati Liz Shulok, Dennis Patterson, Carlos Santos e Hilton Giesenow. Si è interessati a esaminare i prossimi articoli MSDN? In tal caso, rilasciami una riga in mitchell@4GuysFromRolla.com.