35

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

मैं डेटाबेस में 3 टेबल है: Group, Person, और GroupPersonMap

यहाँ मेरी स्थिति का एक उदाहरण है। GroupPersonMap एक लिंक तालिका है और इसमें Group और Person प्राथमिक कुंजी शामिल हैं। मैंने वीएस 2010 डिजाइनर के साथ 3 टेबलों का ईएफ मॉडल बनाया। ईएफ GroupPersonMap मानने के लिए पर्याप्त स्मार्ट था एक लिंक टेबल है, इसलिए यह इसे डिजाइनर में नहीं दिखाता है। मैं ईएफ के जेनरेट किए गए वर्गों की बजाय अपने मौजूदा डोमेन ऑब्जेक्ट्स का उपयोग करना चाहता हूं ताकि मैं मॉडल के लिए कोड जनरेशन बंद कर दूं।

public interface IRepository<T> where T: class 
{ 
    IQueryable<T> GetAll(); 
    T Add(T entity); 
    T Update(T entity); 
    void Delete(T entity); 
    void Save() 
} 

और एक सामान्य एफई भंडार:

public class EF4Repository<T> : IRepository<T> where T: class 
{ 
    public DbContext Context { get; private set; } 
    private DbSet<T> _dbSet; 

    public EF4Repository(string connectionString) 
    { 
     Context = new DbContext(connectionString); 
     _dbSet = Context.Set<T>(); 
    } 

    public EF4Repository(DbContext context) 
    { 
     Context = context; 
     _dbSet = Context.Set<T>(); 
    } 

    public IQueryable<T> GetAll() 
    { 
     // code 
    } 

    public T Insert(T entity) 
    { 
     // code 
    } 

    public T Update(T entity) 
    { 
     Context.Entry(entity).State = System.Data.EntityState.Modified; 
     Context.SaveChanges(); 
    } 

    public void Delete(T entity) 
    { 
     // code 
    } 

    public void Save() 
    { 
     // code 
    } 
} 

public class Group 
{ 
    public int GroupId { get; set; } 
    public string Name { get; set; } 

    public virtual ICollection<Person> People { get; set; } 
} 

public class Person 
{ 
    public int PersonId {get; set; } 
    public string FirstName { get; set; } 

    public virtual ICollection<Group> Groups { get; set; } 
} 

मैं बहुत की तरह एक सामान्य भंडार इंटरफेस है:

कि एफई मॉडल से मिलान मेरे मौजूदा कक्षाएं इस प्रकार हैं

अब मान लीजिए कि मैं मौजूदापर मौजूदा Group को मानचित्र बनाना चाहता हूं। मैं निम्नलिखित की तरह कुछ करने के लिए होता है:

 EFRepository<Group> groupRepository = new EFRepository<Group>("name=connString"); 
     EFRepository<Person> personRepository = new EFRepository<Person>("name=connString"); 

     var group = groupRepository.GetAll().Where(g => g.GroupId == 5).First(); 
     var person = personRepository.GetAll().Where(p => p.PersonId == 2).First(); 

     group.People.Add(person); 
     groupRepository.Update(group); 

लेकिन क्योंकि एफई सोचता Person नया है यह काम नहीं करता है, और INSERT डेटाबेस में Person जो एक प्राथमिक कुंजी बाधा त्रुटि का कारण होगा फिर से करने की कोशिश करेंगे । मुझे DbSet की Attach विधि का उपयोग ईएफ को बताने के लिए करना चाहिए कि Person डेटाबेस में पहले से मौजूद है, इसलिए और Person के बीच के बीच एक नक्शा बनाएं।

तो आदेश संदर्भ के लिए Person संलग्न करने के लिए अब मैं अपने IRepository के लिए एक Attach विधि जोड़नी होगी में:

EFRepository<Group> groupRepository = new EFRepository<Group>("name=connString"); 
EFRepository<Person> personRepository = new EFRepository<Person>(groupRepository.Context); 

var group = groupRepository.GetAll().Where(g => g.GroupId == 5).First(); 
var person = personRepository.GetAll().Where(p => p.PersonId == 2).First(); 

personRepository.Attach(person); 
group.People.Add(person); 
groupRepository.Update(group); 

फिक्स्ड:

public interface IRepository<T> where T: class 
{ 
    // existing methods 
    T Attach(T entity); 
} 

प्राथमिक कुंजी बाधा त्रुटि को ठीक करने। अब मुझे किसी अन्य मुद्दे से निपटना होगा जहां हर बार जब मैं समूह/व्यक्ति मानचित्र बनाता हूं तो डेटाबेस में Group डेटाबेस में अद्यतन किया जा रहा है। ऐसा इसलिए है क्योंकि मेरी EFRepository.Update() विधि में, इकाई स्थिति स्पष्ट रूप से Modified'. I must set the Group's state to पर अपरिवर्तित so the समूह की तालिका संशोधित नहीं होती है।

इसे ठीक करने के मैं, GroupUpdate मेरी IRepository लिए अधिभार कि जड़ इकाई को अपडेट नहीं है, या किसी प्रकार जोड़ना होगा इस मामले में:

