Manipulating data in asp.net 2.0: Creating a business Logic Layer _ self-study process

Source: Internet
Author: User
Tags bind constructor

Introduction

The data access layer, described in the first section of this tutorial, is a clear distinction between presentation logic and data access logic, as in the case of Layer, the following referred to as DAL. However, even if the DAL separates the details of data access from the presentation layer, it cannot handle any business rules. For example, we may not want the "category number" or "vendor number" of products in the product table that are marked "deactivated" to be updated; We may also need to apply some seniority rules, such as we don't want to be managed by someone who is less than our own qualifications. Another common scenario is authorization, for example, that only users with special privileges can delete a product or change the unit price.

We can actually think of the business Logic layer (Business Logic Layer, BLL) as a bridge between the data access layer and the presentation layer, and in this section we'll discuss how to integrate these business rules into a BLL. It should be explained that in a real-world application, BLL is implemented in the form of class libraries, but in order to simplify the structure of the project, in this tutorial we will implement BLL as a series of classes in the App_Code folder. The graph has always shown the structural relationship between the presentation layer, the BLL, and the DAL.

Figure one: BLL separates the presentation layer from the Dal and joins the business rules

First step: Create BLL Class

Our BLL consists of 4 classes, each of which corresponds to a TableAdapter in the DAL, which are read, inserted, modified, and deleted from their TableAdapter to apply the appropriate business rules.

To make a clearer distinction between dal and BLL classes, we created two subfolders in the App_Code folder, named Dal and BLL respectively. You just need to right-click the App_Code folder in Solution Explorer (Solution Explorer) and select New folder to create a new subfolder. After the two folders have been built, move the typed dataset (Typed DataSet) created in the first section to the Dal folder.

Then, create 4 class files in the BLL folder. Again, you just need to right-click the BLL folder in the Solution Explorer (Solution Explorer) and select New Item, and then select Class template in the pop-up dialog box to create a new class file. Each of these four files is named Productsbll, CATEGORIESBLL, SUPPLIERSBLL, and EMPLOYEESBLL.

Figure Two: Add 4 new classes to the BLL folder

Next, let's add some methods to these newly created classes and simply wrap those methods in the TableAdapter in the first section. Now, these methods will only use the methods in the DAL directly, and we'll add some business logic to them later.

Note: If you are using Visual Studio Standard or above (that is, you are not using the visual Web Developer), then you can also use
in the PRODUCTSBLL class, we need to add 7 methods altogether:
GetProducts () – Returns all Products
Getproductbyproductid (ProductID) – returns the product of the specified ProductID
Getproductsbycategoryid ( CategoryID) – Returns the product for the specified category
Getproductsbysupplier (SupplierID) – returns the product of the specified vendor
Addproduct (ProductName, SupplierID, CategoryID, QuantityPerUnit, UnitPrice, UnitsInStock, UnitsOnOrder, ReorderLevel, discontinued) – Add a product information to the database, and return the newly added products to the ProductID
UpdateProduct (ProductName, SupplierID, CategoryID, QuantityPerUnit, UnitPrice, UnitsInStock, UnitsOnOrder, ReorderLevel, discontinued, ProductID) – updates a product that already exists in a database, returns true if one record is updated, or returns false
Deleteproduct (ProductID) – Deletes the product for the specified ProductID

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 Productstableada

  Pter (); 
 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? unitpri CE, short? UnitsInStock, short? UnitsOnOrder, short? ReorderLevel, BOOL discontinued) {//Create a new Productrow instance northwind.productsdatatable products = new Northwind.produc
 Tsdatatable (); 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 new product products.
 Addproductsrow (product);

 int rowsaffected = adapter.update (products);
 Returns true if a record is just added, otherwise returns false return rowsaffected = 1; } [System.ComponentModel.DataObjectMethodAttribute (System.ComponentModel.DataObjectMethodType.update, true)] public bool UpdateProduct (string productName, int? supplierID, int? CategoryID, String Quantityperuni T, decimal? UnitPrice, short? UnitsInStock, short? UnitsOnOrder, short? ReorderLevel, BOOL discontinued, int ProductID) {Northwind.productsdatatable products = Adapter.getproductbyproduct
 ID (ProductID); if (products.

 Count = = 0)//No matching record found, returns 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;

 New product record int rowsaffected = adapter.update (product);
 Returns true if a record is just updated, otherwise returns false return rowsaffected = 1;
 } [System.ComponentModel.DataObjectMethodAttribute (System.ComponentModel.DataObjectMethodType.Delete, True)]

 public bool Deleteproduct (int productID) {int rowsaffected = Adapter.delete (ProductID);
 Returns true if a record is just deleted, otherwise returns false return rowsaffected = 1; }
}

