Sdílet prostřednictvím


Vytvoření modelu s ověřením obchodních pravidel

od Microsoftu

Stáhnout PDF

Toto je krok 3 bezplatného kurzu aplikace NerdDinner , který vás provede sestavením malé, ale úplné webové aplikace pomocí ASP.NET MVC 1.

Krok 3 ukazuje, jak vytvořit model, který můžeme použít k dotazování a aktualizaci databáze pro naši aplikaci NerdDinner.

Pokud používáte ASP.NET MVC 3, doporučujeme postupovat podle kurzů Začínáme S MVC 3 nebo MVC Music Store.

NerdDinner Krok 3: Sestavení modelu

V architektuře kontroleru zobrazení modelu termín "model" odkazuje na objekty, které představují data aplikace, a také na odpovídající logiku domény, která s ní integruje ověřování a obchodní pravidla. Model je v mnoha ohledech "srdcem" aplikace založené na MVC, a jak uvidíme později, zásadně řídí jeho chování.

Rozhraní ASP.NET MVC podporuje použití jakékoli technologie přístupu k datům a vývojáři si mohou vybrat z celé řady bohatých datových možností .NET pro implementaci svých modelů, mezi které patří: LINQ to Entities, LINQ to SQL, NHibernate, LLBLGen Pro, SubSonic, WilsonORM nebo jen nezpracované ADO.NET DataReaders nebo DataSets.

Pro naši aplikaci NerdDinner použijeme LINQ to SQL k vytvoření jednoduchého modelu, který bude poměrně přesně odpovídat návrhu databáze, a přidáme vlastní logiku ověřování a obchodní pravidla. Pak implementujeme třídu úložiště, která pomáhá abstrahovat implementaci trvalosti dat od zbytku aplikace a umožňuje snadné testování jednotek.

Technologie LINQ to SQL

LINQ to SQL je orm (objektový relační mapovač), který se dodává jako součást .NET 3.5.

LINQ to SQL poskytuje snadný způsob mapování databázových tabulek na třídy .NET, pro které můžeme kódovat. Pro naši aplikaci NerdDinner ji použijeme k mapování tabulek Dinners a RSVP v naší databázi na třídy Dinner a RSVP. Sloupce tabulek Večeře a RSVP budou odpovídat vlastnostem v předmětech Večeře a RSVP. Každý objekt Dinner a RSVP bude představovat samostatný řádek v rámci tabulek Dinners nebo RSVP v databázi.

LINQ to SQL nám umožňuje vyhnout se ručnímu vytváření příkazů SQL pro načtení a aktualizaci objektů Dinner a RSVP databázovými daty. Místo toho definujeme třídy Dinner a RSVP, způsob jejich mapování do a z databáze a vztahy mezi nimi. LINQ to SQL se pak postará o vygenerování odpovídající logiky spouštění SQL, která se použije za běhu při interakci a použití.

Podporu jazyka LINQ v rámci jazyka VB a C# můžeme použít k psaní výrazových dotazů, které z databáze načítají objekty Dinner a RSVP. To minimalizuje množství datového kódu, který potřebujeme napsat, a umožňuje nám vytvářet opravdu čisté aplikace.

Přidání tříd LINQ to SQL do projektu

Začneme kliknutím pravým tlačítkem na složku Modely v rámci našeho projektu a výběrem příkazu Nabídky Přidat> novou položku :

Snímek obrazovky se složkou Modely Nová položka je zvýrazněná. Modely jsou zvýrazněné a vybrané.

Tím se zobrazí dialogové okno Přidat novou položku. Provedeme filtrování podle kategorie Data a vybereme v ní šablonu "LINQ to SQL Třídy":

Snímek obrazovky s dialogovým oknem Přidat novou položku Data jsou zvýrazněná. Jsou vybrány a zvýrazněny třídy L I N Q až S Q L.

Položku pojmenujeme NerdDinner a klikneme na tlačítko Přidat. Visual Studio přidá do adresáře \Models soubor NerdDinner.dbml a pak otevře LINQ to SQL návrháře relačních objektů:

Snímek obrazovky s dialogovým oknem Nerd Dinner v sadě Visual Studio Je vybrán soubor Nerd Dinner dot d b m l.

Vytváření tříd datového modelu pomocí LINQ to SQL