public interface IRepository<T> where T: class 
{ 
    // existing methods 
    T Update(T entity, bool updateRootEntity); 
} 

अद्यतन विधि के EF4 implentation कुछ ऐसा दिखाई देगा इस तरह:

T Update(T entity, bool updateRootEntity) 
{ 
    if (updateRootEntity) 
     Context.Entry(entity).State = System.Data.EntityState.Modified; 
    else 
     Context.Entry(entity).State = System.Data.EntityState.Unchanged; 

    Context.SaveChanges(); 
} 

मेरा प्रश्न है: क्या मैं इसे सही तरीके से देख रहा हूं? मेरा रिपोजिटरी ईएफ केंद्रित दिखना शुरू कर रहा है क्योंकि मैं ईएफ और रिपोजिटरी पैटर्न के साथ काम करना शुरू कर देता हूं। इस लंबी पोस्ट को पढ़ने के लिए धन्यवाद

उत्तर

66

The primary reason why I want to use this pattern is to avoid calling EF 4.1 specific data access operations from the domain. I'd rather call generic CRUD operations from a IRepository interface. This will make testing easier

नहीं will not make your testing easierYou exposed IQueryable तो आपका भंडार is not unit testable

if I ever have to change the data access framework in the future, I will be able to do so without refactoring a lot of code.

नहीं तुम वैसे भी कोड का एक बहुत कुछ बदलने के लिए है क्योंकि आप IQueryable उजागर किया है और क्योंकि एफई/ORM टपकाया अमूर्त है होगा - अपने ऊपरी परत की उम्मीद है कुछ व्यवहार अपने ORM अंदर जादुई होता है (उदाहरण के आलसी लोड करने के लिए)। यह भंडार के लिए जाने के सबसे अजीब कारणों में से एक है। बस सही तकनीक का चयन करें और इसके दांव पाने के लिए इसका इस्तेमाल करें। यदि आपको बाद में इसे बदलना है तो इसका मतलब है कि you did a mistake and chose the wrong one or requirements have changed - किसी भी मामले में यह बहुत काम करेगा।

But this doesn't work because EF thinks Person is new, and will try to re-INSERT the Person into the database which will cause a primary key constraint error.

हां क्योंकि आप प्रत्येक भंडार के लिए एक नया संदर्भ उपयोग कर रहे हैं = यह गलत दृष्टिकोण है। रेपॉजिटरीज को संदर्भ साझा करना होगा। आपका दूसरा समाधान भी सही नहीं है क्योंकि आप अपनी ईएफ निर्भरता को एप्लिकेशन पर वापस डालते हैं - भंडार संदर्भ को उजागर कर रहा है। यह आमतौर पर दूसरे पैटर्न - काम की इकाई द्वारा हल किया जाता है। Unit of work wraps the context और कार्य की इकाई परमाणु परिवर्तन सेट - SaveChanges सभी संबंधित भंडारों द्वारा किए गए परिवर्तनों को करने के लिए काम की इकाई पर खुलासा होना चाहिए।

Now I have an issue with the Group being UPDATE'd in the database every time I want to create a Group/Person map.

आप राज्य को क्यों बदलते हैं? आपको रिपॉजिटरी से इकाई प्राप्त हुई है, जब तक कि आप इसे अलग नहीं करते हैं, Attach पर कॉल करने और राज्य को मैन्युअल रूप से बदलने का कोई कारण नहीं है। यह सब संलग्न इकाई पर स्वचालित रूप से होना चाहिए। बस SaveChanges पर कॉल करें। यदि आप पृथक इकाइयों का उपयोग कर रहे हैं तो you must correctly set state for every entity और ऐसे मामले में संबंध आपको सभी परिदृश्यों को संभालने के लिए वास्तव में कुछ तर्क या अद्यतन अधिभार की आवश्यकता होगी।

Am I approaching this the right way? My Repository is starting to look EF centric as I start to work with EF and the repository pattern.

मुझे ऐसा नहीं लगता है। सबसे पहले आप कुल जड़ों का उपयोग नहीं कर रहे हैं। यदि आप करते हैं तो आपको तुरंत पता चलेगा कि generic repository इसके लिए उपयुक्त नहीं है। कुल जड़ों के लिए रिपोजिटरी में रूट द्वारा एकत्रित संबंधों के साथ काम करने के लिए कुल रूट के लिए विशिष्ट विधियां हैं। GroupPerson कुल का हिस्सा नहीं है लेकिन GroupPersonMap होना चाहिए ताकि आपके व्यक्ति भंडार में व्यक्तियों को समूह जोड़ने और निकालने के लिए विशिष्ट विधियां होनी चाहिए (लेकिन खुद को समूह बनाना या हटाना नहीं)। इमो जेनेरिक रिपोजिटरी redundant layer है।

+4

+1 ग्रेट उत्तर –

+1

+1 इकाई ढांचे + काम की इकाई के लिए बहुत जानकारीपूर्ण –

+0

+1। भंडारों को एक ही dbcontext साझा करने की आवश्यकता है। मैं शर्त लगाता हूं कि मेरी समस्या यही है। – Jhayes2118