Bagikan melalui


Membangun Model dengan Validasi Aturan Bisnis

oleh Microsoft

Unduh PDF

Ini adalah langkah 3 dari tutorial aplikasi "NerdDinner" gratis yang memandu cara membangun aplikasi web kecil, tetapi lengkap menggunakan ASP.NET MVC 1.

Langkah 3 memperlihatkan cara membuat model yang dapat kita gunakan untuk mengkueri dan memperbarui database untuk aplikasi NerdDinner kita.

Jika Anda menggunakan ASP.NET MVC 3, kami sarankan Anda mengikuti tutorial Memulai MVC 3 atau MVC Music Store .

NerdDinner Langkah 3: Membangun Model

Dalam kerangka kerja model-view-controller istilah "model" mengacu pada objek yang mewakili data aplikasi, serta logika domain terkait yang mengintegrasikan validasi dan aturan bisnis dengannya. Model ini dalam banyak hal merupakan "jantung" aplikasi berbasis MVC, dan seperti yang akan kita lihat nanti pada dasarnya mendorong perilakunya.

Kerangka kerja MVC ASP.NET mendukung penggunaan teknologi akses data apa pun, dan pengembang dapat memilih dari berbagai opsi data .NET yang kaya untuk mengimplementasikan model mereka termasuk: LINQ ke Entitas, LINQ ke SQL, NHibernate, LLBLGen Pro, SubSonic, WilsonORM, atau hanya dataReaders atau Himpunan Data ADO.NET mentah.

Untuk aplikasi NerdDinner kami, kami akan menggunakan LINQ ke SQL untuk membuat model sederhana yang sesuai dengan desain database kami, dan menambahkan beberapa logika validasi kustom dan aturan bisnis. Kami kemudian akan menerapkan kelas repositori yang membantu mengabstraksi implementasi kegigihan data dari aplikasi lainnya, dan memungkinkan kami untuk dengan mudah mengujinya secara unit.

LINQ ke SQL

LINQ ke SQL adalah ORM (pemeta hubungan objek) yang dikirim sebagai bagian dari .NET 3.5.

LINQ ke SQL menyediakan cara mudah untuk memetakan tabel database ke kelas .NET yang dapat kita kodekan. Untuk aplikasi NerdDinner kami, kami akan menggunakannya untuk memetakan tabel Makan Malam dan RSVP dalam database kami ke kelas Makan Malam dan RSVP. Kolom tabel Makan Malam dan RSVP akan sesuai dengan properti pada kelas Makan Malam dan RSVP. Setiap objek Makan Malam dan RSVP akan mewakili baris terpisah dalam tabel Makan Malam atau RSVP dalam database.

LINQ ke SQL memungkinkan kita untuk menghindari harus membuat pernyataan SQL secara manual untuk mengambil dan memperbarui objek Makan Malam dan RSVP dengan data database. Sebagai gantinya, kita akan menentukan kelas Makan Malam dan RSVP, bagaimana mereka memetakan ke/dari database, dan hubungan di antara mereka. LINQ ke SQL kemudian akan mengurus pembuatan logika eksekusi SQL yang sesuai untuk digunakan pada runtime ketika kita berinteraksi dan menggunakannya.

Kita dapat menggunakan dukungan bahasa LINQ dalam VB dan C# untuk menulis kueri ekspresif yang mengambil objek Makan Malam dan RSVP dari database. Ini meminimalkan jumlah kode data yang perlu kita tulis, dan memungkinkan kita untuk membangun aplikasi yang benar-benar bersih.

Menambahkan LINQ ke Kelas SQL ke proyek kami

Kita akan mulai dengan mengklik kanan folder "Model" dalam proyek kita, dan pilih perintah menu Tambahkan> Item Baru :

Cuplikan layar folder Model. Item baru disorot. Model disorot dan dipilih.

Ini akan memunculkan dialog "Tambahkan Item Baru". Kita akan memfilter berdasarkan kategori "Data" dan memilih templat "LINQ ke Kelas SQL" di dalamnya:

Cuplikan layar dialog Tambahkan Item Baru. Data disorot. Kelas L I N Q ke S Q L dipilih dan disorot.

Kita akan memberi nama item "NerdDinner" dan klik tombol "Tambahkan". Visual Studio akan menambahkan file NerdDinner.dbml di bawah direktori \Model kami, lalu membuka LINQ ke perancang relasional objek SQL:

Cuplikan layar kotak dialog Nerd Dinner di Visual Studio. File Nerd Dinner dot d b m l dipilih.

Membuat Kelas Model Data dengan LINQ ke SQL

LINQ ke SQL memungkinkan kami membuat kelas model data dengan cepat dari skema database yang ada. Untuk melakukan ini, kita akan membuka database NerdDinner di Server Explorer, dan memilih Tabel yang ingin kita model di dalamnya:

Cuplikan layar Server Explorer. Tabel diperluas. Makan malam dan R S V P disorot.

Kita kemudian dapat menyeret tabel ke permukaan desainer LINQ ke SQL. Ketika kita melakukan LINQ ini ke SQL akan secara otomatis membuat kelas Makan Malam dan RSVP menggunakan skema tabel (dengan properti kelas yang memetakan ke kolom tabel database):

Cuplikan layar kotak dialog Nerd Dinner. Kelas Makan Malam dan R S V P ditampilkan.

Secara default, LINQ ke perancang SQL secara otomatis "mendesalisasi" nama tabel dan kolom saat membuat kelas berdasarkan skema database. Misalnya: tabel "Makan Malam" dalam contoh kami di atas menghasilkan kelas "Makan Malam". Penamaan kelas ini membantu membuat model kami konsisten dengan konvensi penamaan .NET, dan saya biasanya menemukan bahwa memiliki perancang memperbaiki ini dengan nyaman (terutama ketika menambahkan banyak tabel). Jika Anda tidak menyukai nama kelas atau properti yang dihasilkan perancang, Anda selalu dapat menggantinya dan mengubahnya menjadi nama apa pun yang Anda inginkan. Anda dapat melakukan ini baik dengan mengedit entitas/nama properti dalam baris dalam perancang atau dengan memodifikasinya melalui kisi properti.

Secara default LINQ ke perancang SQL juga memeriksa hubungan kunci primer/kunci asing tabel, dan berdasarkannya secara otomatis membuat "hubungan hubungan" default antara kelas model yang berbeda yang dibuatnya. Misalnya, ketika kami menyeret tabel Makan Malam dan RSVP ke perancang LINQ ke SQL, hubungan hubungan satu-ke-banyak antara keduanya disimpulkan berdasarkan fakta bahwa tabel RSVP memiliki kunci asing ke tabel Makan Malam (ini ditunjukkan oleh panah di perancang):

Cuplikan layar tabel Makan Malam dan R S V P. Panah disorot dan menunjuk dari pohon properti Makan Malam dan pohon properti R S V P.

Asosiasi di atas akan menyebabkan LINQ ke SQL menambahkan properti "Makan Malam" yang sangat diketik ke kelas RSVP yang dapat digunakan pengembang untuk mengakses Makan Malam yang terkait dengan RSVP tertentu. Ini juga akan menyebabkan kelas Makan Malam memiliki properti koleksi "RSVP" yang memungkinkan pengembang untuk mengambil dan memperbarui objek RSVP yang terkait dengan Makan Malam tertentu.

Di bawah ini Anda dapat melihat contoh intellisense dalam Visual Studio saat kami membuat objek RSVP baru dan menambahkannya ke koleksi RSVP Makan Malam. Perhatikan bagaimana LINQ ke SQL secara otomatis menambahkan koleksi "RSVP" pada objek Makan Malam:

Cuplikan layar intellisense dalam Visual Studio. R S V Ps disorot.

Dengan menambahkan objek RSVP ke koleksi RSVP Makan Malam, kami memberi tahu LINQ ke SQL untuk mengaitkan hubungan kunci asing antara baris Makan Malam dan RSVP dalam database kami:

Cuplikan layar objek R S V P dan koleksi R S V P Makan Malam.

Jika Anda tidak menyukai bagaimana perancang telah memodelkan atau menamai asosiasi tabel, Anda dapat mengambil alihnya. Cukup klik panah asosiasi dalam perancang dan akses propertinya melalui kisi properti untuk mengganti nama, menghapus, atau memodifikasinya. Namun, untuk aplikasi NerdDinner kami, aturan asosiasi default berfungsi dengan baik untuk kelas model data yang kami bangun dan kami hanya dapat menggunakan perilaku default.

Kelas NerdDinnerDataContext

