Bagikan melalui


Membuat Lapisan Logika Bisnis (C#)

oleh Scott Mitchell

Unduh PDF

Dalam tutorial ini kita akan melihat cara memusatkan aturan bisnis Anda ke dalam Lapisan Logika Bisnis (BLL) yang berfungsi sebagai perantara untuk pertukaran data antara lapisan presentasi dan DAL.

Pendahuluan

Lapisan Akses Data (DAL) yang dibuat dalam tutorial pertama memisahkan logika akses data dengan bersih dari logika presentasi. Namun, sementara DAL secara tepat memisahkan detail akses data dari lapisan presentasi, DAL tidak memberlakukan aturan bisnis apa pun yang mungkin berlaku. Misalnya, untuk aplikasi kami, kami mungkin ingin melarang kolom CategoryID atau SupplierID dari tabel Products untuk dimodifikasi ketika kolom Discontinued diatur ke 1, atau kami mungkin ingin menginjeksikan aturan senioritas dengan melarang situasi di mana seorang karyawan dikelola oleh seseorang yang dipekerjakan setelah mereka. Skenario umum lainnya adalah otorisasi mungkin hanya pengguna dalam peran tertentu yang dapat menghapus produk atau dapat mengubah nilainya UnitPrice .

Dalam tutorial ini kita akan melihat cara memusatkan aturan bisnis ini ke dalam Lapisan Logika Bisnis (BLL) yang berfungsi sebagai perantara untuk pertukaran data antara lapisan presentasi dan DAL. Dalam aplikasi dunia nyata, BLL harus diimplementasikan sebagai proyek Pustaka Kelas terpisah; namun, untuk tutorial ini kami akan menerapkan BLL sebagai serangkaian kelas di folder kami App_Code untuk menyederhanakan struktur proyek. Gambar 1 menggambarkan hubungan arsitektur di antara lapisan presentasi, BLL, dan DAL.

BLL Memisahkan Lapisan Presentasi dari Lapisan Akses Data dan Memberlakukan Aturan Bisnis

Gambar 1: BLL Memisahkan Lapisan Presentasi dari Lapisan Akses Data dan Memberlakukan Aturan Bisnis

Langkah 1: Membuat Kelas BLL

BLL kami akan terdiri dari empat kelas, satu untuk setiap TableAdapter dalam DAL; masing-masing kelas BLL ini akan memiliki metode untuk ambil, menyisipkan, memperbarui, dan menghapus dari TableAdapter terkait dalam DAL, dan menerapkan aturan bisnis yang sesuai.

Untuk memisahkan kelas terkait DAL dan BLL dengan lebih bersih, mari kita buat dua subfolder di App_Code folder, DAL dan BLL. Cukup klik kanan pada App_Code folder di Penjelajah Solusi dan pilih Folder Baru. Setelah membuat kedua folder ini, pindahkan Typed DataSet yang dibuat dalam tutorial pertama ke subfolder DAL.

Selanjutnya, buat empat file kelas BLL di BLL subfolder. Untuk menyelesaikan ini, klik kanan pada BLL subfolder, pilih Tambahkan Item Baru, dan pilih templat Kelas. Beri nama empat kelas ProductsBLL, , CategoriesBLLSuppliersBLL, dan EmployeesBLL.

Menambahkan Empat Kelas Baru ke Folder App_Code

Gambar 2: Tambahkan Empat Kelas Baru ke App_Code Folder

Selanjutnya, mari kita tambahkan metode ke setiap kelas untuk dengan mudah membungkus metode yang telah ditentukan untuk TableAdapters dari tutorial pertama. Untuk saat ini, metode ini hanya akan memanggil langsung ke DAL; kita akan kembali nanti untuk menambahkan logika bisnis yang diperlukan.

Nota

Jika Anda menggunakan Visual Studio Standard Edition atau lebih tinggi (yaitu, Anda tidak menggunakan Visual Web Developer), Anda dapat secara opsional mendesain kelas Anda secara visual menggunakan Perancang Kelas. Lihat Blog Perancang Kelas untuk informasi selengkapnya tentang fitur baru ini di Visual Studio.

Untuk kelas, ProductsBLL kita perlu menambahkan total tujuh metode:

  • GetProducts() mengembalikan semua produk
  • GetProductByProductID(productID) mengembalikan produk dengan ID produk yang ditentukan
  • GetProductsByCategoryID(categoryID) mengembalikan semua produk dari kategori yang ditentukan
  • GetProductsBySupplier(supplierID) mengembalikan semua produk dari pemasok yang ditentukan
  • AddProduct(productName, supplierID, categoryID, quantityPerUnit, unitPrice, unitsInStock, unitsOnOrder, reorderLevel, discontinued) menyisipkan produk baru ke dalam database menggunakan nilai yang diteruskan; ProductID mengembalikan nilai rekaman yang baru disisipkan
  • UpdateProduct(productName, supplierID, categoryID, quantityPerUnit, unitPrice, unitsInStock, unitsOnOrder, reorderLevel, discontinued, productID) memperbarui produk yang ada dalam database menggunakan nilai yang diteruskan; mengembalikan true jika justru satu baris diperbarui, false jika tidak
  • DeleteProduct(productID) menghapus produk yang ditentukan dari 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;
    }
}

