Aracılığıyla paylaş


İş Kuralı Doğrulamaları ile Model Oluşturma

Microsoft tarafından

PDF’yi İndir

Bu, ASP.NET MVC 1 kullanarak küçük ama eksiksiz bir web uygulaması oluşturmayı gösteren ücretsiz bir "NerdDinner" uygulaması öğreticisinin 3. adımıdır.

3. adım, NerdDinner uygulamamız için veritabanını hem sorgulamak hem de güncelleştirmek için kullanabileceğimiz bir modelin nasıl oluşturulacağını gösterir.

ASP.NET MVC 3 kullanıyorsanız , MVC 3 veya MVC Music Store ile Çalışmaya Başlama öğreticilerini izlemenizi öneririz.

NerdDinner 3. Adım: Modeli Oluşturma

Model-view-controller çerçevesinde "model" terimi, uygulamanın verilerini temsil eden nesnelerin yanı sıra doğrulama ve iş kurallarını onunla tümleştiren ilgili etki alanı mantığını ifade eder. Model birçok yönden MVC tabanlı bir uygulamanın "kalbidir" ve daha sonra göreceğimiz gibi bu uygulamanın davranışını temelden yönlendirmektedir.

ASP.NET MVC çerçevesi herhangi bir veri erişim teknolojisinin kullanılmasını destekler ve geliştiriciler modellerini uygulamak için çeşitli zengin .NET veri seçenekleri arasından seçim yapabilir: LINQ to Entities, LINQ to SQL, NHibernate, LLBLGen Pro, SubSonic, WilsonORM veya yalnızca ham ADO.NET DataReaders veya DataSets.

NerdDinner uygulamamızda, veritabanı tasarımımıza oldukça yakın olan ve bazı özel doğrulama mantığı ve iş kuralları ekleyen basit bir model oluşturmak için LINQ to SQL kullanacağız. Ardından, veri kalıcılığı uygulamasını uygulamanın geri kalanından soyutlamanıza yardımcı olan ve kolayca birim testi yapmamızı sağlayan bir depo sınıfı uygulayacağız.

LINQ to SQL

LINQ to SQL, .NET 3.5'in bir parçası olarak gelen bir ORM 'dir (nesne ilişkisel eşleyici).

LINQ to SQL, veritabanı tablolarını kodlayabileceğiniz .NET sınıflarına eşlemek için kolay bir yol sağlar. NerdDinner uygulamamızda, veritabanımız içindeki Dinners ve RSVP tablolarını Dinner ve RSVP sınıflarıyla eşlemek için bu uygulamayı kullanacağız. Dinners ve RSVP tablolarının sütunları Dinner ve RSVP sınıflarındaki özelliklere karşılık gelir. Her Dinner ve RSVP nesnesi, veritabanındaki Dinners veya RSVP tablolarında ayrı bir satırı temsil eder.

LINQ to SQL, Dinner ve RSVP nesnelerini veritabanı verileriyle almak ve güncelleştirmek için SQL deyimlerini el ile oluşturmak zorunda kalmamamızı sağlar. Bunun yerine, Dinner ve RSVP sınıflarını, bunların veritabanından nasıl eşlendiklerini ve aralarındaki ilişkileri tanımlayacağız. LINQ to SQL daha sonra etkileşime girip kullandığımızda çalışma zamanında kullanılacak uygun SQL yürütme mantığını oluşturma işlemini üstlenir.

VERITABANıNDAn Dinner ve RSVP nesnelerini alan açıklayıcı sorgular yazmak için VB ve C# içinde LINQ dil desteğini kullanabiliriz. Bu, yazmamız gereken veri kodu miktarını en aza indirir ve gerçekten temiz uygulamalar oluşturmamızı sağlar.

Projemize LINQ to SQL Sınıfları Ekleme

Projemizdeki "Modeller" klasörüne sağ tıklayıp Yeni Öğe Ekle> menü komutunu seçerek başlayacağız:

Modeller klasörünün ekran görüntüsü. Yeni öğe vurgulanır. Modeller vurgulanır ve seçilir.

Bu, "Yeni Öğe Ekle" iletişim kutusunu açar. "Veri" kategorisine göre filtreleyip içindeki "LINQ to SQL Sınıfları" şablonunu seçeceğiz:

