Aracılığıyla paylaş


4. Yineleme – Uygulamanın gevşek bir şekilde bağlanmasını sağlama (C#)

Microsoft tarafından

Kodu İndir

Bu dördüncü yinelemede, Contact Manager uygulamasının bakımını ve değiştirilmesini kolaylaştırmak için çeşitli yazılım tasarım desenlerinden yararlanıyoruz. Örneğin, uygulamamızı Depo desenini ve Bağımlılık Ekleme düzenini kullanacak şekilde yeniden düzenleyeceğiz.

MVC Uygulaması ASP.NET Kişi Yönetimi Oluşturma (C#)

Bu öğretici serisinde, baştan sona bir Kişi Yönetimi uygulamasının tamamını oluşturuyoruz. Contact Manager uygulaması kişi listesi için kişi bilgilerini (adlar, telefon numaraları ve e-posta adresleri) depolamanıza olanak tanır.

Uygulamayı birden çok yineleme üzerinden oluşturuyoruz. Her yinelemede uygulamayı aşamalı olarak iyileştiriyoruz. Bu çoklu yineleme yaklaşımının amacı, her değişikliğin nedenini anlamanıza olanak tanımaktır.

  • Yineleme #1 - Uygulamayı oluşturun. İlk yinelemede, Contact Manager'ı mümkün olan en basit şekilde oluştururuz. Temel veritabanı işlemleri için destek ekliyoruz: Oluşturma, Okuma, Güncelleştirme ve Silme (CRUD).

  • Yineleme #2 - Uygulamanın güzel görünmesini sağlayın. Bu yinelemede, varsayılan ASP.NET MVC görünüm ana sayfasını ve basamaklı stil sayfasını değiştirerek uygulamanın görünümünü iyileştireceğiz.

  • Yineleme #3 - Form doğrulaması ekleyin. Üçüncü yinelemede temel form doğrulaması ekliyoruz. Gerekli form alanlarını tamamlamadan kişilerin form göndermesini engelleriz. Ayrıca e-posta adreslerini ve telefon numaralarını da doğrularız.

  • Yineleme #4 - Uygulamayı gevşek bir şekilde birleştirin. Bu dördüncü yinelemede, Contact Manager uygulamasının bakımını ve değiştirilmesini kolaylaştırmak için çeşitli yazılım tasarım desenlerinden yararlanıyoruz. Örneğin, uygulamamızı Depo desenini ve Bağımlılık Ekleme düzenini kullanacak şekilde yeniden düzenleyeceğiz.

  • Yineleme #5 - Birim testleri oluşturun. Beşinci yinelemede, birim testleri ekleyerek uygulamamızın bakımını ve değiştirilmesini kolaylaştırıyoruz. Veri modeli sınıflarımızla dalga geçiyoruz ve denetleyicilerimiz ve doğrulama mantığımız için birim testleri oluşturuyoruz.

  • Yineleme #6 - Test temelli geliştirmeyi kullanın. Bu altıncı yinelemede, önce birim testleri yazıp birim testlerine kod yazarak uygulamamıza yeni işlevler ekleyeceğiz. Bu yinelemede kişi grupları ekleyeceğiz.

  • Yineleme #7 - Ajax işlevselliğini ekleyin. Yedinci yinelemede Ajax desteği ekleyerek uygulamamızın yanıt hızını ve performansını iyileştiriyoruz.

Bu Yineleme

Contact Manager uygulamasının bu dördüncü yinelemesinde, uygulamayı daha gevşek bir şekilde bağlanmış hale getirmek için uygulamayı yeniden düzenleyeceğiz. Bir uygulama gevşek bir şekilde bağlandığında, uygulamanın diğer bölümlerindeki kodu değiştirmenize gerek kalmadan uygulamanın bir bölümündeki kodu değiştirebilirsiniz. Gevşek şekilde bağlanmış uygulamalar değişmeye daha dayanıklıdır.

Şu anda, Contact Manager uygulaması tarafından kullanılan tüm veri erişimi ve doğrulama mantığı denetleyici sınıflarında yer almaktadır. Bu kötü bir fikir. Uygulamanızın bir bölümünü değiştirmeniz gerektiğinde, hataları uygulamanızın başka bir bölümüne ekleme riskiyle karşılaşırsınız. Örneğin, doğrulama mantığınızı değiştirirseniz veri erişiminize veya denetleyici mantığınıza yeni hatalar ekleme riskiyle karşılaşırsınız.

Not

(SRP), bir sınıfın değiştirmek için hiçbir zaman birden fazla nedeni olmamalıdır. Denetleyici, doğrulama ve veritabanı mantığının karıştırılması, Tek Sorumluluk İlkesi'nin çok büyük bir ihlalidir.

Uygulamanızı değiştirmenizin birkaç nedeni olabilir. Uygulamanıza yeni bir özellik eklemeniz, uygulamanızdaki bir hatayı düzeltmeniz veya uygulamanızın bir özelliğinin uygulanma şeklini değiştirmeniz gerekebilir. Uygulamalar nadiren statiktir. Zamanla büyüme ve sessize alma eğilimindedirler.

Örneğin, veri erişim katmanınızı uygulama şeklinizi değiştirmeye karar vermenizi düşünün. Contact Manager uygulaması şu anda veritabanına erişmek için Microsoft Entity Framework'i kullanıyor. Ancak, ADO.NET Data Services veya NHibernate gibi yeni veya alternatif bir veri erişim teknolojisine geçmeyi düşünebilirsiniz. Ancak, veri erişim kodu doğrulama ve denetleyici kodundan yalıtılmadığından, veri erişimiyle doğrudan ilgili olmayan diğer kodları değiştirmeden uygulamanızdaki veri erişim kodunu değiştirmenin hiçbir yolu yoktur.

Bir uygulama gevşek bir şekilde bağlandığında, diğer yandan, bir uygulamanın diğer bölümlerine dokunmadan uygulamanın bir bölümünde değişiklik yapabilirsiniz. Örneğin, doğrulama veya denetleyici mantığınızı değiştirmeden veri erişim teknolojileri arasında geçiş yapabilirsiniz.

Bu yinelemede, Contact Manager uygulamamızı daha gevşek bir şekilde bağlanmış bir uygulamada yeniden düzenlememizi sağlayan çeşitli yazılım tasarım desenlerinden yararlanıyoruz. bitirdiğimizde, Contact Manager daha önce yapmadığı hiçbir şeyi yapmaz. Ancak, gelecekte uygulamayı daha kolay değiştirebileceğiz.

Not

Yeniden düzenleme, bir uygulamayı mevcut işlevleri kaybetmeyebilecek şekilde yeniden yazma işlemidir.

Depo Yazılımı Tasarım Desenini Kullanma

İlk değişikliğimiz, Depo deseni olarak adlandırılan bir yazılım tasarımı deseninin avantajlarından yararlanmaktır. Veri erişim kodumuzu uygulamamızın geri kalanından yalıtmak için Depo düzenini kullanacağız.

Depo düzenini uygulamak için aşağıdaki iki adımı tamamlamamız gerekir:

  1. Arabirim oluşturma
  2. Arabirimini uygulayan somut bir sınıf oluşturma

İlk olarak, gerçekleştirmemiz gereken tüm veri erişim yöntemlerini açıklayan bir arabirim oluşturmamız gerekir. IContactManagerRepository arabirimi Listeleme 1'de yer alır. Bu arabirim beş yöntemi açıklar: CreateContact(), DeleteContact(), EditContact(), GetContact ve ListContacts().

Listeleme 1 - Models\IContactManagerRepository.cs

using System;
using System.Collections.Generic;

namespace ContactManager.Models
{
    public interface IContactRepository
    {
        Contact CreateContact(Contact contactToCreate);
        void DeleteContact(Contact contactToDelete);
        Contact EditContact(Contact contactToUpdate);
        Contact GetContact(int id);
        IEnumerable<Contact> ListContacts();

    }
}

Ardından, IContactManagerRepository arabirimini uygulayan somut bir sınıf oluşturmamız gerekir. Veritabanına erişmek için Microsoft Entity Framework'i kullandığımız için EntityContactManagerRepository adlı yeni bir sınıf oluşturacağız. Bu sınıf Liste 2'de yer alır.

Listeleme 2 - Models\EntityContactManagerRepository.cs

using System.Collections.Generic;
using System.Linq;

namespace ContactManager.Models
{
    public class EntityContactManagerRepository : ContactManager.Models.IContactManagerRepository
    {
        private ContactManagerDBEntities _entities = new ContactManagerDBEntities();

        public Contact GetContact(int id)
        {
            return (from c in _entities.ContactSet
                    where c.Id == id
                    select c).FirstOrDefault();
        }

        public IEnumerable ListContacts()
        {
            return _entities.ContactSet.ToList();
        }

        public Contact CreateContact(Contact contactToCreate)
        {
            _entities.AddToContactSet(contactToCreate);
            _entities.SaveChanges();
            return contactToCreate;
        }

        public Contact EditContact(Contact contactToEdit)
        {
            var originalContact = GetContact(contactToEdit.Id);
            _entities.ApplyPropertyChanges(originalContact.EntityKey.EntitySetName, contactToEdit);
            _entities.SaveChanges();
            return contactToEdit;
        }

        public void DeleteContact(Contact contactToDelete)
        {
            var originalContact = GetContact(contactToDelete.Id);
            _entities.DeleteObject(originalContact);
            _entities.SaveChanges();
        }

    }
}

EntityContactManagerRepository sınıfının IContactManagerRepository arabirimini uyguladığına dikkat edin. sınıfı, bu arabirim tarafından açıklanan yöntemlerin beşini de uygular.

Neden bir arabirimle uğraşmamız gerektiğini merak ediyor olabilirsiniz. Neden hem bir arabirim hem de bunu uygulayan bir sınıf oluşturmamız gerekiyor?

Tek bir istisna dışında, uygulamamızın geri kalanı somut sınıfla değil arabirimle etkileşim kurar. EntityContactManagerRepository sınıfı tarafından kullanıma sunulan yöntemleri çağırmak yerine, IContactManagerRepository arabirimi tarafından kullanıma sunulan yöntemleri çağıracağız.

Bu şekilde, uygulamamızın geri kalanını değiştirmeye gerek kalmadan arabirimini yeni bir sınıfla uygulayabiliriz. Örneğin, gelecekteki bir tarihte, IContactManagerRepository arabirimini uygulayan bir DataServicesContactManagerRepository sınıfı uygulamak isteyebiliriz. DataServicesContactManagerRepository sınıfı, Microsoft Entity Framework yerine bir veritabanına erişmek için ADO.NET Data Services kullanabilir.

Uygulama kodumuz beton EntityContactManagerRepository sınıfı yerine IContactManagerRepository arabirimine göre programlanmışsa kodumuzun geri kalanını değiştirmeden somut sınıflar arasında geçiş yapabiliriz. Örneğin, veri erişimimizi veya doğrulama mantığımızı değiştirmeden EntityContactManagerRepository sınıfından DataServicesContactManagerRepository sınıfına geçebiliriz.

Somut sınıflar yerine arabirimlere (soyutlamalar) karşı programlamak, uygulamamızı değiştirmek için daha dayanıklı hale getirir.

Not

Yeniden Düzenle, Arabirimi Ayıkla menü seçeneğini belirleyerek Visual Studio'da somut bir sınıftan hızla bir arabirim oluşturabilirsiniz. Örneğin, önce EntityContactManagerRepository sınıfını oluşturabilir ve ardından Arabirimi Ayıkla'yı kullanarak IContactManagerRepository arabirimini otomatik olarak oluşturabilirsiniz.

Bağımlılık Ekleme Yazılımı Tasarım Desenini Kullanma

Veri erişim kodumuzu ayrı bir Depo sınıfına geçirdiğimize göre, Kişi denetleyicimizi bu sınıfı kullanacak şekilde değiştirmemiz gerekiyor. Denetleyicimizdeki Depo sınıfını kullanmak için Bağımlılık Ekleme adlı yazılım tasarım deseninin avantajlarından yararlanacağız.

Değiştirilen Kişi denetleyicisi Liste 3'te yer alır.

Liste 3 - Controllers\ContactController.cs

using System.Text.RegularExpressions;
using System.Web.Mvc;
using ContactManager.Models;

namespace ContactManager.Controllers
{
    public class ContactController : Controller
    {
        private IContactManagerRepository _repository;

        public ContactController()
            : this(new EntityContactManagerRepository())
        {}

        public ContactController(IContactManagerRepository repository)
        {
            _repository = repository;
        }

        protected void ValidateContact(Contact contactToValidate)
        {
            if (contactToValidate.FirstName.Trim().Length == 0)
                ModelState.AddModelError("FirstName", "First name is required.");
            if (contactToValidate.LastName.Trim().Length == 0)
                ModelState.AddModelError("LastName", "Last name is required.");
            if (contactToValidate.Phone.Length > 0 && !Regex.IsMatch(contactToValidate.Phone, @"((\(\d{3}\) ?)|(\d{3}-))?\d{3}-\d{4}"))
                ModelState.AddModelError("Phone", "Invalid phone number.");
            if (contactToValidate.Email.Length > 0 && !Regex.IsMatch(contactToValidate.Email, @"^[\w-\.]+@([\w-]+\.)+[\w-]{2,4}$"))
                ModelState.AddModelError("Email", "Invalid email address.");
        }

        public ActionResult Index()
        {
            return View(_repository.ListContacts());
        }

        public ActionResult Create()
        {
            return View();
        } 

        [AcceptVerbs(HttpVerbs.Post)]
        public ActionResult Create([Bind(Exclude = "Id")] Contact contactToCreate)
        {
            // Validation logic
            ValidateContact(contactToCreate);
            if (!ModelState.IsValid)
                return View();

            // Database logic
            try
            {
                _repository.CreateContact(contactToCreate);
                return RedirectToAction("Index");
            }
            catch
            {
                return View();
            }
        }

        public ActionResult Edit(int id)
        {
            return View(_repository.GetContact(id));
        }

        [AcceptVerbs(HttpVerbs.Post)]
        public ActionResult Edit(Contact contactToEdit)
        {
            // Validation logic
            ValidateContact(contactToEdit);
            if (!ModelState.IsValid)
                return View();

            // Database logic
            try
            {
                _repository.EditContact(contactToEdit);
                return RedirectToAction("Index");
            }
            catch
            {
                return View();
            }
        }

        public ActionResult Delete(int id)
        {
            return View(_repository.GetContact(id));
        }

        [AcceptVerbs(HttpVerbs.Post)]
        public ActionResult Delete(Contact contactToDelete)
        {
            try
            {
                _repository.DeleteContact(contactToDelete);
                return RedirectToAction("Index");
            }
            catch
            {
                return View();
            }
        }

    }
}

Liste 3'teki Kişi denetleyicisinin iki oluşturucuya sahip olduğuna dikkat edin. İlk oluşturucu, IContactManagerRepository arabiriminin somut bir örneğini ikinci oluşturucuya geçirir. Contact denetleyici sınıfı Oluşturucu Bağımlılık Ekleme'yi kullanır.

EntityContactManagerRepository sınıfının kullanıldığı tek ve tek yer ilk oluşturucudadır. sınıfının geri kalanı beton EntityContactManagerRepository sınıfı yerine IContactManagerRepository arabirimini kullanır.

Bu, gelecekte IContactManagerRepository sınıfının uygulamalarını değiştirmeyi kolaylaştırır. EntityContactManagerRepository sınıfı yerine DataServicesContactRepository sınıfını kullanmak istiyorsanız ilk oluşturucuyu değiştirmeniz gerekir.

Oluşturucu Bağımlılığı ekleme ayrıca Contact denetleyici sınıfını çok test edilebilir hale getirir. Birim testlerinizde, IContactManagerRepository sınıfının sahte bir uygulamasını geçirerek Contact denetleyicisinin örneğini oluşturabilirsiniz. Bağımlılık Ekleme'nin bu özelliği, Contact Manager uygulaması için birim testleri oluştururken sonraki yinelemede bizim için çok önemli olacaktır.

Not

Contact denetleyici sınıfını IContactManagerRepository arabiriminin belirli bir uygulamasından tamamen ayrıştırmak istiyorsanız, StructureMap veya Microsoft Entity Framework (MEF) gibi Bağımlılık Eklemeyi destekleyen bir çerçeveden yararlanabilirsiniz. Bağımlılık Ekleme çerçevesinden yararlanarak kodunuzdaki somut bir sınıfa başvurmanız gerekmez.

Hizmet Katmanı Oluşturma

Doğrulama mantığımızın, Liste 3'teki değiştirilmiş denetleyici sınıfındaki denetleyici mantığımızla hala karma olduğunu fark etmiş olabilirsiniz. Veri erişim mantığımızı yalıtmak iyi bir fikir olduğu gibi doğrulama mantığımızı da yalıtmak iyi bir fikirdir.

Bu sorunu çözmek için ayrı bir hizmet katmanı oluşturabiliriz. Hizmet katmanı, denetleyici ve depo sınıflarımız arasına ekleyebileceğimiz ayrı bir katmandır. Hizmet katmanı, tüm doğrulama mantığımız dahil olmak üzere iş mantığımızı içerir.

ContactManagerService, Liste 4'te yer alır. Kişi denetleyicisi sınıfından doğrulama mantığını içerir.

Listeleme 4 - Models\ContactManagerService.cs

using System.Collections.Generic;
using System.Text.RegularExpressions;
using System.Web.Mvc;
using ContactManager.Models.Validation;

namespace ContactManager.Models
{
    public class ContactManagerService : IContactManagerService
    {
        private IValidationDictionary _validationDictionary;
        private IContactManagerRepository _repository;

        public ContactManagerService(IValidationDictionary validationDictionary) 
            : this(validationDictionary, new EntityContactManagerRepository())
        {}

        public ContactManagerService(IValidationDictionary validationDictionary, IContactManagerRepository repository)
        {
            _validationDictionary = validationDictionary;
            _repository = repository;
        }

        public bool ValidateContact(Contact contactToValidate)
        {
            if (contactToValidate.FirstName.Trim().Length == 0)
                _validationDictionary.AddError("FirstName", "First name is required.");
            if (contactToValidate.LastName.Trim().Length == 0)
                _validationDictionary.AddError("LastName", "Last name is required.");
            if (contactToValidate.Phone.Length > 0 && !Regex.IsMatch(contactToValidate.Phone, @"((\(\d{3}\) ?)|(\d{3}-))?\d{3}-\d{4}"))
                _validationDictionary.AddError("Phone", "Invalid phone number.");
            if (contactToValidate.Email.Length > 0 && !Regex.IsMatch(contactToValidate.Email, @"^[\w-\.]+@([\w-]+\.)+[\w-]{2,4}$"))
                _validationDictionary.AddError("Email", "Invalid email address.");
            return _validationDictionary.IsValid;
        }

        #region IContactManagerService Members

        public bool CreateContact(Contact contactToCreate)
        {
            // Validation logic
            if (!ValidateContact(contactToCreate))
                return false;

            // Database logic
            try
            {
                _repository.CreateContact(contactToCreate);
            }
            catch
            {
                return false;
            }
            return true;
        }

        public bool EditContact(Contact contactToEdit)
        {
            // Validation logic
            if (!ValidateContact(contactToEdit))
                return false;

            // Database logic
            try
            {
                _repository.EditContact(contactToEdit);
            }
            catch
            {
                return false;
            }
            return true;
        }

        public bool DeleteContact(Contact contactToDelete)
        {
            try
            {
                _repository.DeleteContact(contactToDelete);
            }
            catch
            {
                return false;
            }
            return true;
        }

        public Contact GetContact(int id)
        {
            return _repository.GetContact(id);
        }

        public IEnumerable<Contact> ListContacts()
        {
            return _repository.ListContacts();
        }

        #endregion
    }
}

ContactManagerService oluşturucusunun geçerlilik sözlüğü gerektirdiğine dikkat edin. Hizmet katmanı, denetleyici katmanıyla bu Doğrulama Sözlüğü aracılığıyla iletişim kurar. Aşağıdaki bölümde, Dekoratör desenini tartışırken Geçerlilik Sözlüğü'ne ayrıntılı bir şekilde değineceğiz.

Ayrıca, ContactManagerService'in IContactManagerService arabirimini uyguladığına dikkat edin. Her zaman somut sınıflar yerine arabirimlere karşı programlamaya çalışmanız gerekir. Contact Manager uygulamasındaki diğer sınıflar, ContactManagerService sınıfıyla doğrudan etkileşim kurmaz. Bunun yerine, tek bir özel durum dışında, Contact Manager uygulamasının geri kalanı IContactManagerService arabirimine göre programlanır.

IContactManagerService arabirimi Listeleme 5'te yer alır.

Listeleme 5 - Models\IContactManagerService.cs

using System.Collections.Generic;

namespace ContactManager.Models
{
    public interface IContactManagerService
    {
        bool CreateContact(Contact contactToCreate);
        bool DeleteContact(Contact contactToDelete);
        bool EditContact(Contact contactToEdit);
        Contact GetContact(int id);
        IEnumerable ListContacts();
    }
}

Değiştirilen Contact controller sınıfı Liste 6'da yer alır. Kişi denetleyicisinin artık ContactManager deposuyla etkileşim kurmadığını göreceksiniz. Bunun yerine, Kişi denetleyicisi ContactManager hizmetiyle etkileşim kurar. Her katman diğer katmanlardan mümkün olduğunca yalıtılır.

Liste 6 - Controllers\ContactController.cs

using System.Web.Mvc;
using ContactManager.Models;

namespace ContactManager.Controllers
{
    public class ContactController : Controller
    {
        private IContactManagerService _service;

        public ContactController()
        {
            _service = new ContactManagerService(new ModelStateWrapper(this.ModelState));

        }

        public ContactController(IContactManagerService service)
        {
            _service = service;
        }
        
        public ActionResult Index()
        {
            return View(_service.ListContacts());
        }

        public ActionResult Create()
        {
            return View();
        }

        [AcceptVerbs(HttpVerbs.Post)]
        public ActionResult Create([Bind(Exclude = "Id")] Contact contactToCreate)
        {
            if (_service.CreateContact(contactToCreate))
                return RedirectToAction("Index");
            return View();
        }

        public ActionResult Edit(int id)
        {
            return View(_service.GetContact(id));
        }

        [AcceptVerbs(HttpVerbs.Post)]
        public ActionResult Edit(Contact contactToEdit)
        {
            if (_service.EditContact(contactToEdit))
                return RedirectToAction("Index");
            return View();
        }

        public ActionResult Delete(int id)
        {
            return View(_service.GetContact(id));
        }

        [AcceptVerbs(HttpVerbs.Post)]
        public ActionResult Delete(Contact contactToDelete)
        {
            if (_service.DeleteContact(contactToDelete))
                return RedirectToAction("Index");
            return View();
        }

    }
}

Uygulamamız artık Tek Sorumluluk İlkesi'nin (SRP) afoul'unu çalıştırmaz. Liste 6'daki Kişi denetleyicisi, uygulama yürütme akışını denetleme dışındaki tüm sorumluluklardan çıkarılmıştır. Tüm doğrulama mantığı Kişi denetleyicisinden kaldırıldı ve hizmet katmanına gönderildi. Tüm veritabanı mantığı depo katmanına gönderildi.

Dekoratör Desenini Kullanma

Hizmet katmanımızı denetleyici katmanımızdan tamamen ayrıştırabilmek istiyoruz. Prensip olarak, MVC uygulamamıza başvuru eklemeye gerek kalmadan hizmet katmanımızı denetleyici katmanımızdan ayrı bir derlemede derleyebilmemiz gerekir.

Ancak hizmet katmanımızın doğrulama hata iletilerini denetleyici katmanına geri geçirebilmesi gerekir. Hizmet katmanının denetleyiciyle hizmet katmanını bağlamadan doğrulama hata iletilerini iletmesini nasıl sağlayabiliriz? Dekoratör deseni adlı bir yazılım tasarım deseninin avantajlarından yararlanabiliriz.

Denetleyici, doğrulama hatalarını göstermek için ModelState adlı bir ModelStateDictionary kullanır. Bu nedenle, ModelState'i denetleyici katmanından hizmet katmanına geçirmek isteyebilirsiniz. Ancak hizmet katmanında ModelState kullanmak, hizmet katmanınızı ASP.NET MVC çerçevesinin bir özelliğine bağımlı hale getirir. Bir gün hizmet katmanını ASP.NET MVC uygulaması yerine wpf uygulamasıyla kullanmak isteyebileceğiniz için bu kötü olabilir. Bu durumda, ModelStateDictionary sınıfını kullanmak için ASP.NET MVC çerçevesine başvurmak istemezsiniz.

Dekoratör deseni, bir arabirim uygulamak için var olan bir sınıfı yeni bir sınıfa sarmalamanızı sağlar. Contact Manager projemiz, Liste 7'de yer alan ModelStateWrapper sınıfını içerir. ModelStateWrapper sınıfı, Listing 8'de arabirimini uygular.

Listeleme 7 - Models\Validation\ModelStateWrapper.cs

using System.Web.Mvc;

namespace ContactManager.Models.Validation
{
    public class ModelStateWrapper : IValidationDictionary
    {
        private ModelStateDictionary _modelState;

        public ModelStateWrapper(ModelStateDictionary modelState)
        {
            _modelState = modelState;
        }

        public void AddError(string key, string errorMessage)
        {
            _modelState.AddModelError(key, errorMessage);
        }

        public bool IsValid
        {
            get { return _modelState.IsValid; }
        }
    }
}

Listeleme 8 - Models\Validation\IValidationDictionary.cs

namespace ContactManager.Models.Validation
{
    public interface IValidationDictionary
    {
        void AddError(string key, string errorMessage);
        bool IsValid {get;}
    }
}

Liste 5'e yakından bakarsanız ContactManager hizmet katmanının yalnızca IValidationDictionary arabirimini kullandığını görürsünüz. ContactManager hizmeti ModelStateDictionary sınıfına bağımlı değildir. Contact denetleyicisi ContactManager hizmetini oluşturduğunda, denetleyici ModelState'ini şöyle sarmalar:

_service = new ContactManagerService(new ModelStateWrapper(this.ModelState));

Özet

Bu yinelemede, Contact Manager uygulamasına yeni bir işlev eklemedik. Bu yinelemenin amacı, bakımı ve değiştirilmesi daha kolay olması için Contact Manager uygulamasını yeniden düzenlemekti.

İlk olarak, Depo yazılımı tasarım desenini uyguladık. Tüm veri erişim kodunu ayrı bir ContactManager deposu sınıfına geçirdik.

Ayrıca doğrulama mantığımızı denetleyici mantığımızdan yalıttık. Tüm doğrulama kodumuzu içeren ayrı bir hizmet katmanı oluşturduk. Denetleyici katmanı hizmet katmanıyla, hizmet katmanı ise depo katmanıyla etkileşim kurar.

Hizmet katmanını oluşturduğumuzda ModelState'i hizmet katmanımızdan yalıtmak için Dekoratör deseninden yararlandık. Hizmet katmanımızda ModelState yerine IValidationDictionary arabirimine göre programlandık.

Son olarak, Bağımlılık Ekleme deseni adlı bir yazılım tasarım deseninin avantajlarından yararlandık. Bu düzen, somut sınıflar yerine arabirimlere (soyutlamalar) karşı programlama yapmamızı sağlar. Bağımlılık Ekleme tasarım desenini uygulamak da kodumuzu daha test edilebilir hale getirir. Sonraki yinelemede projemize birim testleri ekleyeceğiz.