Metode yang hanya mengembalikan data GetProducts, , GetProductByProductIDGetProductsByCategoryID, dan GetProductBySuppliersID cukup mudah karena mereka hanya memanggil ke DAL. Meskipun dalam beberapa skenario mungkin ada aturan bisnis yang perlu diterapkan pada tingkat ini (seperti aturan otorisasi berdasarkan pengguna yang saat ini masuk atau peran tempat pengguna berada), kami hanya akan meninggalkan metode ini as-is. Untuk metode ini, maka, BLL hanya berfungsi sebagai proksi di mana lapisan presentasi mengakses data yang mendasar dari Lapisan Akses Data.

Metode AddProduct dan UpdateProduct keduanya mengambil sebagai parameter nilai untuk berbagai bidang produk dan menambahkan produk baru atau memperbarui yang sudah ada, masing-masing. Karena banyak kolom Product tabel dapat menerima nilai NULL (CategoryID, SupplierID, dan UnitPrice, contohnya), parameter input tersebut untuk AddProduct dan UpdateProduct yang sesuai dengan kolom tersebut menggunakan jenis nullable. Jenis nullable baru untuk .NET 2.0 dan menyediakan teknik untuk menunjukkan apakah jenis nilai harus, sebagai gantinya, menjadi null. Di C# Anda dapat menandai jenis nilai sebagai jenis nullable dengan menambahkan ? setelah jenis (seperti int? x;). Lihat bagian Jenis Nullable di Panduan Pemrograman C# untuk informasi selengkapnya.

Ketiga metode mengembalikan nilai Boolean yang menunjukkan apakah baris dimasukkan, diperbarui, atau dihapus karena operasi mungkin tidak menghasilkan baris yang terpengaruh. Misalnya, jika pengembang halaman memanggil DeleteProduct meneruskan ProductID untuk produk yang tidak ada, pernyataan DELETE yang dikeluarkan untuk database tidak akan berpengaruh dan metode DeleteProduct akan mengembalikan false.

Perhatikan bahwa saat menambahkan produk baru atau memperbarui produk yang sudah ada, kami mengambil nilai bidang produk baru atau yang dimodifikasi sebagai daftar skalar alih-alih menerima suatu instans ProductsRow. Pendekatan ini dipilih karena ProductsRow kelas berasal dari kelas ADO.NET DataRow , yang tidak memiliki konstruktor tanpa parameter default. Untuk membuat instans baru ProductsRow , kita harus terlebih dahulu membuat ProductsDataTable instans dan kemudian memanggil metodenya NewProductRow() (yang kita lakukan di AddProduct). Kekurangan ini terasa jelas saat kita menyisipkan dan memperbarui produk menggunakan ObjectDataSource. Singkatnya, ObjectDataSource akan mencoba membuat objek dari parameter input. Jika metode BLL mengharapkan instans ProductsRow , ObjectDataSource akan mencoba membuatnya, tetapi gagal karena kurangnya konstruktor tanpa parameter default. Untuk informasi selengkapnya tentang masalah ini, lihat dua posting forum ASP.NET berikut: Memperbarui ObjectDataSources dengan Strongly-Typed Himpunan Data, dan Masalah Dengan ObjectDataSource dan Strongly-Typed Himpunan Data.

Selanjutnya, dalam AddProduct dan UpdateProduct, kode membuat instans ProductsRow dan mengisinya dengan nilai yang baru saja diteruskan. Saat menetapkan nilai ke DataColumns dari dataRow berbagai pemeriksaan validasi tingkat bidang dapat terjadi. Oleh karena itu, memasukkan nilai yang diteruskan secara manual kembali ke DataRow membantu memastikan validitas data yang diteruskan ke metode BLL. Sayangnya, kelas DataRow bertipe kuat yang dihasilkan oleh Visual Studio tidak menggunakan Tipe Nullable. Sebaliknya, untuk menunjukkan bahwa DataColumn tertentu dalam DataRow harus sesuai dengan nilai database NULL, kita harus menggunakan metode SetColumnNameNull().