Yeni Öğe Ekle iletişim kutusunun ekran görüntüsü. Veriler vurgulanır. L I N Q-S Q L Sınıfları seçilir ve vurgulanır.

Öğeye "NerdDinner" adını vereceğiz ve "Ekle" düğmesine tıklıyoruz. Visual Studio\ Models dizinimizin altına bir NerdDinner.dbml dosyası ekleyecek ve ardından nesne ilişkisel tasarımcısını LINQ to SQL açacaktır:

Visual Studio'da İnek Yemeği iletişim kutusunun ekran görüntüsü. İnek Akşam Yemeği nokta d b m l dosyası seçilidir.

LINQ to SQL ile Veri Modeli Sınıfları Oluşturma

LINQ to SQL, mevcut veritabanı şemasından hızlı bir şekilde veri modeli sınıfları oluşturmamızı sağlar. Bunu yapmak için Sunucu Gezgini'nde NerdDinner veritabanını açıp modellemesini istediğimiz Tabloları seçeceğiz:

Sunucu Gezgini'nin ekran görüntüsü. Tablolar genişletilir. Akşam yemekleri ve R S V P vurgulanır.

Ardından tabloları LINQ to SQL tasarımcı yüzeyine sürükleyebilirsiniz. Bunu yaptığımızda LINQ to SQL otomatik olarak tabloların şemasını kullanarak Akşam Yemeği ve RSVP sınıfları oluşturur (veritabanı tablosu sütunlarına eşleyen sınıf özellikleriyle):

İnek Yemeği iletişim kutusunun ekran görüntüsü. Dinner ve R S V P sınıfları gösterilmektedir.

Varsayılan olarak LINQ to SQL tasarımcısı, veritabanı şemasını temel alan sınıflar oluşturduğunda tablo ve sütun adlarını otomatik olarak "çoğullaştırır". Örneğin: Yukarıdaki örneğimizdeki "Dinners" tablosu bir "Dinner" sınıfına neden oldu. Bu sınıf adlandırması modellerimizin .NET adlandırma kurallarıyla tutarlı olmasına yardımcı olur ve genellikle tasarımcının bunu düzeltmesini sağlamanın uygun olduğunu görüyorum (özellikle çok fazla tablo eklerken). Tasarımcının oluşturduğu bir sınıfın veya özelliğin adını beğenmezseniz, istediğiniz zaman geçersiz kılabilir ve istediğiniz adla değiştirebilirsiniz. Bunu yapmak için, varlık/özellik adını tasarımcı içinde satır içinde düzenleyebilir veya özellik kılavuzu aracılığıyla değiştirebilirsiniz.

Varsayılan olarak LINQ to SQL tasarımcısı tabloların birincil anahtar/yabancı anahtar ilişkilerini de inceler ve bunları temel alarak oluşturduğu farklı model sınıfları arasında otomatik olarak varsayılan "ilişki ilişkilendirmeleri" oluşturur. Örneğin, Dinners ve RSVP tablolarını LINQ to SQL tasarımcısına sürüklediğimizde, RSVP tablosunun Dinners tablosunda yabancı anahtara sahip olması (bu, tasarımcıdaki okla gösterilir) temelinde ikisi arasında bire çok ilişki ilişkisi olduğu sonucuna varıldı:

Akşam Yemeği ve R S V P tablolarının ekran görüntüsü. Akşam yemeği özellikleri ağacından ve R S V P özellikler ağacından bir ok vurgulanır ve işaret edilir.

Yukarıdaki ilişkilendirme, LINQ to SQL geliştiricilerin belirli bir RSVP ile ilişkili Akşam Yemeği'ne erişmek için kullanabileceği RSVP sınıfına kesin olarak belirlenmiş bir "Dinner" özelliği eklemesine neden olur. Ayrıca Dinner sınıfının geliştiricilerin belirli bir Akşam Yemeği ile ilişkili RSVP nesnelerini almasını ve güncelleştirmesini sağlayan bir "RSVP" koleksiyon özelliğine sahip olmasına neden olur.

Aşağıda, yeni bir RSVP nesnesi oluşturup bunu bir Dinner'ın RSVP koleksiyonuna eklediğimizde Visual Studio'da intellisense örneğini görebilirsiniz. LINQ to SQL Dinner nesnesine otomatik olarak bir "RSVP" koleksiyonu eklediğine dikkat edin:

Visual Studio'da intellisense'in ekran görüntüsü. R S V Ps vurgulanır.

RSVP nesnesini Dinner'ın RSVP koleksiyonuna ekleyerek LINQ to SQL veritabanımızdaki Akşam Yemeği ile RSVP satırı arasında yabancı anahtar ilişkisini ilişkilendirmesini söylüyoruz:

R S V P nesnesinin ve Dinner'un R S V P koleksiyonunun ekran görüntüsü.

Tasarımcının tablo ilişkilendirmesini modellemesini veya adlandırılmış şeklini beğenmezseniz, bunu geçersiz kılabilirsiniz. Yeniden adlandırmak, silmek veya değiştirmek için tasarımcının içindeki ilişkilendirme okuna tıklayın ve özellik kılavuzu aracılığıyla özelliklerine erişin. Ancak NerdDinner uygulamamız için varsayılan ilişkilendirme kuralları, oluşturmakta olduğumuz veri modeli sınıfları için iyi çalışır ve yalnızca varsayılan davranışı kullanabiliriz.

NerdDinnerDataContext Sınıfı

Visual Studio, LINQ to SQL tasarımcısı kullanılarak tanımlanan modelleri ve veritabanı ilişkilerini temsil eden .NET sınıflarını otomatik olarak oluşturur. Çözüme eklenen her LINQ to SQL tasarımcısı dosyası için bir LINQ to SQL DataContext sınıfı da oluşturulur. LINQ to SQL sınıf öğemizi "NerdDinner" olarak adlandırdığımız için oluşturulan DataContext sınıfı "NerdDinnerDataContext" olarak adlandırılır. Bu NerdDinnerDataContext sınıfı, veritabanıyla etkileşim kurmanın birincil yoludur.

NerdDinnerDataContext sınıfımız, veritabanında modellediğimiz iki tabloyu temsil eden "Dinners" ve "RSVP" adlı iki özelliği kullanıma sunar. Veritabanından Dinner ve RSVP nesnelerini sorgulamak ve almak üzere bu özelliklere karşı LINQ sorguları yazmak için C# kullanabiliriz.

Aşağıdaki kodda bir NerdDinnerDataContext nesnesinin örneğini oluşturma ve gelecekte gerçekleşen Akşam Yemekleri dizisi elde etmek için buna karşı LINQ sorgusu gerçekleştirme işlemi gösterilmektedir. Visual Studio, LINQ sorgusu yazarken tam intellisense sağlar ve bu sorgudan döndürülen nesneler kesin olarak yazılır ve intellisense'i de destekler:

Visual Studio'nun ekran görüntüsü. Açıklama vurgulanmış.

NerdDinnerDataContext, Akşam Yemeği ve RSVP nesnelerini sorgulamamıza izin vermenin yanı sıra, daha sonra akşam yemeği ve rsvp nesnelerinde yaptığımız değişiklikleri de otomatik olarak izler. Açık SQL güncelleştirme kodu yazmak zorunda kalmadan değişiklikleri kolayca veritabanına geri kaydetmek için bu işlevi kullanabiliriz.

Örneğin, aşağıdaki kod veritabanından tek bir Dinner nesnesini almak, Dinner özelliklerinden ikisini güncelleştirmek ve ardından değişiklikleri veritabanına geri kaydetmek için LINQ sorgusunun nasıl kullanılacağını gösterir:

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

Yukarıdaki koddaki NerdDinnerDataContext nesnesi, içinden aldığımız Dinner nesnesinde yapılan özellik değişikliklerini otomatik olarak izlemektedir. "SubmitChanges()" yöntemini çağırdığımızda, güncelleştirilmiş değerleri yeniden kalıcı hale getirmek için veritabanında uygun bir SQL "UPDATE" deyimi yürütür.

DinnerRepository Sınıfı Oluşturma

Küçük uygulamalar için bazen Denetleyicilerin doğrudan bir LINQ to SQL DataContext sınıfına karşı çalışması ve LINQ sorgularını Denetleyiciler içine eklemesi normaldir. Ancak uygulamalar büyüdükçe, bu yaklaşımı korumak ve test etmek zahmetli hale gelir. Aynı LINQ sorgularını birden çok yerde yinelememize de yol açabilir.