Visual Studio akan secara otomatis membuat kelas .NET yang mewakili model dan hubungan database yang ditentukan menggunakan LINQ ke perancang SQL. Kelas LINQ ke SQL DataContext juga dihasilkan untuk setiap file desainer LINQ ke SQL yang ditambahkan ke solusi. Karena kami menamai LINQ kami ke item kelas SQL "NerdDinner", kelas DataContext yang dibuat akan disebut "NerdDinnerDataContext". Kelas NerdDinnerDataContext ini adalah cara utama kita akan berinteraksi dengan database.

Kelas NerdDinnerDataContext kami mengekspos dua properti - "Makan Malam" dan "RSVP" - yang mewakili dua tabel yang kami model dalam database. Kita dapat menggunakan C# untuk menulis kueri LINQ terhadap properti tersebut untuk mengkueri dan mengambil objek Makan Malam dan RSVP dari database.

Kode berikut menunjukkan cara membuat instans objek NerdDinnerDataContext dan melakukan kueri LINQ terhadapnya untuk mendapatkan urutan Makan Malam yang terjadi di masa depan. Visual Studio menyediakan intellisense penuh saat menulis kueri LINQ, dan objek yang dikembalikan darinya diketik dengan kuat dan juga mendukung intellisense:

Cuplikan layar Visual Studio. Deskripsi disorot.

Selain memungkinkan kami untuk mengkueri objek Makan Malam dan RSVP, NerdDinnerDataContext juga secara otomatis melacak perubahan apa pun yang kemudian kami lakukan pada objek Makan Malam dan RSVP yang kami ambil melaluinya. Kita dapat menggunakan fungsionalitas ini untuk dengan mudah menyimpan perubahan kembali ke database - tanpa harus menulis kode pembaruan SQL eksplisit.

Misalnya, kode di bawah ini menunjukkan cara menggunakan kueri LINQ untuk mengambil satu objek Makan Malam dari database, memperbarui dua properti Makan Malam, lalu menyimpan perubahan kembali ke database:

NerdDinnerDataContext db = new NerdDinnerDataContext();

// Retrieve Dinner object that reprents row with DinnerID of 1
Dinner dinner = db.Dinners.Single(d => d.DinnerID == 1);

// Update two properties on Dinner 
dinner.Title = "Changed Title";
dinner.Description = "This dinner will be fun";

// Persist changes to database
db.SubmitChanges();

Objek NerdDinnerDataContext dalam kode di atas secara otomatis melacak perubahan properti yang dilakukan pada objek Makan Malam yang kami ambil darinya. Ketika kami memanggil metode "SubmitChanges()", metode tersebut akan menjalankan pernyataan "UPDATE" SQL yang sesuai ke database untuk mempertahankan nilai yang diperbarui kembali.

Membuat Kelas DinnerRepository

Untuk aplikasi kecil terkadang baik-baik saja agar Pengontrol bekerja langsung terhadap kelas LINQ ke SQL DataContext, dan menyematkan kueri LINQ dalam Pengontrol. Namun, ketika aplikasi menjadi lebih besar, pendekatan ini menjadi rumit untuk dipertahankan dan diuji. Ini juga dapat menyebabkan kami menduplikasi kueri LINQ yang sama di beberapa tempat.

Salah satu pendekatan yang dapat membuat aplikasi lebih mudah dipertahankan dan diuji adalah menggunakan pola "repositori". Kelas repositori membantu merangkum kueri data dan logika persistensi, dan mengabstraksi detail implementasi persistensi data dari aplikasi. Selain membuat pembersih kode aplikasi, menggunakan pola repositori dapat memudahkan untuk mengubah implementasi penyimpanan data di masa depan, dan dapat membantu memfasilitasi pengujian unit aplikasi tanpa memerlukan database nyata.

Untuk aplikasi NerdDinner kami, kami akan menentukan kelas DinnerRepository dengan tanda tangan di bawah ini:

public class DinnerRepository {

    // Query Methods
    public IQueryable<Dinner> FindAllDinners();
    public IQueryable<Dinner> FindUpcomingDinners();
    public Dinner             GetDinner(int id);

    // Insert/Delete
    public void Add(Dinner dinner);
    public void Delete(Dinner dinner);

    // Persistence
    public void Save();
}

