6

मेरे डोमेन कक्षाएं एक-से-कई मैपिंग है कि आम तौर पर निम्नलिखित प्रपत्र (अपरीक्षित कोड) ले:क्या मैं अपने आईसीओलेक्शन <T> फ़ील्ड को छुपा सकता हूं जब मेरे पास केवल ईएफ 4 कोड में एक से अधिक मैपिंग है?

public Customer Customer 
{ 
    // Public methods. 

    public Order AddOrder(Order order) 
    { 
     _orders.Add(order); 
    } 

    public Order GetOrder(long id) 
    { 
     return _orders.Where(x => x.Id).Single(); 
    } 

    // etc. 

    // Private fields. 

    private ICollection<Order> _orders = new List<Order>(); 
} 

EF4 code-only samples मैंने देखा है एक सार्वजनिक ICollection बेनकाब जब एक-से-कई रिश्तों के साथ काम कर।

क्या मेरे संग्रह को जारी रखने और पुनर्स्थापित करने का कोई तरीका है? यदि नहीं, तो ऐसा लगता है कि मेरी डोमेन ऑब्जेक्ट्स को ओआरएम की आवश्यकताओं को पूरा करने के लिए डिज़ाइन किया जाएगा, जो प्रयास की भावना के खिलाफ जाना प्रतीत होता है। एक आईसीओलेक्शन (इसके जोड़ों के साथ, इत्यादि के साथ) का खुलासा करना विशेष रूप से साफ नहीं लगता है, और यह मेरा डिफ़ॉल्ट दृष्टिकोण नहीं होगा।

अद्यतन

मिले this post चलता है कि यह मई में संभव नहीं था। बेशक, माइक्रोसॉफ्ट पोस्टर ने कहा था कि वे "इसे लागू करने पर दृढ़ता से विचार कर रहे थे" (मुझे उम्मीद है) और हम आधे साल हैं, तो शायद कुछ प्रगति हुई है?

उत्तर

1

मैंने पाया कि जो कुछ भी किया गया था, ईएफ को सार्वजनिक होने के लिए ICollection<T> की आवश्यकता है। मुझे लगता है कि ऐसा इसलिए है क्योंकि जब डेटाबेस डेटाबेस से लोड होते हैं, तो मानचित्रण संग्रह संपत्ति की तलाश करता है, संग्रह प्राप्त करता है और फिर प्रत्येक बच्चे ऑब्जेक्ट्स को जोड़ने के लिए संग्रह के Add विधि को कॉल करता है।

मैं यह सुनिश्चित करना चाहता था कि अतिरिक्त ऑब्जेक्ट पर एक विधि के माध्यम से जोड़ा गया था, इसलिए संग्रह को लपेटने का एक समाधान बनाया गया, जोड़ को पकड़कर और इसे मेरी पसंदीदा विधि में निर्देशित किया गया।

List और अन्य संग्रह प्रकारों को विस्तारित करना संभव नहीं था क्योंकि Add विधि वर्चुअल नहीं है। एक विकल्प Collection वर्ग का विस्तार करना और InsertItem विधि को ओवरराइड करना है।

मैं केवल उन लोगों के रूप ICollection<T> इंटरफ़ेस का Add, Remove, और Clear कार्यों पर ध्यान केंद्रित किया है जो कि संग्रह को संशोधित कर सकते हैं।

पहला, मेरा बेस संग्रह रैपर है जो ICollection<T> इंटरफ़ेस लागू करता है डिफ़ॉल्ट व्यवहार सामान्य संग्रह का होता है। हालांकि, कॉलर कॉल करने के लिए वैकल्पिक Add विधि निर्दिष्ट कर सकता है। इसके अलावा, कॉलर Add, Remove, Clear संचालन को null पर विकल्पों को सेट करके अनुमति नहीं दे सकता है। यदि कोई भी विधि का उपयोग करने का प्रयास करता है तो इसका परिणाम NotSupportedException में फेंक दिया जाता है।

