ओआरएम लॉजिक एनकैप्सुलेट करने के लिए रिपॉजिटरी पैटर्न के विकल्प?


24

मुझे सिर्फ एक ORM को स्विच करना पड़ा है और यह एक अपेक्षाकृत कठिन काम था, क्योंकि क्वेरी लॉजिक हर जगह लीक हो रहा था। अगर मुझे कभी भी एक नया एप्लिकेशन विकसित करना होता, तो मेरी व्यक्तिगत पसंद बदलाव के लिए भविष्य के लिए सभी क्वेरी तर्क (ORM का उपयोग करके) को इनकैप्सुलेट करना होगा। रिपॉजिटरी पैटर्न कोड को बनाए रखने के लिए काफी परेशानी है और इसलिए मैं सोच रहा था कि क्या समस्या को हल करने के लिए कोई अन्य पैटर्न है?

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

मेरा पहला विचार एक सामान्य प्रकार का भंडार था, जिसमें मैं विस्तार विधियों के माध्यम से विशिष्ट प्रकार के रिपॉजिटरी वर्गों के लिए आवश्यक तरीकों को जोड़ता हूं, लेकिन स्थिर तरीकों का परीक्षण करने वाली इकाई भयानक रूप से दर्दनाक है। अर्थात:

public static class PersonExtensions
{
    public static IEnumerable<Person> GetRetiredPeople(this IRepository<Person> personRep)
    {
        // logic
    }
}

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

1
वहाँ विकल्प हैं। ऐसा ही एक क्वेरी ऑब्जेक्ट पैटर्न का उपयोग कर रहा है, जो कि मैंने उपयोग नहीं किया है, एक अच्छे दृष्टिकोण की तरह लगता है। रिपोजिटरी IMHO एक अच्छा पैटर्न है अगर सही इस्तेमाल किया गया है लेकिन सभी नहीं हैं और सभी को समाप्त करते हैं
dreza

जवाबों:


14

सबसे पहले: एक सामान्य रिपॉजिटरी को एक आधार वर्ग माना जाना चाहिए और पूर्ण कार्यान्वयन नहीं होना चाहिए। इसकी मदद से आपको सामान्य CRUD के तरीकों को लाने में मदद करनी चाहिए। लेकिन आपको अभी भी क्वेरी विधियों को लागू करना है:

public class UserRepository : EntityFrameworkRepository<User>, IUserRepository
{
    public UserRepository(DbContext context){}

    public ICollection<Person> FindRetired()
    {
        return context.Persons.Where(p => p.Status == "Retired").ToList();
    }
}

दूसरा:

वापसी न करें IQueryable। यह एक टपका हुआ अमूर्त है। उस इंटरफ़ेस को स्वयं लागू करने का प्रयास करें या अंतर्निहित db प्रदाता के बारे में ज्ञान के बिना इसका उपयोग करें। उदाहरण के लिए, प्रत्येक db प्रदाता के पास उत्सुक लोड संस्थाओं के लिए स्वयं का API है। इसलिए यह टपका हुआ अमूर्त है।

विकल्प

एक विकल्प के रूप में आप प्रश्नों का उपयोग कर सकते हैं (उदाहरण के लिए कमांड / क्वेरी अलगाव पैटर्न द्वारा परिभाषित)। CQS का वर्णन विकिपीडिया में किया गया है।

आप मूल रूप से क्वेरी क्लासेस बनाते हैं जिसे आप आमंत्रित करते हैं:

public class GetMyMessages
{
    public GetMyMessages(IDbConnection connection)
    {
    }


    public Message[] Execute(DateTime minDate)
    {
        //query
    }
}

प्रश्नों के साथ महान बात यह है कि आप कोड को अव्यवस्थित किए बिना विभिन्न प्रश्नों के लिए विभिन्न डेटा एक्सेस विधियों का उपयोग कर सकते हैं। आप उदाहरण के लिए एक वेब सेवा का उपयोग कर सकते हैं, दूसरे में nhibernate और तीसरे में ADO.NET।

यदि आप एक .NET कार्यान्वयन में रुचि रखते हैं, तो मेरा लेख पढ़ें: http://blog.gauffin.org/2012/10/griffin-decoupled-the-queries/


क्या आपके द्वारा सुझाए गए वैकल्पिक पैटर्न से बड़ी परियोजनाओं पर और अधिक अव्यवस्था पैदा नहीं होगी, यह देखते हुए कि रिपोजिटरी पैटर्न में एक विधि क्या होगी, अब पूरी कक्षा है?
दांते