Di UpdateProduct, pertama-tama kami memuat produk yang akan diperbarui dengan menggunakan GetProductByProductID(productID). Meskipun ini mungkin tampak seperti perjalanan yang tidak perlu ke database, perjalanan tambahan ini akan terbukti berguna dalam tutorial di masa depan yang mengeksplorasi konkurensi optimis. Konkurensi optimis adalah teknik untuk memastikan bahwa dua pengguna yang secara bersamaan mengerjakan data yang sama tidak secara tidak sengaja menimpa perubahan satu sama lain. Mengambil seluruh catatan juga memudahkan untuk membuat metode pembaruan di BLL yang hanya memodifikasi subset kolom DataRow. Ketika kita menjelajahi SuppliersBLL kelas, kita akan melihat contoh seperti itu.

Terakhir, perhatikan bahwa ProductsBLL kelas memiliki atribut DataObject yang diterapkan padanya (sintaksnya tepat di atas pernyataan kelas di bagian atas file) dan metode memiliki [System.ComponentModel.DataObject]. Atribut DataObject menandai kelas sebagai objek yang cocok untuk mengikat kontrol ObjectDataSource, sedangkan DataObjectMethodAttribute menunjukkan tujuan metode . Seperti yang akan kita lihat di tutorial mendatang, ObjectDataSource ASP.NET 2.0 memudahkan untuk mengakses data secara deklaratif dari kelas. Untuk membantu memfilter daftar kelas yang mungkin diikat dalam wizard ObjectDataSource, secara default hanya kelas yang ditandai seperti DataObjects yang diperlihatkan dalam daftar drop-down wizard. Kelas ProductsBLL juga akan berfungsi tanpa atribut ini, tetapi menambahkannya membuatnya lebih mudah untuk dikerjakan dalam wizard ObjectDataSource.

Menambahkan Kelas Lain

Dengan kelas ProductsBLL selesai, kita masih perlu menambahkan kelas untuk bekerja dengan kategori, pemasok, dan karyawan. Luangkan waktu sejenak untuk membuat kelas dan metode berikut menggunakan konsep dari contoh di atas:

  • CategoriesBLL.cs

    • GetCategories()
    • GetCategoryByCategoryID(categoryID)
  • SuppliersBLL.cs

    • GetSuppliers()
    • GetSupplierBySupplierID(supplierID)
    • GetSuppliersByCountry(country)
    • UpdateSupplierAddress(supplierID, address, city, country)
  • EmployeesBLL.cs

    • GetEmployees()
    • GetEmployeeByEmployeeID(employeeID)
    • GetEmployeesByManager(managerID)

Salah satu metode yang perlu diperhatikan adalah metode SuppliersBLL dari kelas UpdateSupplierAddress. Metode ini menyediakan antarmuka untuk memperbarui hanya informasi alamat pemasok. Secara internal, metode ini membaca objek SupplierDataRow untuk supplierID yang ditentukan (menggunakan GetSupplierBySupplierID), mengatur properti terkait alamatnya, lalu memanggil metode SupplierDataTable pada Update. Metode UpdateSupplierAddress adalah sebagai berikut:

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

Lihat unduhan artikel ini untuk implementasi lengkap kelas BLL saya.

Langkah 2: Mengakses Himpunan Data yang Ditik melalui Kelas BLL

Dalam tutorial pertama kami melihat contoh bekerja langsung dengan Typed DataSet secara terprogram, tetapi dengan penambahan kelas BLL kami, tingkat presentasi harus bekerja melawan BLL sebagai gantinya. AllProducts.aspx Dalam contoh dari tutorial pertama, ProductsTableAdapter digunakan untuk mengikat daftar produk ke GridView, seperti yang ditunjukkan dalam kode berikut:

ProductsTableAdapter productsAdapter = new ProductsTableAdapter();
GridView1.DataSource = productsAdapter.GetProducts();
GridView1.DataBind();

Untuk menggunakan kelas BLL baru, yang perlu diubah adalah baris kode pertama cukup ganti ProductsTableAdapter objek dengan ProductBLL objek:

ProductsBLL productLogic = new ProductsBLL();
GridView1.DataSource = productLogic.GetProducts();
GridView1.DataBind();

