ASP.NET MVC’de veri erişimini ayırma


35

MVC'deki ilk gerçek çatışmamla endüstri standartlarını ve en iyi uygulamaları takip ettiğime emin olmak istiyorum. Bu durumda, C # kullanarak ASP.NET MVC'dir.

Modelim için Entity Framework 4.1'i kod birinci nesnelerle (veritabanı zaten var) kullanacağım, bu nedenle veritabanından veri almak için bir DBContext nesnesi olacak.

Asp.net web sitesinde geçirdiğim demolarda, kontrol cihazlarında veri erişim kodu var. Bu bana uygun görünmüyor, özellikle DRY (kendinizi tekrar etmeyin) uygulamalarını takip ederken.

Örneğin, halk kütüphanesinde kullanılacak bir web uygulaması yazdığımı ve katalogda kitap oluşturmak, güncellemek ve silmek için bir kontrol cihazım olduğunu varsayalım.

İşlemlerin bazıları bir ISBN alabilir ve bir "Kitap" nesnesi döndürmek gerekebilir (bunun% 100 geçerli kod olmadığını unutmayın):

public class BookController : Controller
{
    LibraryDBContext _db = new LibraryDBContext();

    public ActionResult Details(String ISBNtoGet)
    {
        Book currentBook = _db.Books.Single(b => b.ISBN == ISBNtoGet);
        return View(currentBook);
    }

    public ActionResult Edit(String ISBNtoGet)
    {
        Book currentBook = _db.Books.Single(b => b.ISBN == ISBNtoGet);
        return View(currentBook);
    }
}

Bunun yerine, db bağlam nesnemde bir kitap döndürecek bir yöntemim olmalı mı? Bu benim için daha iyi bir ayrım gibi gözüküyor ve DRY'nin tanıtımına yardımcı oluyor, çünkü web uygulamamda ISBN üzerinden başka bir yere bir Kitap nesnesi almam gerekebilir.

public partial class LibraryDBContext: DBContext
{
    public Book GetBookByISBN(String ISBNtoGet)
    {
        return Books.Single(b => b.ISBN == ISBNtoGet);
    }
}

public class BookController : Controller
{
    LibraryDBContext _db = new LibraryDBContext();

    public ActionResult Details(String ISBNtoGet)
    {
        return View(_db.GetBookByISBN(ISBNtoGet));
    }

    public ActionResult Edit(ByVal ISBNtoGet as String)
    {
        return View(_db.GetBookByISBN(ISBNtoGet));
    }
}

Bu başvurumun kodlanmasında uyulması gereken geçerli bir kurallar dizisi midir?

Veya, daha öznel bir soru olacağını düşünüyorum: "Bunu yapmanın doğru yolu bu mu?"

Yanıtlar:


55

Genel olarak, Denetleyicilerinizin yalnızca birkaç şey yapmasını istersiniz:

  1. Gelen isteği işleme
  2. İşlemeyi bir iş nesnesine devretme
  3. İş işlemenin sonucunu işlemek için uygun görünüme geçirin

Denetleyicide herhangi bir veri erişimi veya karmaşık bir iş mantığı olmamalıdır .

[En basit uygulamalarda, denetleyicinizdeki temel veri CRUD işlemlerinden muhtemelen kurtulabilirsiniz, ancak basit Get and Update çağrılarından daha fazlasını eklemeye başladığınızda, işlemlerinizi ayrı bir sınıfa bölmek isteyeceksiniz. ]

Kontrol cihazlarınız genellikle fiili işleme işlemini yapmak için bir 'Servis'e bağlı olacaktır. Servis sınıfta birden olabilir kendinizi veri erişimi ek olarak iş kurallarının bir sürü yazı bulursanız, muhtemelen işinizi ayırmak isteyeceksiniz, bir kez daha veri kaynağı (sizin durumunuzda, DBContext) ile doğrudan çalışır, ancak veri erişiminizden mantık.

Bu noktada, muhtemelen veri erişiminden başka hiçbir şey yapmayan bir sınıfa sahip olacaksınız. Bazen buna Depo adı verilir, ancak adın ne olduğu önemli değildir. Mesele şu ki, veritabanına veri giriş ve çıkış kodlarının tümü tek bir yerde.

Çalıştığım her MVC projesi için her zaman şöyle bir yapıya sahip oldum:

kontrolör

public class BookController : Controller
{
    ILibraryService _libraryService;

    public BookController(ILibraryService libraryService)
    {
        _libraryService = libraryService;
    }

    public ActionResult Details(String isbn)
    {
        Book currentBook = _libraryService.RetrieveBookByISBN(isbn);
        return View(ConvertToBookViewModel(currentBook));
    }