अपवाद की फेंकना उतना अच्छा नहीं है जितना कि पहले स्थान पर पहुंच को रोकना। हालांकि, कोड का परीक्षण किया जाना चाहिए (इकाई परीक्षण) और एक अपवाद बहुत जल्दी मिलेगा और उपयुक्त कोड परिवर्तन किया जाएगा।

public abstract class WrappedCollectionBase<T> : ICollection<T> 
{ 

    private ICollection<T> InnerCollection { get { return GetWrappedCollection(); } } 

    private Action<T> addItemFunction; 
    private Func<T, bool> removeItemFunction; 
    private Action clearFunction; 


    /// <summary> 
    /// Default behaviour is to be like a normal collection 
    /// </summary> 
    public WrappedCollectionBase() 
    { 
     this.addItemFunction = this.AddToInnerCollection; 
     this.removeItemFunction = this.RemoveFromInnerCollection; 
     this.clearFunction = this.ClearInnerCollection; 
    } 

    public WrappedCollectionBase(Action<T> addItemFunction, Func<T, bool> removeItemFunction, Action clearFunction) : this() 
    { 
     this.addItemFunction = addItemFunction; 
     this.removeItemFunction = removeItemFunction; 
     this.clearFunction = clearFunction; 
    } 

    protected abstract ICollection<T> GetWrappedCollection(); 

    public void Add(T item) 
    { 
     if (this.addItemFunction != null) 
     { 
      this.addItemFunction(item); 
     } 
     else 
     { 
      throw new NotSupportedException("Direct addition to this collection is not permitted"); 
     } 
    } 

    public void AddToInnerCollection(T item) 
    { 
     this.InnerCollection.Add(item); 
    } 

    public bool Remove(T item) 
    { 
     if (removeItemFunction != null) 
     { 
      return removeItemFunction(item); 
     } 
     else 
     { 
      throw new NotSupportedException("Direct removal from this collection is not permitted"); 
     } 
    } 

    public bool RemoveFromInnerCollection(T item) 
    { 
     return this.InnerCollection.Remove(item); 
    } 

    public void Clear() 
    { 
     if (this.clearFunction != null) 
     { 
      this.clearFunction(); 
     } 
     else 
     { 
      throw new NotSupportedException("Clearing of this collection is not permitted"); 
     } 
    } 

    public void ClearInnerCollection() 
    { 
     this.InnerCollection.Clear(); 
    } 

    public bool Contains(T item) 
    { 
     return InnerCollection.Contains(item); 
    } 

    public void CopyTo(T[] array, int arrayIndex) 
    { 
     InnerCollection.CopyTo(array, arrayIndex); 
    } 

    public int Count 
    { 
     get { return InnerCollection.Count; } 
    } 

    public bool IsReadOnly 
    { 
     get { return ((ICollection<T>)this.InnerCollection).IsReadOnly; } 
    } 

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

    System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() 
    { 
     return InnerCollection.GetEnumerator(); 
    } 

} 

यह देखते हुए कि बेस क्लास हम इसे दो तरीकों से उपयोग कर सकते हैं। उदाहरण मूल पोस्ट ऑब्जेक्ट्स का उपयोग कर रहे हैं।

1) लिपटे संग्रह की एक विशिष्ट प्रकार (उदाहरण के लिए, List सार्वजनिक वर्ग WrappedListCollection बनाएं): WrappedCollectionBase, IList { निजी सूची innerList;

public WrappedListCollection(Action<T> addItemFunction, Func<T, bool> removeItemFunction, Action clearFunction) 
     : base(addItemFunction, removeItemFunction, clearFunction) 
    { 
    this.innerList = new List<T>(); 
    } 

    protected override ICollection<T> GetWrappedCollection() 
    { 
     return this.innerList; 
    } 
<...snip....> // fill in implementation of IList if important or don't implement IList 
    } 