Kelas BLL juga dapat diakses secara deklaratif (seperti halnya Typed DataSet) dengan menggunakan ObjectDataSource. Kita akan membahas ObjectDataSource secara lebih rinci dalam tutorial berikut.

Daftar Produk Ditampilkan dalam GridView

Gambar 3: Daftar Produk Ditampilkan dalam GridView (Klik untuk melihat gambar ukuran penuh)

Langkah 3: Menambahkan Validasi Field-Level ke Kelas DataRow

Validasi tingkat lapangan adalah pemeriksaan yang berkaitan dengan nilai properti objek bisnis ketika menyisipkan atau memperbarui. Beberapa aturan validasi tingkat bidang untuk produk meliputi:

  • Panjang ProductName bidang harus 40 karakter atau kurang
  • Panjang QuantityPerUnit bidang harus 20 karakter atau kurang
  • Bidang ProductID, ProductName, dan Discontinued diperlukan, tetapi semua bidang lainnya bersifat opsional
  • Bidang UnitPrice, UnitsInStock, UnitsOnOrder, dan ReorderLevel harus lebih besar dari atau sama dengan nol

Aturan ini dapat dan harus diekspresikan di tingkat database. Batas karakter pada kolom ProductName dan QuantityPerUnit ditentukan oleh jenis data dari kolom tersebut dalam tabel Products (nvarchar(40) dan nvarchar(20), masing-masing). Apakah bidang diperlukan atau opsional ditentukan oleh apakah kolom tabel database mengizinkan NULL . Ada empat batasan pemeriksaan yang memastikan bahwa hanya nilai yang lebih besar dari atau sama dengan nol dapat masuk ke kolom UnitPrice, UnitsInStock, UnitsOnOrder, atau ReorderLevel.

Selain memberlakukan aturan ini di database, aturan tersebut juga harus diberlakukan di tingkat Himpunan Data. Bahkan, panjang kolom dan apakah nilai diperlukan atau opsional sudah dicatat untuk setiap kumpulan DataColumn dalam DataTable. Untuk melihat validasi tingkat bidang yang ada secara otomatis disediakan, buka Perancang Himpunan Data, pilih bidang dari salah satu DataTables lalu buka jendela Properti. Seperti yang ditunjukkan Gambar 4, QuantityPerUnit DataColumn dalam ProductsDataTable memiliki panjang maksimum 20 karakter dan mengizinkan nilai NULL. Jika kita mencoba mengatur properti ProductsDataRow dari QuantityPerUnit ke nilai string yang lebih panjang dari 20 karakter, maka ArgumentException akan dihasilkan.

DataColumn Menyediakan Validasi Field-Level Dasar

Gambar 4: DataColumn Menyediakan Validasi Field-Level Dasar (Klik untuk melihat gambar ukuran penuh)

Sayangnya, kami tidak dapat menentukan pemeriksaan batas, seperti UnitPrice nilainya harus sama dengan atau lebih besar dari nol, melalui jendela Properti. Untuk menyediakan jenis validasi tingkat bidang ini, kita perlu membuat penanganan aktivitas untuk peristiwa ColumnChanging DataTable. Seperti disebutkan dalam tutorial sebelumnya, objek DataSet, DataTables, dan DataRow yang dibuat oleh Typed DataSet dapat diperluas melalui penggunaan kelas parsial. Menggunakan teknik ini kita dapat membuat ColumnChanging penanganan aktivitas untuk ProductsDataTable kelas . Mulailah dengan membuat kelas di App_Code folder bernama ProductsDataTable.ColumnChanging.cs.

Menambahkan Kelas Baru ke Folder App_Code

Gambar 5: Tambahkan Kelas Baru ke App_Code Folder (Klik untuk melihat gambar ukuran penuh)

Selanjutnya, buat penangan acara untuk peristiwa ColumnChanging yang memastikan bahwa nilai kolom UnitPrice, UnitsInStock, UnitsOnOrder, dan ReorderLevel (jika tidak NULL) lebih besar dari atau sama dengan nol. Jika kolom tersebut berada di luar rentang, lemparkan ArgumentException.

ProdukDataTable.PengubahanKolom.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);
                }
            }
         }
    }
}

Langkah 4: Menambahkan Aturan Bisnis Kustom ke Kelas BLL