Methods such as GetProducts, Getproductbyproductid, Getproductsbycategoryid, and Getproductbysuppliersid simply call the methods in the DAL directly to return the data. In some cases, however, we may also need to implement some business rules (such as authorization rules, different users, or no roles should be able to see different data), and now we can simply make them. So, for these methods, BLL is simply acting as a proxy between the presentation layer and the DAL.

Both the Addproduct and UpdateProduct methods use the product information in the parameters to add or update a product record. Because there are many fields in the product table that allow null values (CategoryID, SupplierID, UnitPrice ...). And so on), so the corresponding parameters in Addproduct and updateproduct use nullable types. Nullable types is a new technology provided in. NET 2.0 that indicates whether a value type can be empty. In C #, you can add a question mark after a value type that is allowed to be empty (for example, int?). x;). For more information about nullable types, you can refer to C # programming Guide.

Since inserts, modifications, and deletions may not affect any rows, all three methods return a BOOL value to indicate whether the operation was successful. For example, page developers use a nonexistent ProductID to invoke Deleteproduct, and it is clear that the DELETE statement submitted to the database will have no effect, so deleteproduct will return false.

Note: When we add or update the details of a product, we accept a scalar list of product information instead of directly accepting a Productsrow instance. Because Productsrow is inherited from Ado.net DataRow, and DataRow does not have a default parameterless constructor, in order to create an instance of Productsrow, we must first create an instance of productsdatatable and then call its newproductr Ow method (as we did in the Addproduct method). However, when I use ObjectDataSource to insert or update, the disadvantage of doing so will be exposed. Simply put, the ObjectDataSource try to create an instance of the input parameter, and if the BLL method wants a productsrow, then ObjectDataSource will attempt to create one, but it is clear that such an operation will fail, Because there is no default parameterless constructor. Detailed information on this issue can be found in the following two posts in the ASP.net forum: updating objectdatasources with strongly-typed datasets, Problem with ObjectDataSource and Strongly-typed DataSet.

Then, in Addproduct and UpdateProduct, we created a Productsrow instance and assigned the incoming argument to it. When assigning values to a DataRow datacolumns, validation of various field levels can be triggered. Therefore, we should manually verify the incoming parameters to ensure that the data passed to the BLL method is valid. Unfortunately, the strongly typed dataset (strongly-typed DataRow) generated by Visual Studio does not use nullable values. To show that a DataColumn in DataRow can accept null values, we must use the Setcolumnnamenull method.

In UpdateProduct, we first use the Getproductbyproductid (ProductID) method to read the product information that needs to be updated. There seems to be no need to do this, but we will demonstrate that this extra action is useful in the subsequent course on concurrency optimization (optimistic concurrency). Concurrent optimization is a technology that guarantees two users to manipulate one data at a time without conflict. Getting the entire record can also make it easier to create a method that updates only a subset of the DataRow, and we can find such an example in the Suppliersbll class.

Finally, notice that we add a DataObject tag to the PRODUCTSBLL class (that is, [System.ComponentModel.DataObject] above the class declaration statement), There are also dataobjectmethodattribute tags on each method. The DataObject label marks this class as being able to bind to a ObjectDataSource control, while Dataobjectmethodattribute illustrates the purpose of the method. We'll see in later tutorials that the ObjectDataSource of ASP.net 2.0 makes it easier to access data from a class. For the ObjectDataSource Wizard to filter the existing classes appropriately, only the classes marked as DataObject are displayed by default in the class list. Of course, the PRODUCTSBLL class can work without this tag, but plus it makes it easier and more enjoyable to operate in the ObjectDataSource Wizard.

Add another class

After completing the PRODUCTSBLL class, we also add some classes for categories, suppliers, and employees services. Let's take a moment to create the following class, based on the example above:

· CategoriesBLL.cs
GetCategories ()
Getcategorybycategoryid (CategoryID)

· SuppliersBLL.cs
Getsuppliers ()
Getsupplierbysupplierid (SupplierID)
Getsuppliersbycountry (Country)
Updatesupplieraddress (SupplierID, Address, city, country)

· EmployeesBLL.cs
GetEmployees ()
Getemployeebyemployeeid (EmployeeID)
Getemployeesbymanager (ManagerID)

The Updatesupplieraddress method in the Suppliersbll class is a noteworthy thing. This method provides an interface for updating only vendor address information. It first reads a supplierdatarow (using the Getsupplierbysupplierid method) based on the specified SupplierID, sets all its properties on the address, and then calls the Supplierdatatable Update method. The code for the Updatesupplieraddress method looks like this:

[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 entry found, return false to 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 vendor's information about the address
 int rowsaffected = adapter.update (supplier);

 Returns true if a record is updated exactly, otherwise returns false return
 rowsaffected = = 1;
 }

You can download the complete code for the BLL class from the link at the top of the page.

Step Two: Access the typed DataSet through the BLL class

In the first section of this tutorial, we give an example of using a typed dataset directly, but after we add the BLL class, the presentation layer can work through BLL. In the allproducts.aspx example in the first section of this tutorial, ProductsTableAdapter is used to bind the product list to the GridView, and the code looks like this:

ProductsTableAdapter productsadapter = new ProductsTableAdapter ();
Gridview1.datasource = Productsadapter.getproducts ();
Gridview1.databind ();

To use the new BLL class, all we need to do is simply modify the first line of code. Replace ProductsTableAdapter with PRODUCTBLL objects:

PRODUCTSBLL productlogic = new Productsbll ();
Gridview1.datasource = Productlogic.getproducts ();
Gridview1.databind ();

The BLL class can also be explicitly accessed by using ObjectDataSource (just like a typed dataset). We'll discuss the ObjectDataSource in detail in the next tutorial.

Figure III: List of products displayed in the GridView

Step Three: Add field-level validation to DataRow

Field-level validation refers to checking all the property values involved in a business object when inserting or updating. Take the product for example, some field-level validation rules are as follows:
· ProductName field must not exceed 40 characters
· QuantityPerUnit field must not exceed 20 characters
· ProductID, ProductName, and discontinued fields are required and other fields can be filled out.
· UnitPrice, UnitsInStock, UnitsOnOrder, and ReorderLevel fields must not be less than 0

These rules can or should be described in the database layer. The number of characters on the ProductName and QuantityPerUnit fields can be achieved by the data type of the corresponding column in the Products table (nvarchar and nvarchar (20) respectively). The field "required" can be implemented by setting the corresponding column of the table in the database to allow null. To ensure that the values of the UnitPrice, UnitsInStock, UnitsOnOrder, and ReorderLevel fields are not less than 0, you can add a constraint to their respective columns.
In addition to applying these rules to the database, they are also applied to the dataset. In fact, information such as field length and whether it is allowed to be empty has been applied to the DataColumn collection of each DataTable. We can see the field-level validation that already exists in the DataSet Designer (Designer), select a field from a DataTable, and then find it in the Properties window. As shown in Figure four, the QuantityPerUnit field in productdatatable allows null values and a maximum length of 20 characters. If we try to set a string that is longer than 20 characters for a Productsdatarow QuantityPerUnit property, a ArgumentException is thrown.

Figure IV: DataColumn provides basic field-level validation

Unfortunately, we cannot specify a boundary check through the Properties window, such as the UnitPrice value cannot be less than 0. To provide such field-level validation, we need to create an event Handler for the ColumnChanging event in the DataTable. As mentioned in the previous tutorial, datasets, DataTable, and DataRow objects created by typed datasets can be extended by partial classes. Using this technique, we can create a columnchanging event Handler for productdatatable. Let's start by creating a new class file named ProductsDataTable.ColumnChanging.cs in the App_Code folder, as shown in the following illustration.

Figure five: Adding a new class in the App_Code folder

Then, create an event handler for the ColumnChanging event to ensure that the values of UnitPrice, UnitsInStock, UnitsOnOrder, and ReorderLevel fields are not less than 0. If the values of these columns are out of range, a ArgumentException is thrown.

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