यह तो इस्तेमाल किया जा सकता:

public Customer Customer 
{ 
public ICollection<Order> Orders {get { return _orders; } } 
// Public methods. 

public void AddOrder(Order order) 
{ 
    _orders.AddToInnerCollection(order); 
} 

// Private fields. 

private WrappedListCollection<Order> _orders = new WrappedListCollection<Order>(this.AddOrder, null, null); 
} 

2) एक संग्रह दें का उपयोग कर लिपटे होने के लिए

public class WrappedCollection<T> : WrappedCollectionBase<T> 
{ 
    private ICollection<T> wrappedCollection; 

    public WrappedCollection(ICollection<T> collectionToWrap, Action<T> addItemFunction, Func<T, bool> removeItemFunction, Action clearFunction) 
     : base(addItemFunction, removeItemFunction, clearFunction) 
    { 
     this.wrappedCollection = collectionToWrap; 
    } 

    protected override ICollection<T> GetWrappedCollection() 
    { 
     return this.wrappedCollection; 
    } 
} 

जो इस प्रकार किया जा सकता है:

{ सार्वजनिक ICollection आदेश {प्राप्त {वापसी _wrappedOrders; }} // सार्वजनिक विधियां।

public void AddOrder(Order order) 
{ 
    _orders.Add(order); 
} 

// Private fields. 
private ICollection<Order> _orders = new List<Order>(); 
private WrappedCollection<Order> _wrappedOrders = new WrappedCollection<Order>(_orders, this.AddOrder, null, null); 
} 

वहाँ WrappedCollection कंस्ट्रक्टर्स उदाहरण के लिए, ओवरराइड करने के लिए कॉल करने के लिए जोड़ने लेकिन निकालने रखने के लिए और स्पष्ट रूप में सामान्य

private WrappedListCollection<Order> _orders = new WrappedListCollection(this.AddOrder, (Order o) => _orders.RemoveFromInnerCollection(o),() => _orders.ClearInnerCollection()); 

मैं मानता हूँ कि यह सबसे अच्छा हो सकता है अगर एफई की आवश्यकता नहीं होगी कुछ अन्य तरीके हैं संग्रह सार्वजनिक होना है लेकिन यह समाधान मुझे अपने संग्रह के संशोधन को नियंत्रित करने की अनुमति देता है।

क्वेरीिंग के लिए संग्रह तक पहुंच को रोकने की समस्या के लिए आप ऊपर 2 का उपयोग कर सकते हैं और NotSupportedException फेंकने के लिए लपेटा गया चयन GetEnumerator विधि सेट कर सकते हैं। फिर आपकी GetOrder विधि इस तरह रह सकती है। लिपटे संग्रह को बेनकाब करने के लिए एक स्वच्छ विधि हो सकती है। उदाहरण के लिए:

public class WrappedCollection<T> : WrappedCollectionBase<T> 
{ 
    public ICollection<T> InnerCollection { get; private set; } 

    public WrappedCollection(ICollection<T> collectionToWrap, Action<T> addItemFunction, Func<T, bool> removeItemFunction, Action clearFunction) 
     : base(addItemFunction, removeItemFunction, clearFunction) 
    { 
     this.InnerCollection = collectionToWrap; 
    } 


    protected override ICollection<T> GetWrappedCollection() 
    { 
     return this.InnerCollection; 
    } 
} 

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

_orders.InnerCollection.Where(x => x.Id == id).Single(); 
0

यदि आप अपने _orders संग्रह का नाम अपने डेटाबेस में ऑर्डर टेबल के नाम पर बदलते हैं, तो यह काम करना चाहिए। सम्मेलन द्वारा संग्रह/संपत्तियों के लिए ईएफ मानचित्र तालिका/फ़ील्ड नाम। यदि आप एक अलग नाम का उपयोग करना चाहते हैं तो आप edmx फ़ाइल में मैपिंग संपादित कर सकते हैं।