LINQ to SQL nám umožňuje rychle vytvářet třídy datového modelu z existujícího schématu databáze. Abychom to mohli udělat, otevřeme databázi NerdDinner v Průzkumníku serveru a vybereme tabulky, které v ní chceme modelovat:

Snímek obrazovky s Průzkumníkem serveru Tabulky jsou rozbalené. Zvýrazní se večeře a R S V P.

Tabulky pak můžeme přetáhnout na plochu návrháře LINQ to SQL. Když to uděláme, LINQ to SQL automaticky vytvoří třídy Dinner a RSVP pomocí schématu tabulek (s vlastnostmi třídy, které se mapují na sloupce tabulky databáze):

Snímek obrazovky s dialogovým oknem Nerd Dinner Jsou zobrazeny třídy Dinner a R S V P.

Ve výchozím nastavení návrhář LINQ to SQL při vytváření tříd založených na schématu databáze automaticky "pluralizuje" názvy tabulek a sloupců. Příklad: Tabulka "Večeře" v našem příkladu výše měla za následek třídu "Večeře". Toto pojmenování tříd pomáhá zajistit, aby naše modely byly konzistentní s konvencemi vytváření názvů v .NET. Obvykle zjišťuji, že návrhář to opraví (zejména při přidávání velkého množství tabulek). Pokud se vám ale nelíbí název třídy nebo vlastnosti vygenerované návrhářem, můžete je vždy přepsat a změnit na libovolný název. Můžete to provést buď úpravou názvu entity nebo vlastnosti přímo v návrháři, nebo úpravou prostřednictvím mřížky vlastností.

Ve výchozím nastavení návrhář LINQ to SQL také kontroluje relace primárního klíče a cizího klíče tabulek a na jejich základě automaticky vytvoří výchozí "přidružení relací" mezi různými třídami modelu, které vytváří. Když jsme například přetáhli tabulky Dinners a RSVP do LINQ to SQL návrháře, bylo odvozeno přidružení relace 1:N mezi těmito dvěma na základě skutečnosti, že tabulka RSVP měla cizí klíč ke tabulce Dinners (to je označeno šipkou v návrháři):

Snímek obrazovky s tabulkami Dinner a R S V P Šipka je zvýrazněná a ukazuje ze stromu vlastností Večeře a stromu vlastností R S V P.

Výše uvedené přidružení způsobí, že LINQ to SQL do třídy RSVP přidá vlastnost "Dinner" se silným typem, kterou můžou vývojáři použít pro přístup k večeři přidružené k dané žádosti o odpověď. Způsobí to také, že třída Dinner bude mít vlastnost kolekce RSVPs, která vývojářům umožňuje načítat a aktualizovat objekty RSVP přidružené ke konkrétní večeři.

Níže si můžete prohlédnout příklad technologie IntelliSense v sadě Visual Studio, když vytvoříme nový objekt RSVP a přidáme ho do kolekce RSVP Večeřa. Všimněte si, že LINQ to SQL do objektu Dinner automaticky přidali kolekci RSVP:

Snímek obrazovky intellisense v sadě Visual Studio R S V Ps je zvýrazněno.

Přidáním objektu RSVP do kolekce RSVP Dinner říkáme LINQ to SQL, aby přidružil relaci cizího klíče mezi dinner a řádek RSVP v naší databázi:

Snímek obrazovky s objektem R S V P a kolekcí R S V P večeří

Pokud se vám nelíbí, jak návrhář namodeloval nebo pojmenoval přidružení tabulky, můžete ho přepsat. Stačí kliknout na přidružovací šipku v návrháři a získat přístup k jeho vlastnostem prostřednictvím mřížky vlastností, abyste ji mohli přejmenovat, odstranit nebo upravit. Pro naši aplikaci NerdDinner však výchozí asociační pravidla fungují dobře pro třídy datového modelu, které vytváříme, a můžeme použít pouze výchozí chování.

NerdDinnerDataContext – třída

Visual Studio automaticky vytvoří třídy .NET, které představují modely a databázové vztahy definované pomocí návrháře LINQ to SQL. Pro každý soubor návrháře LINQ to SQL přidaný do řešení se také vygeneruje třída LINQ to SQL DataContext. Vzhledem k tomu, že jsme naši položku třídy LINQ to SQL pojmenovali "NerdDinner", bude vytvořená třída DataContext nazvána "NerdDinnerDataContext". Tato třída NerdDinnerDataContext je primárním způsobem, jak budeme pracovat s databází.