Step Fourth: Add a business rule to the BLL class

In addition to field-level validation, there may be more advanced business rules that contain different entities or concepts that cannot be represented in a single column, such as:
• If a product is marked as "inactive", its unit price cannot be modified
• The place of residence of an employee must be the same as the residence of his/her supervisor
• If a product is the only product provided by a vendor, then the product cannot be marked as "inactive"

The BLL class should ensure that the application's business rules are always validated. These validations can be added directly to the method in which they are applied.
Imagine that our business rules show that if a product is the only product of a given vendor, it cannot be marked as deactivated. In other words, if product X is the only product we buy from supplier Y, then we can't Mark X as inactive, but if supplier Y provides us with a total of 3 products, A, B, and C, then we can mark any one or three of them as "inactive." Pretty weird business rules, huh? But the business rules are usually different from what we normally feel.

To apply this business rule to the Updateproducts method, we should first check whether the discontinued is set to true. If so, then we should call Getproductsbysupplierid first to see how many products we have purchased from this vendor altogether. If we buy this product only from this supplier, then we throw a applicationexception.

public bool UpdateProduct (string productName, int? supplierID, int? CategoryID, String QuantityPerUnit, decimal? Price, short? UnitsInStock, short? UnitsOnOrder, short? ReorderLevel, BOOL discontinued, int ProductID) {Northwind.productsdatatable products = Adapter.getproductbyproducti
 D (ProductID); if (products.

 Count = = 0)//No match found, returns false return false;

 Northwind.productsrow product = Products[0]; Business Rule Check – cannot deactivate the only product provided by a vendor if (discontinued) {//Get all the products we get from this supplier northwind.productsdatatable PRODUCTSBYSUPPL IER = Adapter.getproductsbysupplierid (product.

  SupplierID); if (Productsbysupplier.count = 1)//This is the only product we get from this supplier throw new ApplicationException ("You cannot mark a product a
 s discontinued if its is 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;

 New product record int rowsaffected = adapter.update (product);
Returns true if a record is just updated, otherwise returns false return rowsaffected = 1; }

Responding to validation errors in the presentation layer

When we call BLL from the presentation layer, we can decide whether to handle an exception that might be thrown or throw it directly to asp.net (this will cause an HttpApplication error event). When using BLL, if you want to handle an exception programmatically, we can use the Try...catch block, just like the following example:

PRODUCTSBLL productlogic = new PRODUCTSBLL ();

Update ProductID 1 Product Information

Try
{
 //This operation will fail because we are trying to use a UnitPrice productlogic.updateproduct less than 0
 ("Scott's Tea", 1, 1, NULL, -14M, 10, NULL, NULL, FALSE, 1);
catch (ArgumentException ae)
{
 Response.Write ("There was a problem:" + AE.) message);
}

As we'll see later in the tutorial, when you insert, modify, or delete operational data through a data Web control, the handling of exceptions thrown from BLL can be done directly in an event handler without the need to use a try ... Catch block to wrap the code.

Summarize

An application with a good architecture has a clear hierarchy, and each layer encapsulates a specific role. In the first installment of this tutorial, we created a data access layer with a typed dataset; In this article, we set up a business logic layer that consists of a series of classes in the App_Code and calls the corresponding methods in the DAL. BLL implements the field-level and business-level logic for our applications. In addition to creating a separate BLL, as we have done in this section, another option is to use the partial class to extend the methods in TableAdapter. However, using this technique does not allow us to rewrite existing methods or to separate our Dal and BLL from clear enough.

After the Dal and BLL are finished, we are ready to start processing the presentation layer. In the next tutorial, we will briefly introduce some data access topics and define a consistent page rendering for the entire tutorial.
Happy programming!

About the author
Scott Mitchell, with six asp/asp. NET book, is the founder of 4GuysFromRolla.com, has been applying Microsoft Web technology since 1998. Scott is an independent technical consultant, trainer, writer, recently completed a new book to be published by Sams Press, proficient in asp.net 2.0 within 24 hours. His contact email is mitchell@4guysfromrolla.com, or he can contact him through his blog Http://ScottOnWriting.NET.

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.