Uygulamaların bakımını ve testini kolaylaştırabilecek bir yaklaşım da "depo" deseni kullanmaktır. Depo sınıfı, veri sorgulama ve kalıcılık mantığının kapsüllenmesine yardımcı olur ve uygulamadan veri kalıcılığının uygulama ayrıntılarını soyutlar. Uygulama kodunu temizlemenin yanı sıra, bir depo düzeni kullanmak gelecekte veri depolama uygulamalarını değiştirmeyi kolaylaştırabilir ve gerçek bir veritabanı gerektirmeden bir uygulamanın birim testini kolaylaştırmaya yardımcı olabilir.

NerdDinner uygulamamız için aşağıdaki imzaya sahip bir DinnerRepository sınıfı tanımlayacağız:

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

Not: Bu bölümün ilerleyen bölümlerinde bu sınıftan bir IDinnerRepository arabirimi ayıklayacağız ve denetleyicilerimizle bağımlılık eklemeyi etkinleştireceğiz. Bununla birlikte, başlangıç olarak basit bir başlangıç yapacağız ve doğrudan DinnerRepository sınıfıyla çalışacağız.

Bu sınıfı uygulamak için "Modeller" klasörümüze sağ tıklayıp Yeni Öğe Ekle> menü komutunu seçeceğiz. "Yeni Öğe Ekle" iletişim kutusunda "Sınıf" şablonunu seçip dosyayı "DinnerRepository.cs" olarak adlandıracağız:

Modeller klasörünün ekran görüntüsü. Yeni Öğe Ekle vurgulanmış.

Ardından aşağıdaki kodu kullanarak DinnerRepository sınıfımızı uygulayabiliriz:

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

DinnerRepository sınıfını kullanarak Alma, Güncelleştirme, Ekleme ve Silme

DinnerRepository sınıfımızı oluşturduğumuza göre, bununla gerçekleştirebileceğimiz yaygın görevleri gösteren birkaç kod örneğine göz atalım:

Örnekleri Sorgulama

Aşağıdaki kod DinnerID değerini kullanarak tek bir Dinner alır:

DinnerRepository dinnerRepository = new DinnerRepository();

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

Aşağıdaki kod yaklaşan tüm akşam yemeklerini alır ve bunlar üzerinde döngüler kurar:

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

Örnek Ekleme ve Güncelleştirme

Aşağıdaki kodda iki yeni akşam yemeği ekleme gösterilmektedir. Depodaki eklemeler/değişiklikler, "Save()" yöntemi çağrılana kadar veritabanına işlenmez. LINQ to SQL veritabanı işlemindeki tüm değişiklikleri otomatik olarak sarmalar; bu nedenle depomuz kaydedildiğinde tüm değişiklikler gerçekleşir veya hiçbiri yapmaz:

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

Aşağıdaki kod mevcut bir Dinner nesnesini alır ve üzerindeki iki özelliği değiştirir. Depomuzda "Save()" yöntemi çağrıldığında değişiklikler veritabanına geri işlenir:

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

Aşağıdaki kod bir akşam yemeği alır ve sonra buna bir RSVP ekler. Bunu, LINQ to SQL bizim için oluşturduğu Dinner nesnesindeki RSVP koleksiyonunu kullanarak yapar (veritabanındaki ikisi arasında birincil anahtar/yabancı anahtar ilişkisi olduğundan). Bu değişiklik, depoda "Save()" yöntemi çağrıldığında veritabanında yeni bir RSVP tablo satırı olarak kalıcı hale getirilir:

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

Örneği Sil

Aşağıdaki kod mevcut bir Dinner nesnesini alır ve silinecek şekilde işaretler. Depoda "Save()" yöntemi çağrıldığında silme işlemini veritabanına geri kaydeder:

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

Doğrulama ve İş Kuralı Mantığını Model Sınıflarıyla Tümleştirme

Doğrulama ve iş kuralı mantığının tümleştirilmesi, verilerle çalışan tüm uygulamaların önemli bir parçasıdır.

Şema Doğrulama

