ASP.NET MVC में डेटा एक्सेस को अलग करना


35

मैं यह सुनिश्चित करना चाहता हूं कि मैं एमवीसी में अपनी पहली वास्तविक दरार के साथ उद्योग के मानकों और सर्वोत्तम प्रथाओं का पालन कर रहा हूं। इस स्थिति में, यह C # का उपयोग करते हुए ASP.NET MVC है।

मैं अपने मॉडल के लिए कोड-प्रथम ऑब्जेक्ट्स (डेटाबेस पहले से मौजूद है) के साथ Entity फ्रेमवर्क 4.1 का उपयोग कर रहा हूं, इसलिए डेटाबेस से डेटा प्राप्त करने के लिए DBContext ऑब्जेक्ट होगा।

डेमो में मैं asp.net वेबसाइट पर चला गया हूं, नियंत्रकों में डेटा एक्सेस कोड है। यह मुझे सही नहीं लगता, खासकर जब DRY (खुद को दोहराना नहीं) प्रथाओं का पालन करते हैं।

उदाहरण के लिए, मान लें कि मैं एक सार्वजनिक पुस्तकालय में उपयोग किए जाने वाला एक वेब एप्लिकेशन लिख रहा हूं, और मेरे पास कैटलॉग में पुस्तकें बनाने, अपडेट करने और हटाने के लिए एक नियंत्रक है।

कई कार्यों में एक आईएसबीएन हो सकता है और उसे "बुक" ऑब्जेक्ट वापस करना होगा (ध्यान दें कि यह संभवतः 100% वैध कोड नहीं है):

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

इसके बजाय, क्या मुझे वास्तव में एक पुस्तक वापस करने के लिए मेरे db संदर्भ ऑब्जेक्ट में एक विधि होनी चाहिए ? ऐसा लगता है कि यह मेरे लिए एक बेहतर अलगाव है, और DRY को बढ़ावा देने में मदद करता है, क्योंकि मुझे अपने वेब एप्लिकेशन में कहीं और ISBN द्वारा बुक ऑब्जेक्ट प्राप्त करने की आवश्यकता हो सकती है।

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

क्या यह मेरे आवेदन के कोडिंग में पालन करने के लिए नियमों का एक वैध सेट है?

या, मुझे लगता है कि एक अधिक व्यक्तिपरक प्रश्न होगा: "क्या यह ऐसा करने का सही तरीका है?"

जवाबों:


55

आम तौर पर, आप चाहते हैं कि आपके नियंत्रक केवल कुछ चीजें करें:

  1. आने वाले अनुरोध को संभालें
  2. कुछ व्यावसायिक वस्तु के लिए प्रसंस्करण प्रतिनिधि
  3. रेंडरिंग के लिए उपयुक्त दृश्य के लिए व्यावसायिक प्रसंस्करण का परिणाम पास करें

नियंत्रक में कोई डेटा एक्सेस या जटिल व्यावसायिक तर्क नहीं होना चाहिए ।

[सबसे सरल ऐप्स में, आप संभवतः अपने कंट्रोलर में बेसिक डेटा CRUD एक्शन से दूर हो सकते हैं, लेकिन एक बार जब आप सिंपल गेट और अपडेट कॉल से अधिक जोड़ना शुरू कर देंगे, तो आप अपनी प्रोसेसिंग को एक अलग वर्ग में तोड़ना चाहते हैं। ]

आपके नियंत्रक आमतौर पर वास्तविक प्रसंस्करण कार्य करने के लिए 'सेवा' पर निर्भर होंगे। आपकी सेवा श्रेणी में आप सीधे अपने डेटा स्रोत (आपके मामले में, DbContext) के साथ काम कर सकते हैं, लेकिन एक बार फिर, यदि आप अपने आप को डेटा एक्सेस के अलावा बहुत से व्यावसायिक नियम लिख रहे हैं, तो आप संभवतः अपने व्यवसाय को अलग करना चाहेंगे। आपके डेटा एक्सेस से तर्क।

उस बिंदु पर, आपके पास संभवतः एक वर्ग होगा जो डेटा एक्सेस के अलावा कुछ नहीं करता है। कभी-कभी इसे एक रिपॉजिटरी कहा जाता है, लेकिन यह वास्तव में कोई फर्क नहीं पड़ता कि नाम क्या है। मुद्दा यह है कि डेटा को डेटाबेस में और उसके बाहर प्राप्त करने के लिए सभी कोड एक ही स्थान पर हैं।

मेरे द्वारा काम किए गए हर MVC प्रोजेक्ट के लिए, मैंने हमेशा एक संरचना के साथ समाप्त किया है:

नियंत्रक

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