    public ActionResult DoSomethingComplexWithBook(ComplexBookActionRequest request)
    {
        var responseViewModel = _libraryService.ProcessTheComplexStuff(request);
        return View(responseViewModel);
    }
}

İşletme Hizmeti

public class LibraryService : ILibraryService
{
     IBookRepository _bookRepository;
     ICustomerRepository _customerRepository;

     public LibraryService(IBookRepository bookRepository, 
                           ICustomerRepository _customerRepository )
     {
          _bookRepository = bookRepository;
          _customerRepository = customerRepository;
     }

     public Book RetrieveBookByISBN(string isbn)
     {
          return _bookRepository.GetBookByISBN(isbn);
     }

     public ComplexBookActionResult ProcessTheComplexStuff(ComplexBookActionRequest request)
     {
          // Possibly some business logic here

          Book book = _bookRepository.GetBookByISBN(request.Isbn);
          Customer customer = _customerRepository.GetCustomerById(request.CustomerId);

          // Probably more business logic here

          _libraryRepository.Save(book);

          return complexBusinessActionResult;

     } 
}

depo

public class BookRepository : IBookRepository
{
     LibraryDBContext _db = new LibraryDBContext();

     public Book GetBookByIsbn(string isbn)
     {
         return _db.Books.Single(b => b.ISBN == isbn);
     }

     // And the rest of the data access
}

+1 Genel olarak harika tavsiyeler, ancak depo soyutlamasının herhangi bir değer sağlayıp sağlamadığını sorguluyorum.
MattDavey

3
@MattDavey Evet, en başında (ya da en basit uygulamalar için), bir depo katmanına olan ihtiyacı görmek zordur, ancak iş mantığınızda orta derecede bir karmaşıklık seviyesine sahip olduğunuzda bile hiç düşünmeden olur veri erişimini ayırın. Yine de bunu basit bir şekilde iletmek kolay değil.
Eric King,

1
@Billy IoC çekirdeğinin MVC projesinde olması gerekmez . MVC projesinin dayandığı ancak sırayla depo projesine bağlı olan bir projesinde elde edebilirsiniz. Genelde bunu yapmam çünkü ihtiyaç duymuyorum. Öyle olsa bile, MVC projenizin depo sınıflarınızı çağırmasını istemiyorsanız ... o zaman yapmayın. Kendimi çok zorlama hayranı değilim, bu yüzden kendimi meşgul
Eric King,

2
Tam olarak bu modeli kullanıyoruz: Denetleyici-Servis-Depo. Hizmet / depo katmanının parametre nesnelerini (örn. GetBooksParameters) almasının ve ardından parametre sıralamasını yapmak için ILibraryService üzerinde uzatma yöntemlerini kullanmamızın çok faydalı olacağını eklemek isterim. Bu şekilde ILibraryService bir nesneyi alan basit bir giriş noktasına sahiptir ve uzatma yöntemi her seferinde arayüzleri ve sınıfları yeniden yazmak zorunda kalmadan olabildiğince delirir (örneğin, GetBooksByISBN / Müşteri / Tarih / GetBooksParameters nesnesini oluşturan ve ne çağıran hizmet). Combo harikaydı.
BlackjacketMack

1
@IsaacKleinman Büyük harflerden hangisinin onu yazdığını hatırlayamıyorum (Bob Martin?) Ama bu temel bir soru: Oven.Bake (pizza) veya Pizza.Bake (fırın) istiyor musunuz. Ve cevabı 'bağlıdır'. Genellikle bir ya da daha fazla nesneyi (ya da pizzaları!) Manipüle eden bir dış hizmet (ya da iş birimi) istiyoruz. Ancak bu bireysel nesnelerin, pişirildikleri Fırın tipine tepki verme kabiliyetine sahip olmadığını söyleyenler. OrderRepository.Save'i (order) Order.Save () 'e tercih ederim. Ancak Order.Validate () 'ı severim çünkü sipariş kendi ideal formunu biliyor olabilir. Bağlamsal ve kişisel.
BlackjacketMack

2

Bu, uygulamanın yerini alabilmem için veri sağlayıcısına genel bir veri hizmeti arabirimi olarak enjekte etmeme rağmen, bunu yapıyorum.

Bildiğim kadarıyla, kontrolör veri aldığınız, herhangi bir işlem yaptığınız ve görünüme veri aktaracağınız şekilde tasarlanmıştır.


Evet, veri sağlayıcı için bir "servis arayüzü" kullanmayı okudum, çünkü birim testine yardımcı oluyor.
scott.korin
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.