Bagikan melalui


Membuat Lapisan Logika Bisnis (VB)

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

Daripada membuat kelas terpisah untuk mengimplementasikan logika bisnis kami, kami dapat menempatkan logika ini secara langsung di Typed DataSet dengan kelas parsial. Untuk contoh membuat dan memperluas DataSet Bertipe, rujuk kembali ke tutorial pertama.

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.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

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 Nothing. Lihat entri blog Paul VickThe Truth About Nullable Types dan VB dan dokumentasi teknis tentang struktur Nullable untuk informasi lebih lanjut.

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 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

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:

Dim productsAdapter As 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:

Dim productLogic As 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.vb.

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.

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

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.

<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

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 Coba... Tangkap blok, seperti yang ditunjukkan contoh berikut:

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

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.