Naše třída NerdDinnerDataContext zveřejňuje dvě vlastnosti - "Dinners" a "RSVPs" - které představují dvě tabulky, které jsme modelovali v databázi. Pomocí jazyka C# můžeme na tyto vlastnosti zapsat dotazy LINQ pro dotazování a načtení objektů Dinner a RSVP z databáze.

Následující kód ukazuje, jak vytvořit instanci NerdDinnerDataContext objektu a provést linq dotaz na něj získat sekvenci Dinners, ke kterým dojde v budoucnu. Visual Studio poskytuje při psaní dotazu LINQ úplnou technologii IntelliSense a objekty vrácené z tohoto dotazu jsou silného typu a podporují intellisense:

Snímek obrazovky se sadou Visual Studio Popis je zvýrazněný.

Kromě toho, že nám umožňuje dotazovat se na objekty Dinner a RSVP, nerdDinnerDataContext také automaticky sleduje všechny změny, které následně provedeme v objektech Dinner a RSVP, které prostřednictvím objektu načítáme. Tuto funkci můžeme použít ke snadnému uložení změn zpět do databáze bez nutnosti psát explicitní aktualizační kód SQL.

Následující kód například ukazuje, jak pomocí dotazu LINQ načíst jeden objekt Dinner z databáze, aktualizovat dvě vlastnosti Dinner a pak uložit změny zpět do databáze:

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

Objekt NerdDinnerDataContext ve výše uvedeném kódu automaticky sledoval změny vlastností provedené u objektu Dinner, který jsme z něj načetli. Když jsme volali metodu SubmitChanges(), spustí do databáze odpovídající příkaz SQL UPDATE, aby se aktualizované hodnoty zachovaly zpět.

Vytvoření třídy DinnerRepository

U malých aplikací je někdy vhodné, aby kontrolery pracovaly přímo proti třídě LINQ to SQL DataContext a do kontrolerů vložily dotazy LINQ. S tím, jak se ale aplikace zvětšovat, se tento přístup stává těžkopádným udržovat a testovat. Může to také vést k duplikování stejných dotazů LINQ na více místech.

Jedním z přístupů, které usnadňují údržbu a testování aplikací, je použití vzoru úložiště. Třída úložiště pomáhá zapouzdřovat dotazování na data a logiku trvalosti a abstrahuje podrobnosti implementace trvalosti dat z aplikace. Kromě čistějšího kódu aplikace může použití vzoru úložiště usnadnit v budoucnu změnu implementace úložiště dat a může usnadnit testování jednotek aplikace bez nutnosti skutečné databáze.

Pro naši aplikaci NerdDinner nadefinujeme třídu DinnerRepository s následujícím podpisem:

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

Poznámka: Později v této kapitole extrahujeme rozhraní IDinnerRepository z této třídy a povolíme injektáž závislostí s ním na našich kontrolerech. Pro začátek ale začneme jednoduše a budeme pracovat přímo se třídou DinnerRepository.

K implementaci této třídy klikneme pravým tlačítkem myši na složku "Modely" a zvolíme příkaz nabídky Přidat> novou položku . V dialogovém okně Přidat novou položku vybereme šablonu Třída a soubor pojmenujeme DinnerRepository.cs:

Snímek obrazovky se složkou Modely Možnost Přidat novou položku je zvýrazněná.

Pomocí následujícího kódu pak můžeme implementovat naši třídu DinnerRepository:

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

Načítání, aktualizace, vkládání a odstraňování pomocí třídy DinnerRepository

Teď, když jsme vytvořili třídu DinnerRepository, se podíváme na několik příkladů kódu, které ukazují běžné úlohy, které s ní můžeme dělat:

Příklady dotazování

Následující kód načte jednu večeři pomocí hodnoty DinnerID:

DinnerRepository dinnerRepository = new DinnerRepository();

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

Následující kód načte všechny nadcházející večeře a přejdou přes ně smyčky:

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

Příklady vložení a aktualizace