Model sınıfları LINQ to SQL tasarımcısı kullanılarak tanımlandığında, veri modeli sınıflarındaki özelliklerin veri türleri veritabanı tablosunun veri türlerine karşılık gelir. Örneğin: Dinners tablosundaki "EventDate" sütunu bir "datetime" ise, LINQ to SQL tarafından oluşturulan veri modeli sınıfı "DateTime" türündedir (yerleşik bir .NET veri türüdür). Bu, koddan ona bir tamsayı veya boole atamayı denerseniz derleme hataları alacağınız ve çalışma zamanında geçerli olmayan bir dize türünü örtük olarak buna dönüştürmeye çalıştığınızda otomatik olarak hata oluşturacağı anlamına gelir.

LINQ to SQL, dizeleri kullanırken kaçış SQL değerlerini sizin için otomatik olarak işler. Bu, dizeleri kullanırken SQL ekleme saldırılarına karşı korunmanıza yardımcı olur.

Doğrulama ve İş Kuralı Mantığı

Şema doğrulaması ilk adım olarak yararlıdır, ancak nadiren yeterlidir. Gerçek dünya senaryolarının çoğu, birden çok özelliğe yayılabilir, kod yürütebilir ve genellikle bir modelin durumuyla ilgili farkındalığı olan daha zengin doğrulama mantığı belirtebilmesini gerektirir (örneğin: oluşturuldu mu/güncelleştiriliyor/siliniyor mu veya "arşivlendi" gibi etki alanına özgü bir durumda). Model sınıflarına doğrulama kuralları tanımlamak ve uygulamak için kullanılabilecek çeşitli desenler ve çerçeveler vardır ve bu konuda yardımcı olmak için kullanılabilecek çeşitli .NET tabanlı çerçeveler vardır. Bunların hemen hemen herhangi birini ASP.NET MVC uygulamalarında kullanabilirsiniz.

NerdDinner uygulamamızın amaçları doğrultusunda, Dinner model nesnemizde isValid özelliğini ve GetRuleViolations() yöntemini kullanıma sunduğumuz görece basit ve düz bir desen kullanacağız. IsValid özelliği, doğrulama ve iş kurallarının tümünün geçerli olup olmadığına bağlı olarak true veya false döndürür. GetRuleViolations() yöntemi tüm kural hatalarının listesini döndürür.

Projemize "kısmi sınıf" ekleyerek Akşam Yemeği modelimiz için IsValid ve GetRuleViolations() uygulayacağız. Kısmi sınıflar, vs tasarımcısı tarafından tutulan sınıflara (LINQ to SQL tasarımcısı tarafından oluşturulan Dinner sınıfı gibi) yöntemler/özellikler/olaylar eklemek ve aracın kodumuzla uğraşmasını önlemeye yardımcı olmak için kullanılabilir. \Models klasörüne sağ tıklayıp "Yeni Öğe Ekle" menü komutunu seçerek projemize yeni bir kısmi sınıf ekleyebiliriz. Daha sonra "Yeni Öğe Ekle" iletişim kutusundan "Sınıf" şablonunu seçebilir ve dinner.cs olarak adlandırabiliriz.

Modeller klasörünün ekran görüntüsü. Yeni Öğe Ekle seçilidir. Akşam yemeği noktası c sn, Yeni Öğe Ekle iletişim kutusunda yazılır.

"Ekle" düğmesine tıkladığınızda projemize bir Dinner.cs dosyası eklenir ve IDE içinde açılır. Ardından aşağıdaki kodu kullanarak temel bir kural/doğrulama zorlama çerçevesi uygulayabiliriz:

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

Yukarıdaki kod hakkında birkaç not:

  • Dinner sınıfı bir "kısmi" anahtar sözcüğüyle önsöze alınır. Bu, içindeki kodun LINQ to SQL tasarımcısı tarafından oluşturulan/tutulan sınıfla birleştirileceği ve tek bir sınıfa derlendiği anlamına gelir.
  • RuleViolation sınıfı, kural ihlali hakkında daha fazla ayrıntı sağlamamıza olanak tanıyan projeye ekleyebileceğimiz bir yardımcı sınıftır.
  • Dinner.GetRuleViolations() yöntemi doğrulama ve iş kurallarımızın değerlendirilmesine neden olur (bunları kısa süre içinde uygulayacağız). Ardından, kural hataları hakkında daha fazla ayrıntı sağlayan ruleViolation nesnelerinin bir dizisini geri döndürür.
  • Dinner.IsValid özelliği, Dinner nesnesinin herhangi bir etkin RuleViolations olup olmadığını gösteren kullanışlı bir yardımcı özelliği sağlar. Herhangi bir zamanda Dinner nesnesi kullanılarak bir geliştirici tarafından proaktif olarak denetlenebilir (ve özel durum oluşturmaz).
  • Dinner.OnValidate() kısmi yöntemi, LINQ to SQL tarafından sunulan ve Dinner nesnesinin veritabanında kalıcı hale getirmek üzere olduğu her zaman bize bildirilmesini sağlayan bir kancadır. Yukarıdaki OnValidate() uygulamamız, Akşam Yemeğinde kaydedilmeden önce RuleViolations olmamasını sağlar. Geçersiz bir durumdaysa, LINQ to SQL işlemi durdurmasına neden olacak bir özel durum oluşturur.