AFAIK आप निजी संशोधक को छोड़ सकते हैं जैसा कि यह है। संग्रह सार्वजनिक होने की आवश्यकता नहीं है।

+0

अगर मैं कुछ भी लेकिन सार्वजनिक उस में मूल्यों को पढ़ने नहीं है करने के लिए अपने संग्रह की पहुँच संशोधक निर्धारित किया है। से "सुरक्षित" वापस करने के लिए "सार्वजनिक" एक साधारण स्विच, उदाहरण के लिए, यह सब काम करता है। साथ ही, यह सरल (उदा। स्ट्रिंग) गैर-सार्वजनिक गुणों को मानचित्रित नहीं कर रहा है। – dommer

+0

एक और तरीका एक टी 4 फ़ाइल जोड़ने और संग्रह के लिए IENumerable की एपी प्रॉपर्टी का पर्दाफाश करने के लिए इसे संपादित करना और डिफ़ॉल्ट आईसीओलेक्शन गुण सुरक्षित करना है। – Michael

1

दृढ़ता/डोमेन परतों का। डीबीसेट संग्रह तक पहुंच को छिपाने और नियंत्रित करने के लिए आप अपने डीबीकॉन्टेक्स्ट क्लास को इंटरफ़ेस भी कर सकते हैं। जैसे-जैसे यह निकलता है, डीबीसेट गुणों को संरक्षित किया जा सकता है, और मॉडल निर्माता उन्हें टेबल बनाते समय उठाएगा, लेकिन जब आप संग्रह तक पहुंचने का प्रयास करेंगे तो वे शून्य होंगे। डीबीसेट संग्रह को छुपाने के लिए इंटरफेस्ड डीबीकॉन्टेक्स्ट प्राप्त करने के लिए एक कारखाने विधि (मेरे उदाहरण में, CreateNewContext) का उपयोग कन्स्ट्रक्टर के बजाय किया जा सकता है।

कोडिंग में काफी प्रयास किए गए हैं, लेकिन यदि पीओसीओ के भीतर कार्यान्वयन विवरण छिपाना महत्वपूर्ण है, तो यह काम करेगा।

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

public interface ICustomer 
{ 
    void AddOrder(IOrder order); 
    IOrder GetOrder(long id); 
} 

public Customer : ICustomer 
{ 
    // Exposed methods: 
    void ICustomer.AddOrder(IOrder order) 
    { 
     if (order is Order) 
     orders.Add((Order)order); 
     else 
     throw new Exception("Hey! Not a mapped type!"); 
    } 

    IOrder ICustomer.GetOrder(long id) 
    { 
     return orders.Where(x => x.Id).Single(); 
    } 

    // public collection for EF 
    // The Order class definition would follow the same interface pattern illustrated 
    // here for the Customer class. 
    public ICollection<Order> orders = new List<Order>(); 
} 

public interface IMyContext 
{ 
    IEnumerable<ICustomer> GetCustomers(); 
    void AddCustomer(ICustomer customerObject); 
    ICustomer CreateNewCustomer() 
} 


public class MyContext : DbContext, IMyContext 
{ 
    public static IMyContext CreateNewContext() { return new MyContext(); } 

    public DbSet<Customer> Customers {get;set;} 
    public DbSet<Order> Orders {get;set;} 

    public IEnumerable<ICustomer> GetCustomers() 
    { 
     return Customers; 
    } 

    public void AddCustomer(ICustomer customerObject) 
    { 
     if (customerObject is Customer) 
     Customers.Add((Customer)customerObject); 
     else 
     throw new Exception("Hey! Not a mapped type"); 
    } 

    public ICustomer CreateNewCustomer() 
    { 
     return Customers.Create(); 
    } 

    // wrap the Removes, Finds, etc as necessary. Remember to add these to the 
    // DbContext's interface 

    // Follow this pattern also for Order/IOrder 

}