Selain validasi tingkat bidang, mungkin ada aturan bisnis kustom tingkat tinggi yang melibatkan entitas atau konsep yang berbeda yang tidak dapat diekspresikan pada tingkat kolom tunggal, seperti:

  • Jika produk dihentikan, produk UnitPrice tidak dapat diperbarui
  • Negara tempat tinggal karyawan harus sama dengan negara tempat tinggal manajer mereka
  • Produk tidak dapat dihentikan jika merupakan satu-satunya produk yang disediakan oleh pemasok

Kelas BLL harus berisi pemeriksaan untuk memastikan kepatuhan terhadap aturan bisnis aplikasi. Pemeriksaan ini dapat ditambahkan langsung ke metode yang diterapkan.

Bayangkan bahwa aturan bisnis kami menentukan bahwa produk tidak dapat ditandai dihentikan jika itu adalah satu-satunya produk dari pemasok tertentu. Artinya, jika produk X adalah satu-satunya produk yang kami beli dari pemasok Y, kami tidak dapat menandai X sebagai dihentikan; namun, jika, pemasok Y memasok kami dengan tiga produk, A, B, dan C, maka kami dapat menandai salah satu dan semua ini sebagai dihentikan. Aturan bisnis yang aneh, tetapi aturan bisnis dan akal sehat tidak selalu selaras!

Untuk menerapkan aturan bisnis ini dalam metode UpdateProducts, kita akan memulai dengan memeriksa apakah Discontinued disetel ke true dan, jika demikian, kita akan memanggil GetProductsBySupplierID untuk menentukan berapa banyak produk yang kita beli dari pemasok produk ini. Jika hanya satu produk dibeli dari pemasok ini, kami akan menghasilkan 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;
}

Menanggapi Kesalahan Validasi di Tingkat Presentasi

Saat memanggil BLL dari tingkat presentasi, kita dapat memutuskan apakah akan mencoba menangani pengecualian apa pun yang mungkin terjadi atau membiarkannya diteruskan ke ASP.NET (yang akan menyebabkan terjadinya acara HttpApplicationError). Untuk menangani pengecualian saat bekerja dengan BLL secara terprogram, kita dapat menggunakan blok try...catch, seperti yang ditunjukkan contoh berikut:

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

Seperti yang akan kita lihat di tutorial mendatang, menangani pengecualian yang muncul ke permukaan dari BLL saat menggunakan kontrol Web data untuk menyisipkan, memperbarui, atau menghapus data dapat ditangani langsung dalam penangan kejadian dibandingkan harus membungkus kode dalam blok try...catch.

Ringkasan

Aplikasi yang dirancang dengan baik dibuat menjadi lapisan yang berbeda, yang masing-masing merangkum peran tertentu. Dalam tutorial pertama dari seri artikel ini, kami membuat Lapisan Akses Data menggunakan Typed DataSets; dalam tutorial ini kami membangun Lapisan Logika Bisnis sebagai serangkaian kelas di folder aplikasi App_Code kami yang memanggil ke DAL kami. BLL mengimplementasikan logika tingkat bidang dan tingkat bisnis untuk aplikasi kami. Selain membuat BLL terpisah, seperti yang kami lakukan dalam tutorial ini, opsi lain adalah memperluas metode TableAdapters melalui penggunaan kelas parsial. Namun, menggunakan teknik ini tidak memungkinkan kami untuk mengambil alih metode yang ada atau tidak memisahkan DAL dan BLL kami dengan bersih seperti pendekatan yang telah kami ambil dalam artikel ini.

Dengan DAL dan BLL selesai, kami siap untuk memulai pada lapisan presentasi kami. Dalam tutorial berikutnya kita akan menyimpang sejenak dari topik akses data dan menentukan tata letak halaman yang konsisten untuk digunakan di seluruh tutorial.

Selamat Pemrograman!

Tentang Penulis

Scott Mitchell, penulis tujuh buku ASP/ASP.NET dan pendiri 4GuysFromRolla.com, telah bekerja sama dengan teknologi Microsoft Web sejak 1998. Scott bekerja sebagai konsultan, pelatih, dan penulis independen. Buku terbarunya adalah Sams Teach Yourself ASP.NET 2.0 dalam 24 Jam. Dia dapat dijangkau di mitchell@4GuysFromRolla.com.

Ucapan terima kasih khusus kepada

Seri tutorial ini ditinjau oleh banyak peninjau yang bermanfaat. Peninjau utama untuk tutorial ini adalah Liz Shulok, Dennis Patterson, Carlos Santos, dan Hilton Giesenow. Tertarik untuk meninjau artikel MSDN saya yang akan datang? Jika demikian, hubungi saya di mitchell@4GuysFromRolla.com.