Následující kód ukazuje přidání dvou nových večeří. Přidání nebo úpravy úložiště se nezapíšou do databáze, dokud se v databázi nezavolá metoda Save(). LINQ to SQL automaticky zabalí všechny změny v databázové transakci , takže buď dojde ke všem změnám, nebo k žádné z nich nedojde při uložení úložiště:

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

Následující kód načte existující objekt Dinner a upraví dvě vlastnosti. Změny se potvrdí zpět do databáze při zavolání metody Save() v našem úložišti:

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

Následující kód načte večeři a pak do ní přidá ODPOVĚĎ. Provede to pomocí kolekce RSVP u objektu Dinner, který LINQ to SQL pro nás vytvořen (protože mezi těmito dvěma klíči v databázi existuje vztah primárního a cizího klíče). Tato změna se uloží zpět do databáze jako nový řádek tabulky RSVP při zavolání metody "Save()" v úložišti:

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

Příklad odstranění

Následující kód načte existující objekt Dinner a označí ho k odstranění. Při zavolání metody Save() v úložišti potvrdí odstranění zpět do databáze:

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

Integrace ověřování a logiky obchodních pravidel s třídami modelu

Integrace ověřování a logiky obchodních pravidel je klíčovou součástí každé aplikace, která pracuje s daty.

Ověřování schématu

Pokud jsou třídy modelu definovány pomocí návrháře LINQ to SQL, datové typy vlastností v třídách datového modelu odpovídají datovým typům databázové tabulky. Příklad: Pokud je sloupec EventDate v tabulce Dinners datetime, třída datového modelu vytvořená LINQ to SQL bude typu DateTime (což je integrovaný datový typ .NET). To znamená, že při pokusu o přiřazení celého čísla nebo logické hodnoty z kódu dojde k chybám kompilace, a pokud se pokusíte implicitně převést neplatný typ řetězce za běhu, dojde k chybě.

LINQ to SQL také automaticky zpracuje únik hodnot SQL při použití řetězců, což vám pomůže chránit před útoky prostřednictvím injektáže SQL při jejich použití.

Ověřování a logika obchodních pravidel

Ověření schématu je užitečné jako první krok, ale zřídka je dostačující. Většina scénářů z reálného světa vyžaduje možnost zadat bohatší logiku ověřování, která může zahrnovat více vlastností, spouštět kód a často mít povědomí o stavu modelu (například: je vytvářený /aktualizován/odstraněn, nebo ve stavu specifickém pro doménu, například "archivováno"). Existují různé vzory a architektury, které lze použít k definování a použití ověřovacích pravidel na třídy modelů, a existuje několik rozhraní založených na .NET, které lze použít k tomu. V ASP.NET aplikacích MVC můžete použít prakticky libovolný z nich.

Pro účely naší aplikace NerdDinner použijeme poměrně jednoduchý a jednoduchý vzor, kdy u objektu modelu Dinner zveřejníme vlastnost IsValid a metodu GetRuleViolations(). IsValid Vlastnost vrátí hodnotu true nebo false v závislosti na tom, zda jsou všechna platná a obchodní pravidla. Metoda GetRuleViolations() vrátí seznam všech chyb pravidla.

Implementujeme isValid a GetRuleViolations() pro náš model Dinner tak, že do projektu přidáme "částečnou třídu". Částečné třídy lze použít k přidání metod/vlastností/událostí do tříd spravovaných návrhářem sady VS (jako je třída Dinner vygenerovaná návrhářem LINQ to SQL) a zabránit tomu, aby nástroj zaneřádil náš kód. Novou částečnou třídu můžeme do našeho projektu přidat kliknutím pravým tlačítkem na složku \Models a výběrem příkazu nabídky Přidat novou položku. Pak můžeme zvolit šablonu Třída v dialogovém okně Přidat novou položku a pojmenovat ji Dinner.cs.

Snímek obrazovky se složkou Modely Je vybraná možnost Přidat novou položku. V dialogovém okně Přidat novou položku je zapsána tečka k večeři.

Kliknutím na tlačítko Přidat přidáte soubor Dinner.cs do našeho projektu a otevřete ho v integrovaném vývojovém prostředí . Pomocí následujícího kódu pak můžeme implementovat základní architekturu vynucování pravidel nebo ověřování:

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