Catatan: Nanti di bab ini kita akan mengekstrak antarmuka IDinnerRepository dari kelas ini dan mengaktifkan injeksi dependensi dengannya di Controllers kita. Namun, untuk memulainya, kita akan mulai sederhana dan hanya bekerja langsung dengan kelas DinnerRepository.

Untuk mengimplementasikan kelas ini, kita akan mengklik kanan folder "Model" dan memilih perintah menu Item Add-New>. Dalam dialog "Tambahkan Item Baru" kita akan memilih templat "Kelas" dan memberi nama file "DinnerRepository.cs":

Cuplikan layar folder Model. Tambahkan Item Baru disorot.

Kami kemudian dapat mengimplementasikan kelas DinnerRepository kami menggunakan kode di bawah ini:

public class DinnerRepository {
 
    private NerdDinnerDataContext db = new NerdDinnerDataContext();

    //
    // Query Methods

    public IQueryable<Dinner> FindAllDinners() {
        return db.Dinners;
    }

    public IQueryable<Dinner> FindUpcomingDinners() {
        return from dinner in db.Dinners
               where dinner.EventDate > DateTime.Now
               orderby dinner.EventDate
               select dinner;
    }

    public Dinner GetDinner(int id) {
        return db.Dinners.SingleOrDefault(d => d.DinnerID == id);
    }

    //
    // Insert/Delete Methods

    public void Add(Dinner dinner) {
        db.Dinners.InsertOnSubmit(dinner);
    }

    public void Delete(Dinner dinner) {
        db.RSVPs.DeleteAllOnSubmit(dinner.RSVPs);
        db.Dinners.DeleteOnSubmit(dinner);
    }

    //
    // Persistence 

    public void Save() {
        db.SubmitChanges();
    }
}

Mengambil, Memperbarui, Menyisipkan, dan Menghapus menggunakan kelas DinnerRepository

Sekarang setelah kita membuat kelas DinnerRepository, mari kita lihat beberapa contoh kode yang menunjukkan tugas umum yang dapat kita lakukan dengannya:

Contoh Kueri

Kode di bawah ini mengambil satu Makan Malam menggunakan nilai DinnerID:

DinnerRepository dinnerRepository = new DinnerRepository();

// Retrieve specific dinner by its DinnerID
Dinner dinner = dinnerRepository.GetDinner(5);

Kode di bawah ini mengambil semua makan malam yang akan datang dan loop di atasnya:

DinnerRepository dinnerRepository = new DinnerRepository();

// Retrieve all upcoming Dinners
var upcomingDinners = dinnerRepository.FindUpcomingDinners();

// Loop over each upcoming Dinner and print out its Title
foreach (Dinner dinner in upcomingDinners) {
   Response.Write("Title" + dinner.Title);
}

Sisipkan dan Perbarui Contoh

Kode di bawah ini menunjukkan penambahan dua makan malam baru. Penambahan/modifikasi pada repositori tidak diterapkan ke database hingga metode "Save()" dipanggil di atasnya. LINQ ke SQL secara otomatis membungkus semua perubahan dalam transaksi database - jadi semua perubahan terjadi atau tidak ada yang dilakukan ketika repositori kami menyimpan:

DinnerRepository dinnerRepository = new DinnerRepository();

// Create First Dinner
Dinner newDinner1 = new Dinner();
newDinner1.Title = "Dinner with Scott";
newDinner1.HostedBy = "ScotGu";
newDinner1.ContactPhone = "425-703-8072";

// Create Second Dinner
Dinner newDinner2 = new Dinner();
newDinner2.Title = "Dinner with Bill";
newDinner2.HostedBy = "BillG";
newDinner2.ContactPhone = "425-555-5151";

// Add Dinners to Repository
dinnerRepository.Add(newDinner1);
dinnerRepository.Add(newDinner2);

// Persist Changes
dinnerRepository.Save();

Kode di bawah ini mengambil objek Makan Malam yang ada, dan memodifikasi dua properti di atasnya. Perubahan diterapkan kembali ke database ketika metode "Save()" dipanggil di repositori kami:

DinnerRepository dinnerRepository = new DinnerRepository();

// Retrieve specific dinner by its DinnerID
Dinner dinner = dinnerRepository.GetDinner(5);

// Update Dinner properties
dinner.Title = "Update Title";
dinner.HostedBy = "New Owner";

// Persist changes
dinnerRepository.Save();

