2008-11-07 11 views
11

मैं सामान्य खजाने कि विभिन्न डेटा स्टोर के खिलाफ काम लिखने के लिए एक तरह से साथ आने की कोशिश कर रहा है:क्या हम सभी एक ही आईआरपॉजिटरी की तलाश में हैं?

public interface IRepository 
{ 
    IQueryable<T> GetAll<T>(); 
    void Save<T>(T item); 
    void Delete<T>(T item); 
} 
public class MemoryRepository : IRepository {...} 
public class SqlRepository : IRepository {...} 

मैं प्रत्येक में एक ही POCO डोमेन वर्गों के खिलाफ काम करना चाहते हैं।

public interface IRepository<T> 
{ 
    IQueryable<T> GetAll(); 
    void Save(T item); 
    void Delete(T item); 
} 
public class MemoryCustomerRepository : IRepository {...} 
public class SqlCustomerRepository : IRepository {...} 

मेरे सवालों का: 1) पहली दृष्टिकोण भी संभव है मैं भी एक समान दृष्टिकोण है, जहां प्रत्येक डोमेन वर्ग अपने आप भंडार है पर विचार कर रहा हूँ? 2) क्या दूसरे दृष्टिकोण के लिए कोई फायदा है।

उत्तर

5
  1. पहले दृष्टिकोण से संभव है, मैंने पहले भी कुछ इसी तरह जब मैं अपने खुद के मानचित्रण रूपरेखा है कि आरडीबीएमएस और XmlWriter/XmlReader लक्षित लिखा किया है है। आप इकाई परीक्षण को कम करने के लिए इस तरह के दृष्टिकोण का उपयोग कर सकते हैं, हालांकि मुझे लगता है कि अब हमारे पास ऐसा करने के लिए बेहतर ओएसएस उपकरण हैं।

  2. दूसरा दृष्टिकोण वह है जो मैं वर्तमान में IBATIS.NET mappers के साथ उपयोग करता हूं। प्रत्येक मैपर में एक इंटरफेस होता है और प्रत्येक मैपर [मूल] आपके मूल सीआरयूडी संचालन प्रदान कर सकता है। लाभ डोमेन क्लास के लिए प्रत्येक मैपर में विशिष्ट फ़ंक्शंस भी होते हैं (जैसे कि SelectByLastName या DeleteFromParent) जो इंटरफ़ेस द्वारा व्यक्त किए जाते हैं और कंक्रीट मैपर में परिभाषित होते हैं। इस वजह से आप अलग-अलग भंडारों को लागू करने की आवश्यकता नहीं है जैसा कि आप सुझाव दे रहे हैं - हमारे ठोस मैपर डेटाबेस को लक्षित करते हैं। यूनिट परीक्षण करने के लिए मैं और Moq का उपयोग इन-मेमोरी रिपॉजिटरीज़ बनाने के लिए करता हूं जो आपके Memory*Repository करता है। एक बहुत ही टेस्टेबल दृष्टिकोण के लिए इसे लागू करने और प्रबंधित करने और कम काम करने के लिए इसकी कम कक्षाएं। यूनिट परीक्षणों में साझा किए गए डेटा के लिए मैं प्रत्येक डोमेन क्लास के लिए बिल्डर पैटर्न का उपयोग करता हूं जिसमें WithXXX विधियां और AsSomeProfile विधियां हैं (AsSomeProfile केवल पूर्व-कॉन्फ़िगर किए गए परीक्षण डेटा के साथ एक बिल्डर उदाहरण देता है)।

यहाँ क्या मैं आमतौर पर मेरी इकाई परीक्षण में अंत का एक उदाहरण है:

// Moq mocking the concrete PersonMapper through the IPersonMapper interface 
var personMock = new Mock<IPersonMapper>(MockBehavior.Strict); 
personMock.Expect(pm => pm.Select(It.IsAny<int>())).Returns(
    new PersonBuilder().AsMike().Build() 
); 

// StructureMap's ObjectFactory 
ObjectFactory.Inject(personMock.Object); 

// now anywhere in my actual code where an IPersonMapper instance is requested from 
// ObjectFactory, Moq will satisfy the requirement and return a Person instance 
// set with the PersonBuilder's Mike profile unit test data 
4