व्यावसायिक सेवा

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;

     } 
}

कोष

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 समग्र महान सलाह, हालांकि मैं सवाल करूंगा कि क्या रिपॉजिटरी अमूर्तता कोई मूल्य प्रदान कर रही थी।
मैटवेवी

3
@MattDavey हाँ, बहुत शुरुआत में (या ऐप्स के सबसे सरल के लिए) यह एक रिपॉजिटरी लेयर की आवश्यकता को देखने के लिए कठिन है, लेकिन जैसे ही आपके पास अपने व्यावसायिक तर्क में जटिलता का एक मध्यम स्तर होता है, यह नो-ब्रेनर बन जाता है। डेटा एक्सेस को अलग करें। यह आसान नहीं है कि एक सरल तरीके से, हालांकि।
एरिक किंग

1
@ बिल आईओसी कर्नेल को MVC प्रोजेक्ट में होना जरूरी नहीं है । आप इसे स्वयं की एक परियोजना में रख सकते हैं, जो एमवीसी परियोजना पर निर्भर करती है, लेकिन जो बदले में रिपोजिटरी परियोजना पर निर्भर करती है। मैं आमतौर पर ऐसा नहीं करता क्योंकि मुझे इसकी आवश्यकता महसूस नहीं होती। फिर भी, यदि आप अपने MVC प्रोजेक्ट को अपनी रिपॉजिटरी कक्षाओं को कॉल नहीं करना चाहते हैं ... तो नहीं। मैं खुद को हैमस्ट्रिंग करने का बहुत बड़ा प्रशंसक नहीं हूं, ताकि मैं खुद को उन प्रोग्रामिंग प्रथाओं की संभावना से बचा सकूं, जिनमें मैं संलग्न होने की संभावना नहीं हूं।
एरिक किंग

2
हम बिल्कुल इस पैटर्न का उपयोग करते हैं: नियंत्रक-सेवा-रिपोजिटरी। मैं यह जोड़ना चाहूंगा कि हमारे लिए यह बहुत उपयोगी है कि सेवा / रिपॉजिटरी टियर के पैरामीटर ऑब्जेक्ट्स (जैसे GetBooksParameters) लें और फिर पैरामीटर पुनर्परिवर्तन करने के लिए ILibraryService पर एक्सटेंशन विधियों का उपयोग करें। इस तरह से ILibraryService में एक सरल प्रविष्टि बिंदु होता है जो एक ऑब्जेक्ट लेता है, और एक्सटेंशन विधि हर बार इंटरफेस और कक्षाओं को फिर से लिखे बिना पैरामीटर पागल के रूप में संभव हो सकती है (जैसे GetBooksByISBN / ग्राहक / तिथि / जो भी बस GetBooksParameters ऑब्जेक्ट और कॉल को कॉल करता है सर्विस)। कॉम्बो बहुत अच्छा रहा है।
लाठी

1
@IsaacKleinman मुझे याद नहीं है कि किस महान व्यक्ति ने इसे (बॉब मार्टिन?) लिखा था, लेकिन यह एक बुनियादी सवाल है: क्या आप ओवेन.बेके (पिज्जा) या पिज्जा चाहते हैं। बेक (ओवन)। और जवाब है 'यह निर्भर करता है'। आमतौर पर हम एक या अधिक वस्तुओं (या पिज्जा!) से छेड़छाड़ करके एक बाहरी सेवा (या काम की इकाई) चाहते हैं। लेकिन यह कहने के लिए कि उन अलग-अलग वस्तुओं में ओवेन के प्रकार पर प्रतिक्रिया करने की क्षमता नहीं है, जिन्हें मैं बेक किया जा रहा हूं। हालाँकि, मुझे ऑर्डर करना पसंद है। अमान्य () क्योंकि ऑर्डर को पता चल सकता है कि यह स्वयं का आदर्श रूप है। प्रासंगिक, और व्यक्तिगत।
ब्लैकजैकमेटमैक

2

यह वह तरीका है जो मैं कर रहा हूं, हालांकि मैं डेटा प्रदाता को एक सामान्य डेटा सेवा इंटरफ़ेस के रूप में इंजेक्ट कर रहा हूं ताकि मैं कार्यान्वयन को स्वैप कर सकूं।

जहां तक ​​मुझे पता है, कंट्रोलर का इरादा है कि आप डेटा प्राप्त करें, कोई भी कार्य करें और डेटा को दृश्य में पास करें।


हां, मैंने डेटा प्रदाता के लिए "सेवा इंटरफ़ेस" का उपयोग करने के बारे में पढ़ा है, क्योंकि यह इकाई परीक्षण में मदद करता है।
scott.korin 19
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.