Kode di bawah ini mengambil makan malam dan kemudian menambahkan RSVP ke dalamnya. Ini dilakukan menggunakan koleksi RSVP pada objek Makan Malam yang dibuat LINQ ke SQL untuk kami (karena ada hubungan kunci primer/kunci asing antara keduanya dalam database). Perubahan ini dipertahankan kembali ke database sebagai baris tabel RSVP baru ketika metode "Save()" dipanggil pada repositori:

DinnerRepository dinnerRepository = new DinnerRepository();

// Retrieve specific dinner by its DinnerID
Dinner dinner = dinnerRepository.GetDinner(5);

// Create a new RSVP object
RSVP myRSVP = new RSVP();
myRSVP.AttendeeName = "ScottGu";

// Add RSVP to Dinner's RSVP Collection
dinner.RSVPs.Add(myRSVP);

// Persist changes
dinnerRepository.Save();

Hapus Contoh

Kode di bawah ini mengambil objek Makan Malam yang ada, lalu menandainya untuk dihapus. Ketika metode "Save()" dipanggil pada repositori, metode tersebut akan menerapkan penghapusan kembali ke database:

DinnerRepository dinnerRepository = new DinnerRepository();

// Retrieve specific dinner by its DinnerID
Dinner dinner = dinnerRepository.GetDinner(5);

// Mark dinner to be deleted
dinnerRepository.Delete(dinner);

// Persist changes
dinnerRepository.Save();

Mengintegrasikan Validasi dan Logika Aturan Bisnis dengan Kelas Model

Mengintegrasikan validasi dan logika aturan bisnis adalah bagian penting dari aplikasi apa pun yang berfungsi dengan data.

Validasi Skema

Saat kelas model didefinisikan menggunakan perancang LINQ ke SQL, jenis data properti di kelas model data sesuai dengan jenis data tabel database. Misalnya: jika kolom "EventDate" dalam tabel Makan Malam adalah "tanggalwaktu", kelas model data yang dibuat oleh LINQ ke SQL akan berjenis "DateTime" (yang merupakan tipe data .NET bawaan). Ini berarti Anda akan mendapatkan kesalahan kompilasi jika Anda mencoba menetapkan bilangan bulat atau boolean ke dalamnya dari kode, dan itu akan menimbulkan kesalahan secara otomatis jika Anda mencoba mengonversi jenis string yang tidak valid secara implisit ke dalamnya pada runtime.

LINQ ke SQL juga akan secara otomatis menangani pelepasan nilai SQL untuk Anda saat menggunakan string - yang membantu melindungi Anda dari serangan injeksi SQL saat menggunakannya.

Validasi dan Logika Aturan Bisnis

Validasi skema berguna sebagai langkah pertama, tetapi jarang cukup. Sebagian besar skenario dunia nyata memerlukan kemampuan untuk menentukan logika validasi yang lebih kaya yang dapat mencakup beberapa properti, menjalankan kode, dan sering memiliki kesadaran tentang status model (misalnya: apakah sedang dibuat /diperbarui/dihapus, atau dalam status khusus domain seperti "diarsipkan"). Ada berbagai pola dan kerangka kerja yang berbeda yang dapat digunakan untuk menentukan dan menerapkan aturan validasi ke kelas model, dan ada beberapa kerangka kerja berbasis .NET di luar sana yang dapat digunakan untuk membantu hal ini. Anda dapat menggunakan hampir semua dari mereka dalam aplikasi MVC ASP.NET.

Untuk tujuan aplikasi NerdDinner kami, kami akan menggunakan pola yang relatif sederhana dan lurus ke depan di mana kami mengekspos properti IsValid dan metode GetRuleViolations() pada objek model Dinner kami. Properti IsValid akan mengembalikan true atau false tergantung pada apakah validasi dan aturan bisnis semuanya valid. Metode GetRuleViolations() akan mengembalikan daftar kesalahan aturan apa pun.

Kami akan menerapkan IsValid dan GetRuleViolations() untuk model Makan Malam kami dengan menambahkan "kelas parsial" ke proyek kami. Kelas parsial dapat digunakan untuk menambahkan metode/properti/peristiwa ke kelas yang dikelola oleh desainer VS (seperti kelas Makan Malam yang dihasilkan oleh LINQ ke perancang SQL) dan membantu menghindari alat dari mengacak-acak kode kami. Kita dapat menambahkan kelas parsial baru ke proyek kita dengan mengklik kanan pada folder \Model, lalu pilih perintah menu "Tambahkan Item Baru". Kita kemudian dapat memilih templat "Kelas" dalam dialog "Tambahkan Item Baru" dan menamainya Dinner.cs.