3
यदि आपकी छोटी कक्षाएँ होने की परिभाषा अव्यवस्थित है, तो हाँ। उस मामले में आपको ठोस सिद्धांतों का पालन करने की कोशिश नहीं करनी चाहिए क्योंकि उन्हें लागू करने से हमेशा अधिक (छोटे) वर्ग उत्पन्न होंगे।
jgauffin

2
छोटे वर्ग बेहतर होते हैं, लेकिन मौजूदा क्वेरी कक्षाओं का नेविगेशन अधिक परेशानी वाला नहीं होगा, कहने की तुलना में, एक (डोमेन) रिपॉजिटरी के इंटेलीसेंस को ब्राउज़ करने से देखने के लिए, अगर नेकसेरी कार्यक्षमता वहां निहित है और यदि नहीं, तो इसे लागू करें।
दांते

4
परियोजना संरचना अधिक महत्वपूर्ण हो जाती है। यदि आप सभी प्रश्नों को एक नाम स्थान पर रखते हैं जैसे YourProject.YourDomainModelName.Queriesकि नेविगेट करना आसान होगा।
jgauffin

एक बात मैं CQS के साथ एक अच्छे तरीके से करना मुश्किल पा रहा हूं, सामान्य प्रश्न हैं। जैसे, एक उपयोगकर्ता को दिया गया, एक क्वेरी सभी संबद्ध छात्रों (ग्रेड, खुद के बच्चे आदि) को मिलती है। अब एक अन्य क्वेरी को एक उपयोगकर्ता के लिए बच्चे प्राप्त करने की आवश्यकता है और फिर उन पर कुछ ऑपरेशन करें - इसलिए क्वेरी 2 क्वेरी 1 में आम तर्क का लाभ उठा सकती है लेकिन ऐसा करने का कोई सरल तरीका नहीं है। मैं किसी भी CQS कार्यान्वयन को खोजने में सक्षम नहीं हुआ हूं जो आसानी से इसकी सुविधा देता है। भंडार इस संबंध में बहुत मदद करते हैं। किसी भी पैटर्न के प्रशंसक नहीं हैं, लेकिन सिर्फ मेरे टेक को साझा कर रहे हैं।
मृकफिन

8

पहले मुझे लगता है कि ओआरएम आपके डेटाबेस पर पहले से ही पर्याप्त अमूर्त है। कुछ ORM सभी सामान्य संबंधपरक डेटाबेस (NHibernate MSSQL, Oracle, MySQL, Postgrad, आदि के लिए बाइंडिंग प्रदान करते हैं।) इसलिए इसके शीर्ष पर नए अमूर्त का निर्माण करना मेरे लिए लाभदायक नहीं लगता है। इसके अलावा, तर्क है, कि आपको "सार दूर" करने की आवश्यकता है, यह ओआरएम अर्थहीन है।

यदि आप अभी भी इस अमूर्तता का निर्माण करना चाहते हैं, तो मैं रिपॉजिटरी पैटर्न के खिलाफ जाऊंगा। यह शुद्ध एसक्यूएल की उम्र में बहुत अच्छा था, लेकिन आधुनिक ओआरएम के सामने यह काफी परेशानी भरा है। मुख्य रूप से क्योंकि आप अंत में सीआरएम संचालन और क्वेरी की तरह अधिकांश ओआरएम सुविधाओं को फिर से लागू करते हैं।

अगर मुझे इस तरह के सार का निर्माण करना था, तो मैं इन नियमों / पैटर्न का उपयोग करूंगा

  • CQRS
  • मौजूदा ORM सुविधाओं का यथासंभव उपयोग करें
  • केवल जटिल तर्क और प्रश्नों को इनकैप्सुलेट करें
  • ORM के "प्लंबिंग" को किसी आर्किटेक्चर के अंदर छिपाने की कोशिश करें

ठोस क्रियान्वयन में, मैं बिना किसी लपेटे के, सीधे ORM के CRUD संचालन का उपयोग करूंगा। मैं सीधे कोड में सरल प्रश्न भी करूंगा। लेकिन जटिल प्रश्नों को उनकी अपनी वस्तुओं में समझाया जाएगा। ओआरएम को "छिपाने" के लिए, मैं डेटा संदर्भ को पारदर्शी रूप से एक सेवा / यूआई ऑब्जेक्ट में इंजेक्ट करने की कोशिश करूंगा और ऑब्जेक्ट्स को क्वेरी करने के लिए ऐसा ही किया है।

अंतिम बात जो मैं कहना चाहूंगा, वह यह है कि बहुत से लोग ओआरएम का उपयोग बिना जाने कैसे करते हैं और इसका सबसे अच्छा "लाभ" कैसे बनाते हैं। रिपॉजिटरी की सिफारिश करने वाले लोग आमतौर पर इस तरह के होते हैं।