Několik poznámek k výše uvedenému kódu:

  • Třída Dinner má před sebou klíčové slovo "partial" – to znamená, že kód obsažený v ní bude kombinován s třídou vygenerovanou/udržovanou návrhářem LINQ to SQL a zkompilován do jedné třídy.
  • Třída RuleViolation je pomocná třída, kterou přidáme do projektu, která nám umožňuje poskytnout další podrobnosti o porušení pravidla.
  • Metoda Dinner.GetRuleViolations() způsobí, že se vyhodnocují naše ověřovací a obchodní pravidla (brzy je implementujeme). Poté vrátí zpět sekvenci objektů RuleViolation, které poskytují další podrobnosti o chybách pravidla.
  • The Dinner.IsValid vlastnost poskytuje pohodlnou pomocnou službu, která indikuje, zda má objekt Dinner aktivní RuleViolations. Vývojář ho může kdykoli aktivně kontrolovat pomocí objektu Dinner (a nevyvolá výjimku).
  • Částečná metoda Dinner.OnValidate() je háček, který LINQ to SQL poskytuje a který nám umožňuje být upozorněni, kdykoli se objekt Dinner chystá zachovat v databázi. Naše výše uvedená implementace OnValidate() zajišťuje, že Dinner nemá před uložením žádné RuleViolations. Pokud je v neplatném stavu vyvolá výjimku, která způsobí, že LINQ to SQL transakci přeruší.

Tento přístup poskytuje jednoduchou architekturu, do které můžeme integrovat ověřování a obchodní pravidla. Prozatím do metody GetRuleViolations() přidáme následující pravidla:

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

K vrácení posloupnosti libovolných hodnot RuleViolations používáme funkci "yield return" jazyka C#. Prvních šest kontrol pravidla výše jednoduše vynucuje, že vlastnosti řetězce u večeře nesmí být null nebo prázdné. Poslední pravidlo je o něco zajímavější a volá metodu pomocné rutiny PhoneValidator.IsValidNumber(), kterou můžeme přidat do našeho projektu, abychom ověřili, že formát čísla ContactPhone odpovídá zemi nebo oblasti Večeře.

Můžeme použít . PODPORA regulárních výrazů pro implementaci této podpory ověření telefonem. Níže je jednoduchá implementace PhoneValidatoru, kterou můžeme přidat do našeho projektu a která nám umožňuje přidat kontroly vzorů Regulárních výrazů specifických pro zemi nebo oblast:

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

Zpracování ověřování a porušení obchodní logiky

Když jsme teď přidali výše uvedené ověření a kód obchodního pravidla, kdykoli se pokusíme vytvořit nebo aktualizovat aplikaci Dinner, naše pravidla logiky ověřování se vyhodnotí a vynutí.

Vývojáři můžou napsat kód podobný následujícímu, který proaktivně určí, jestli je objekt Dinner platný, a načtou seznam všech porušení, aniž by vyvolal výjimky:

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
}

Pokud se pokusíme uložit položku Dinner v neplatném stavu, vyvolá se při volání metody Save() v části DinnerRepository výjimka. K tomu dochází, protože LINQ to SQL automaticky volá částečnou metodu Dinner.OnValidate() předtím, než uloží změny večeře, a do části Dinner.OnValidate() jsme přidali kód, který vyvolá výjimku, pokud ve večeři dojde k porušení pravidel. Tuto výjimku můžeme zachytit a reaktivně načíst seznam porušení, která se mají opravit:

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
}

Vzhledem k tomu, že se naše ověřovací a obchodní pravidla implementují v rámci naší vrstvy modelu, a ne v rámci vrstvy uživatelského rozhraní, budou použita a použita ve všech scénářích v rámci naší aplikace. Později můžeme změnit nebo přidat obchodní pravidla a veškerý kód, který pracuje s objekty Večeře, je bude respektovat.

Flexibilita při změnách obchodních pravidel na jednom místě, aniž by se tyto změny měnily v celé aplikaci a v logice uživatelského rozhraní, je známkou dobře napsané aplikace a výhodou, kterou architektura MVC podporuje.

Další krok

Teď máme model, který můžeme použít k dotazování a aktualizaci databáze.

Teď do projektu přidáme několik kontrolerů a zobrazení, které můžeme použít k vytvoření uživatelského rozhraní HTML.