Cuplikan layar folder Model. Tambahkan Item Baru dipilih. Titik makan malam c s ditulis dalam kotak dialog Tambahkan Item Baru.

Mengklik tombol "Tambahkan" akan menambahkan file Dinner.cs ke proyek kami dan membukanya dalam IDE. Kita kemudian dapat menerapkan kerangka kerja penegakan aturan/validasi dasar menggunakan kode di bawah ini:

public partial class Dinner {

    public bool IsValid {
        get { return (GetRuleViolations().Count() == 0); }
    }

    public IEnumerable<RuleViolation> GetRuleViolations() {
        yield break;
    }

    partial void OnValidate(ChangeAction action) {
        if (!IsValid)
            throw new ApplicationException("Rule violations prevent saving");
    }
}

public class RuleViolation {

    public string ErrorMessage { get; private set; }
    public string PropertyName { get; private set; }

    public RuleViolation(string errorMessage, string propertyName) {
        ErrorMessage = errorMessage;
        PropertyName = propertyName;
    }
}

Beberapa catatan tentang kode di atas:

  • Kelas Makan Malam diawali dengan kata kunci "parsial" - yang berarti kode yang terkandung di dalamnya akan dikombinasikan dengan kelas yang dihasilkan/dikelola oleh LINQ ke perancang SQL dan dikompilasi ke dalam satu kelas.
  • Kelas RuleViolation adalah kelas pembantu yang akan kami tambahkan ke proyek yang memungkinkan kami memberikan detail selengkapnya tentang pelanggaran aturan.
  • Metode Dinner.GetRuleViolations() menyebabkan validasi dan aturan bisnis kami dievaluasi (kami akan segera menerapkannya). Kemudian mengembalikan urutan objek RuleViolation yang memberikan detail selengkapnya tentang kesalahan aturan apa pun.
  • Properti Dinner.IsValid menyediakan properti pembantu yang nyaman yang menunjukkan apakah objek Makan Malam memiliki RuleViolations aktif. Ini dapat diperiksa secara proaktif oleh pengembang menggunakan objek Makan Malam kapan saja (dan tidak menimbulkan pengecualian).
  • Metode parsial Dinner.OnValidate() adalah kait yang disediakan LINQ ke SQL yang memungkinkan kami untuk diberi tahu kapan saja objek Makan Malam akan dipertahankan dalam database. Implementasi OnValidate() kami di atas memastikan bahwa Makan Malam tidak memiliki RuleViolations sebelum disimpan. Jika dalam keadaan tidak valid, hal ini menimbulkan pengecualian, yang akan menyebabkan LINQ ke SQL membatalkan transaksi.

Pendekatan ini menyediakan kerangka kerja sederhana yang dapat kami integrasikan validasi dan aturan bisnis. Untuk saat ini mari kita tambahkan aturan di bawah ini ke metode GetRuleViolations() kami:

public IEnumerable<RuleViolation> GetRuleViolations() {

    if (String.IsNullOrEmpty(Title))
        yield return new RuleViolation("Title required","Title");

    if (String.IsNullOrEmpty(Description))
        yield return new RuleViolation("Description required","Description");

    if (String.IsNullOrEmpty(HostedBy))
        yield return new RuleViolation("HostedBy required", "HostedBy");

    if (String.IsNullOrEmpty(Address))
        yield return new RuleViolation("Address required", "Address");

    if (String.IsNullOrEmpty(Country))
        yield return new RuleViolation("Country required", "Country");

    if (String.IsNullOrEmpty(ContactPhone))
        yield return new RuleViolation("Phone# required", "ContactPhone");

    if (!PhoneValidator.IsValidNumber(ContactPhone, Country))
        yield return new RuleViolation("Phone# does not match country", "ContactPhone");

    yield break;
}

Kami menggunakan fitur "yield return" C# untuk mengembalikan urutan RuleViolations apa pun. Enam pemeriksaan aturan pertama di atas hanya memberlakukan bahwa properti string pada Makan Malam kami tidak boleh null atau kosong. Aturan terakhir sedikit lebih menarik, dan memanggil metode pembantu PhoneValidator.IsValidNumber() yang dapat kami tambahkan ke proyek kami untuk memverifikasi bahwa format nomor ContactPhone cocok dengan negara/wilayah Makan Malam.