एक अनुशंसित पढ़ने के रूप में, मैं कहूंगा कि आयंडे का ब्लॉग , विशेष रूप से यह लेख


+1 "अंतिम बात जो मैं कहना चाहूंगा, वह यह है कि बहुत से लोग ओआरएम का उपयोग बिना जाने कैसे करते हैं और इसका उपयोग कैसे करें और इससे सर्वश्रेष्ठ" लाभ "कैसे बना सकते हैं। रिपॉजिटरी की सिफारिश करने वाले लोग आमतौर पर इस तरह के होते हैं"
मिस्टर

1

टपका कार्यान्वयन के साथ समस्या यह है कि आपको कई अलग-अलग फ़िल्टर शर्तों की आवश्यकता है।

यदि आप एक रिपॉजिटरी विधि FindByExample को कार्यान्वित करते हैं और इसे इस तरह से उपयोग करते हैं, तो आप इस एपीआई को संकीर्ण कर सकते हैं

// find all retired persons
Person filter = new Person {Status=PersonStatus.Retired};
IEnumerable<Person> found = personRepository.FindByExample(filter);


// find all persons who have a dog named "sam"
Person filter = new Person();
filter.AddPet(new Dog{Name="sam"});
IEnumerable<Person> found = personRepository.FindByExample(filter);

0

अपने एक्सटेंशन को IRepository के बजाय IQueryable पर संचालित करने पर विचार करें।

public static class PersonExtensions
{
    public static IQueryable<Person> AreRetired(this IQueryable<Person> people)
    {
        return people.Where(p => p.Status == "Retired");
    }
}

इकाई परीक्षण के लिए:

List<Person> people = new List<Person>();
people.Add(new Person() { Name = "Bob", Status = "Retired" });
people.Add(new Person() { Name = "Sam", Status = "Working" });

var retiredPeople = people.AsQueryable().AreRetired();
// assert that Bob is in the list
// assert that Sam is not in the list

परीक्षण के लिए आवश्यक कोई फ़ॉकिंग मॉकिंग नहीं। आवश्यकतानुसार अधिक जटिल प्रश्न बनाने के लिए आप इन विस्तार विधियों को भी जोड़ सकते हैं।


5
-1 क) IQueryableएक खराब माँ है, या एक GOD इंटरफ़ेस है। इसे लागू करना बहुत है और बहुत कम (यदि कोई हो) LINQ को Sql प्रदाताओं को पूरा करना है। ख) विस्तार विधियां विस्तार (एक अंतिम संस्कार ओओपी सिद्धांतों में से एक) को असंभव बनाती हैं।
jgauffin

0

मैंने यहाँ NHibernate के लिए एक बहुत साफ क्वेरी ऑब्जेक्ट पैटर्न लिखा है: https://github.com/shaynevanasperen/NHibernate.Session.Operations

यह इस तरह से एक इंटरफेस का उपयोग करके काम करता है:

public interface IDatabases
{
    ISessionManager SessionManager { get; }

    T Query<T>(IDatabaseQuery<T> query);
    T Query<T>(ICachedDatabaseQuery<T> query);

    void Command(IDatabaseCommand command);
    T Command<T>(IDatabaseCommand<T> command);
}

इस तरह एक POCO इकाई वर्ग को देखते हुए:

class Database1Poco
{
    public int Property1 { get; set; }
    public string Property2 { get; set; }
}

आप इस तरह क्वेरी ऑब्जेक्ट बना सकते हैं:

class Database1PocoByProperty1 : DatabaseQuery<Database1Poco>
{
    public override Database1Poco Execute(ISessionManager sessionManager)
    {
        return sessionManager.Session.Query<Database1Poco>().SingleOrDefault(x => x.Property1 == Property1);
    }

    public int Property1 { get; set; }
}

और फिर उन्हें इस तरह से उपयोग करें:

var database1Poco = _databases.Query(new Database1PocoByProperty1 { Property1 = 1 });

1
हालांकि यह लिंक प्रश्न का उत्तर दे सकता है, लेकिन उत्तर के आवश्यक भागों को शामिल करना और संदर्भ के लिए लिंक प्रदान करना बेहतर है। लिंक-केवल उत्तर अमान्य हो सकते हैं यदि लिंक किए गए पृष्ठ बदल जाते हैं।
डैन पिचेलमैन

मेरी माफी, मैं जल्दी में था। कुछ उदाहरण कोड दिखाने के लिए अपडेट किया गया उत्तर।
शाइनी

1
अधिक सामग्री वाली पोस्ट को बेहतर बनाने के लिए +1।
सांगो
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.