Bu yaklaşım, doğrulama ve iş kurallarını tümleştirebileceğimiz basit bir çerçeve sağlar. Şimdilik GetRuleViolations() yöntemimize aşağıdaki kuralları ekleyelim:

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

Herhangi bir RuleViolations dizisini döndürmek için C# öğesinin "verim döndürme" özelliğini kullanıyoruz. Yukarıdaki ilk altı kural denetimi, akşam yemeğimizdeki bu dize özelliklerini zorunlu kılamaz veya boş olamaz. Son kural biraz daha ilginçtir ve ContactPhone numarası biçiminin Akşam Yemeğinin ülke/bölgesiyle eşleşip eşleşmediğini doğrulamak için projemize ekleyebileceğimiz bir PhoneValidator.IsValidNumber() yardımcı yöntemini çağırır.

kullanabiliriz. NET'in bu telefon doğrulama desteğini uygulamak için normal ifade desteği. Aşağıda, ülkeye/bölgeye özgü Regex desen denetimleri eklememizi sağlayan projemize ekleyebileceğimiz basit bir PhoneValidator uygulaması yer almaktadır:

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

Doğrulama ve İş Mantığı İhlallerini İşleme

Yukarıdaki doğrulama ve iş kuralı kodunu eklediğimize göre, akşam yemeği oluşturmaya veya güncelleştirmeye her denediğimizde doğrulama mantığı kurallarımız değerlendirilecek ve zorunlu kılınacaktır.

Geliştiriciler, bir Dinner nesnesinin geçerli olup olmadığını proaktif olarak belirlemek ve özel durum oluşturmadan içindeki tüm ihlallerin listesini almak için aşağıdaki gibi bir kod yazabilir:

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
}

Bir Akşam Yemeğini geçersiz bir durumda kaydetmeye çalışırsak, DinnerRepository üzerinde Save() yöntemini çağırdığımızda bir özel durum oluşur. Bunun nedeni, LINQ to SQL Akşam Yemeğinin değişikliklerini kaydetmeden önce Dinner.OnValidate() kısmi yöntemimizi otomatik olarak çağırması ve Akşam Yemeğinde herhangi bir kural ihlali olması durumunda özel durum tetiklemesi için Dinner.OnValidate() kodu eklememizdir. Bu özel durumu yakalayabilir ve düzeltilmesi gereken ihlallerin listesini yeniden alabilir:

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
}

Doğrulama ve iş kurallarımız kullanıcı arabirimi katmanında değil model katmanımızda uygulandığından, uygulamamız içindeki tüm senaryolarda uygulanır ve kullanılır. Daha sonra iş kurallarını değiştirebilir veya ekleyebiliriz ve Dinner nesnelerimizle çalışan tüm kodların bu kurallara uymalarını sağlayabiliriz.

Uygulama ve kullanıcı arabirimi mantığında bu değişikliklere neden olmadan iş kurallarını tek bir yerde değiştirme esnekliğine sahip olmak, iyi yazılmış bir uygulamanın işareti ve MVC çerçevesinin teşvik etmeye yardımcı olduğu bir avantajdır.

Sonraki Adım

Artık veritabanımızı hem sorgulamak hem de güncelleştirmek için kullanabileceğimiz bir modelimiz var.

Şimdi projeye çevresinde bir HTML kullanıcı arabirimi deneyimi oluşturmak için kullanabileceğimiz bazı denetleyiciler ve görünümler ekleyelim.