Kita dapat menggunakan . Dukungan ekspresi reguler NET untuk mengimplementasikan dukungan validasi telepon ini. Di bawah ini adalah implementasi PhoneValidator sederhana yang dapat kami tambahkan ke proyek kami yang memungkinkan kami menambahkan pemeriksaan pola Regex khusus negara/wilayah:

public class PhoneValidator {

    static IDictionary<string, Regex> countryRegex = new Dictionary<string, Regex>() {
           { "USA", new Regex("^[2-9]\\d{2}-\\d{3}-\\d{4}$")},
           { "UK", new Regex("(^1300\\d{6}$)|(^1800|1900|1902\\d{6}$)|(^0[2|3|7|8]{1}[0-9]{8}$)|(^13\\d{4}$)|(^04\\d{2,3}\\d{6}$)")},
           { "Netherlands", new Regex("(^\\+[0-9]{2}|^\\+[0-9]{2}\\(0\\)|^\\(\\+[0-9]{2}\\)\\(0\\)|^00[0-9]{2}|^0)([0-9]{9}$|[0-9\\-\\s]{10}$)")},
    };

    public static bool IsValidNumber(string phoneNumber, string country) {

        if (country != null && countryRegex.ContainsKey(country))
            return countryRegex[country].IsMatch(phoneNumber);
        else
            return false;
    }

    public static IEnumerable<string> Countries {
        get {
            return countryRegex.Keys;
        }
    }
}

Menangani Validasi dan Pelanggaran Logika Bisnis

Sekarang setelah kami menambahkan validasi dan kode aturan bisnis di atas, setiap kali kami mencoba membuat atau memperbarui Makan Malam, aturan logika validasi kami akan dievaluasi dan diberlakukan.

Pengembang dapat menulis kode seperti di bawah ini untuk secara proaktif menentukan apakah objek Makan Malam valid, dan mengambil daftar semua pelanggaran di dalamnya tanpa menimbulkan pengecualian apa pun:

Dinner dinner = dinnerRepository.GetDinner(5);

dinner.Country = "USA";
dinner.ContactPhone = "425-555-BOGUS";

if (!dinner.IsValid) {

    var errors = dinner.GetRuleViolations();
    
    // do something to fix the errors
}

Jika kita mencoba menyimpan Makan Malam dalam keadaan tidak valid, pengecualian akan dinaikkan ketika kita memanggil metode Save() pada DinnerRepository. Ini terjadi karena LINQ ke SQL secara otomatis memanggil metode parsial Dinner.OnValidate() kami sebelum menyimpan perubahan Makan Malam, dan kami menambahkan kode ke Dinner.OnValidate() untuk menimbulkan pengecualian jika ada pelanggaran aturan dalam Makan Malam. Kita dapat menangkap pengecualian ini dan secara reaktif mengambil daftar pelanggaran untuk diperbaiki:

Dinner dinner = dinnerRepository.GetDinner(5);

try {

    dinner.Country = "USA";
    dinner.ContactPhone = "425-555-BOGUS";

    dinnerRepository.Save();
}
catch {

    var errors = dinner.GetRuleViolations();

    // do something to fix errors
}

Karena validasi dan aturan bisnis kami diterapkan dalam lapisan model kami, dan bukan dalam lapisan UI, aturan tersebut akan diterapkan dan digunakan di semua skenario dalam aplikasi kami. Kita nantinya dapat mengubah atau menambahkan aturan bisnis dan memiliki semua kode yang berfungsi dengan objek Makan Malam kita menghormatinya.

Memiliki fleksibilitas untuk mengubah aturan bisnis di satu tempat, tanpa riak perubahan ini di seluruh aplikasi dan logika UI, adalah tanda aplikasi yang ditulis dengan baik, dan manfaat yang membantu kerangka kerja MVC mendorong.

Langkah Selanjutnya

Kita sekarang memiliki model yang bisa kita gunakan untuk mengkueri dan memperbarui database kita.

Sekarang mari kita tambahkan beberapa pengontrol dan tampilan ke proyek yang dapat kita gunakan untuk membangun pengalaman UI HTML di sekitarnya.