असल में वहाँ एक आम सहमति अब कि डोमेन खजाने सामान्य नहीं होना चाहिए है। आपके भंडार को व्यक्त करना चाहिए कि आप अपनी संस्थाओं को कायम रखने या पुनर्प्राप्त करते समय क्या कर सकते हैं।

कुछ खजाने केवल पढ़ने के लिए, कुछ केवल (कोई अद्यतन, कोई हटाना) सम्मिलित कर रहे हैं कर रहे हैं, कुछ केवल विशिष्ट लुकअप है ...

का उपयोग करते हुए एक IQueryable लौट GetAll, आपकी क्वेरी तर्क को संभवतः अपने कोड में रिसाव हो जाएगा, आवेदन परत।

लेकिन यह अभी भी दिलचस्प है कि आप लिंक Table<T> ऑब्जेक्ट्स को एन्सेप्लेट करने के लिए प्रदान किए गए इंटरफ़ेस का उपयोग करना दिलचस्प है ताकि आप इसे परीक्षण उद्देश्य के लिए स्मृति कार्यान्वयन में बदल सकें।

तो मेरा सुझाव है, यह ITable<T> कॉल करने के लिए है, यह एक ही इंटरफ़ेस है कि LINQ Table<T> वस्तु देते हैं, और इसका इस्तेमाल अंदर अपने विशिष्ट डोमेन खजाने (बजाय नहीं)।

फिर आप मेमोरी ITable<T> कार्यान्वयन में स्मृति का उपयोग कर स्मृति में विशिष्ट भंडारों का उपयोग कर सकते हैं।

सरल स्मृति में ITable<T> लागू करने के लिए जिस तरह से एक List<T> का उपयोग करें और .AsQueryable() विस्तार विधि का उपयोग कर एक IQueryable<T> इंटरफ़ेस प्राप्त करने के लिए है।

public class InMemoryTable<T> : ITable<T> 
{ 
    private List<T> list; 
    private IQueryable<T> queryable; 

    public InMemoryTable<T>(List<T> list) 
    { 
     this.list = list; 
     this.queryable = list.AsQueryable(); 
    } 

    public void Add(T entity) { list.Add(entity); } 
    public void Remove(T entity) { list.Remove(entity); } 

    public IEnumerator<T> GetEnumerator() { return list.GetEnumerator(); } 

    public Type ElementType { get { return queryable.ElementType; } } 
    public IQueryProvider Provider {  get { return queryable.Provider; } } 
    ... 
} 

आप परीक्षण के लिए डेटाबेस के अलगाव में काम कर सकते हैं, लेकिन वास्तविक विशिष्ट भंडारों के साथ जो अधिक डोमेन अंतर्दृष्टि प्रदान करते हैं।

2

यह थोड़ी देर हो चुकी है ... लेकिन कोडप्लेक्स पर CommonLibrary.NET पर आईरिपोजिटरी कार्यान्वयन पर नज़र डालें। यह एक बहुत अच्छी सुविधा सेट है।

आपकी समस्या के बारे में, मैं अपने रिपोजिटरी कार्यान्वयन में GetAllProducts(), GetAllEmployees() जैसी विधियों का उपयोग करके बहुत से लोगों को देखता हूं। यह अनावश्यक है और आपके भंडार को सामान्य होने की अनुमति नहीं देता है। आपको केवल GetAll() या All() की आवश्यकता है। ऊपर प्रदान किया गया समाधान हालांकि नामकरण समस्या को हल करता है।

यह CommonLibrary.NET प्रलेखन ऑनलाइन से लिया जाता है:

0.9.4 बीटा 2 एक शक्तिशाली भंडार कार्यान्वयन है।

* Supports all CRUD methods (Create, Retrieve, Update, Delete) 
* Supports aggregate methods Min, Max, Sum, Avg, Count 
* Supports Find methods using ICriteria<T> 
* Supports Distinct, and GroupBy 
* Supports interface IRepository<T> so you can use an In-Memory table for unit-testing 
* Supports versioning of your entities 
* Supports paging, eg. Get(page, pageSize) 
* Supports audit fields (CreateUser, CreatedDate, UpdateDate etc) 
* Supports the use of Mapper<T> so you can map any table record to some entity 
* Supports creating entities only if it isn't there already, by checking for field values. 
संबंधित मुद्दे