2011-01-09 13 views
18

मैं पीओसीओ टेम्पलेट के साथ एंटीटी फ्रेमवर्क 4 का उपयोग कर रहा हूं।EF4 कास्ट डायनामिक प्रॉक्सीज अंतर्निहित ऑब्जेक्ट

मैं एक सूची जहां MyObject गतिशील प्रॉक्सी हैं। मैं इस सूची को क्रमबद्ध करने के लिए XmlSerializer का उपयोग करना चाहता हूं, लेकिन मैं नहीं चाहता हूं कि वे डायनेमिक प्रॉक्सीज़ के रूप में क्रमबद्ध हों लेकिन अंडरलेइंग पीओसीओ ऑब्जेक्ट के रूप में।

मैं ContextOptions.ProxyCreationEnabled के बारे में पता है, लेकिन मुझे लगता है कि उपयोग करने के लिए नहीं करना चाहती। मैं सिर्फ यह जानना चाहता हूं कि सीरियलाइज करने के लिए पीओसीओ को अंडरलेइंग करने के लिए प्रॉक्सी ऑब्जेक्ट कैसे डाला जाए।

+1

यदि आप डब्ल्यूसीएफ सेवाओं का उपयोग कर रहे हैं, तो इस आलेख में आपकी सहायता करनी चाहिए: http://msdn.microsoft.com/en-us/library/ee705457.aspx – Devart

+0

उत्तर [यहां] (http://stackoverflow.com/ प्रश्न/25770369/मिल-अंतर्निहित इकाई-वस्तु-से-इकाई-ढांचा-प्रॉक्सी/25774651 # 25774651)। –

उत्तर

1

जैसा कि आप प्रॉक्सी क्रिएशन बंद नहीं करना चाहते हैं, आप ऑब्जेक्ट प्रॉपर्टी के लिए वर्चुअल कीवर्ड डालते हैं, जहां आप डायनामिक प्रॉक्सी ऑब्जेक्ट्स फंस जाते हैं (ईएफ संदर्भ आपके ऑब्जेक्ट को प्राप्त करता है और डायनामिक प्रॉपर्टी ऑब्जेक्ट्स को डायनामिक प्रॉक्सी ऑब्जेक्ट्स से बदल देता है)। ये डायनामिक प्रॉक्सी ऑब्जेक्ट्स आपके पीओसीओ इकाइयों से प्राप्त नहीं होते हैं, उनके पास केवल वही गुण होते हैं और आपके पीओसीओ के बजाय इस्तेमाल किया जा सकता है। यदि आपको वास्तव में पीओसीओ ऑब्जेक्ट में कनवर्ट करना होगा (और मुझे विश्वास नहीं है कि कोई इसे कास्ट करने के तरीके के साथ आएगा), तो आप कॉपी कन्स्ट्रक्टर लिखकर काम करने की कोशिश कर सकते हैं जो सभी गुणों को उत्तीर्ण तर्क से कॉपी करेगा (बहुत स्मार्ट नहीं प्रदर्शन दृष्टिकोण से, लेकिन आपको क्या करना है, आपको पेरिस ऑब्जेक्ट में System.Xml.Serialization.XmlTypeAttribute का उपयोग करना है जिसमें वर्चुअल प्रॉपर्टी (किस प्रकार में) को क्रमबद्ध करने के लिए सीरियलाइज़र को बताने के लिए पॉको के बजाय अपनी गतिशील प्रॉक्सी शामिल है।

6

ही समस्या का सामना आज और Value Injecter इस्तेमाल किया इसे हल करने की।

var dynamicProxyMember = _repository.FindOne<Member>(m=>m.Id = 1); 
var member = new Member().InjectFrom(dynamicProxyMember) as Member; 
+0

सदस्य डीबीकॉन्टेक्स्ट से जुड़ा हुआ है? –

+0

हां यह संलग्न है – Korayem

0

मैं एफई 5 में एक ही मुद्दे के साथ सामना करना पड़ा मैं अपने इकाई एक्सएमएल के लिए वस्तुओं को क्रमानुसार करने कोशिश कर रहा था: यह रूप में सरल रूप में है। @ कोरेयम के जवाब ने मुझे एक संकेत दिया। मैंने इसे थोड़ा और विकसित किया। कहीं मेरे कोड में मैं इस

string objXML = EntitySerializer.Serialize(entity); 

को क्रमानुसार विधि की तरह serializer बुला रहा था सामान्य है। तो विधि हेडर कुछ इस तरह है:

public static string Serialize<T>(T tObj) where T : class, new() 

तो मेरी विधि शरीर में मैं value injecter का उपयोग करें:

T obj = new T().InjectFrom(tObj) as T; 

यह सिर्फ मेरी entitites के सभी के लिए मेरी समस्या हल हो जाती।

2

बीमार एक समाधान है कि मुझे मदद की पेशकश के द्वारा इन पुराने हड्डियों की खुदाई। उम्मीद है कि यह किसी को पढ़ने में मदद करेगा।

तो, वहाँ वास्तव में दो समाधान हैं। आप आलसी लोड हो रहा है नहीं करना चाहते हैं तो आप हमेशा गतिशील प्रॉक्सी बंद कर सकते हैं और है कि आप बस entitiy दे देंगे:

public class MyContext : DbContext 
{ 
    public MyContext() 
    { 
     this.Configuration.ProxyCreationEnabled = false 
    } 

    public DbSet<NiceCat> NiceCats {get; set;} 
    public DbSet<CrazyCat> CrazyCats {get; set;} 
    public DbSet<MeanCat> MeanCats {get; set;} 

} 

अन्य समाधान ObjectContext उपयोग करने के लिए मूल इकाई प्रकार प्रॉक्सी के लिए में खड़ा प्राप्त करने के लिए है :

+11

ऑब्जेक्टकॉन्टेक्स्ट.गेटऑब्जेक्ट टाइप टाइप इकाई को टाइप करता है, न कि इकाई ऑब्जेक्ट। – kevinpo

1

अस्वीकरण: मैं इस समस्या का कुछ हद तक सामान्य समाधान बना लिया है। मुझे समाधान के लिए खोज करते समय यह पुराना सवाल मिला, इसलिए मुझे लगा कि मैं अपना समाधान यहां साझा करने में मदद करता हूं ताकि कोई भी अपनी समस्या को उसी समस्या पर फेंकने में मदद कर सके।

मैं एक ही समस्या में पड़ गए: मैं इकाई की रूपरेखा से कुछ सामान लाने के लिए, और उसके बाद ASP.NET वेब एपीआई का उपयोग करने के लिए इसे एक्सएमएल क्रमानुसार करने की जरूरत है।मैंने आलसी लोडिंग और प्रॉक्सी सृजन को अक्षम करने और Include() का उपयोग करने की कोशिश की है, लेकिन कुछ बुनियादी वर्ग पदानुक्रम के अलावा किसी भी चीज पर जिसने विशाल एसक्यूएल प्रश्नों को जन्म दिया जो निष्पादित करने में कई मिनट लग गए। मैंने पाया कि आलसी लोडिंग का उपयोग करके और प्रत्येक संपत्ति को दोबारा संदर्भित करना कई बार पेड़ को लोड करने से कई बार तेज था, इसलिए मुझे लगा कि मुझे आलसी लोड करने के लिए एक रास्ता चाहिए, इसे पीओसीओ के रूप में प्राप्त करें, और उसके बाद इसे क्रमबद्ध करें।

मैंने इस समाधान के आधार के रूप में गर्ट आर्नोल्ड द्वारा this answer का उपयोग किया है, और फिर वहां से काम किया है।

मैंने डीबीसीएन्टेक्स्ट में एक अनियंत्रित विधि बनाई है जो एक (प्रॉक्सी) क्लास इंस्टेंस लेता है (उदाहरण के लिए आप DbContext.Find (id) से वापस आते हैं) और उस इकाई को वास्तविक पॉको प्रकार के रूप में वापस कर देता है प्रत्येक संपत्ति, उप-संपत्ति आदि पूरी तरह से लोड और serialization के लिए तैयार है।

Unproxy विधि और कुछ केवल पढ़ने के लिए फ़ील्ड:

readonly Type ignoreOnUnproxyAttributeType = typeof(IgnoreOnUnproxyAttribute); 
readonly string genericCollectionTypeName = typeof(ICollection<>).Name; 

public T UnProxy<T>(T proxyObject) where T : class 
{ 
    // Remember the proxyCreationEnabled value 
    var proxyCreationEnabled = Configuration.ProxyCreationEnabled; 

    try 
    { 
     Configuration.ProxyCreationEnabled = false; 
     T poco = Entry(proxyObject).CurrentValues.ToObject() as T; // Convert the proxy object to a POCO object. This only populates scalar values and such, so we have to load other properties separately. 

     // Iterate through all properties in the POCO type 
     foreach (var property in poco.GetType().GetProperties()) 
     { 
      // To prevent cycles, like when a child instance refers to its parent and the parent refers to its child, we'll ignore any properties decorated with a custom IgnoreOnUnproxyAttribute. 
      if (Attribute.IsDefined(property, ignoreOnUnproxyAttributeType)) 
      { 
       property.SetValue(poco, null); 
       continue; 
      } 

      dynamic proxyPropertyValue = property.GetValue(proxyObject); // Get the property's value from the proxy object 

      if (proxyPropertyValue != null) 
      { 
       // If the property is a collection, get each item in the collection and set the value of the property to a new collection containing those items. 
       if (property.PropertyType.IsGenericType && property.PropertyType.Name == genericCollectionTypeName) 
       {        
        SetCollectionPropertyOnPoco<T>(poco, property, proxyPropertyValue); 
       } 
       else 
       { 
        // If the property is not a collection, just set the value of the POCO object to the unproxied (if necessary) value of the proxy object's property. 
        if (proxyPropertyValue != null) 
        { 
         // If the type of the property is one of the types in your model, the value needs to be unproxied first. Otherwise, just set the value as is. 
         var unproxiedValue = (ModelTypeNames.Contains(property.PropertyType.Name)) ? SafeUnproxy(proxyPropertyValue) : proxyPropertyValue; 
         property.SetValue(poco, unproxiedValue); 
        } 
       } 
      } 
     } 

     return poco; // Return the unproxied object 
    } 
    finally 
    { 
     // Zet ProxyCreationEnabled weer terug naar de oorspronkelijke waarde. 
     Configuration.ProxyCreationEnabled = proxyCreationEnabled; 
    } 
} 

ModelTypeNames एक संपत्ति मैं अपने DBContext कि बस सभी मॉडल में इस्तेमाल किया प्रकार के रिटर्न को जोड़ दिया है। इस तरह हम जान सकें कि कौन प्रकार हम unproxy की जरूरत है:

private Collection<string> modelTypeNames; 

private Collection<string> ModelTypeNames 
{ 
    get 
    { 
     if (modelTypeNames == null) 
     { 
      // We'll figure out all the EF model types by simply returning all the type arguments of every DbSet<> property in the dbContext. 
      modelTypeNames = new Collection<string>(typeof(VerhaalLokaalDbContext).GetProperties().Where(d => d.PropertyType.Name == typeof(DbSet<>).Name).SelectMany(d => d.PropertyType.GenericTypeArguments).Select(t => t.Name).ToList()); 
     } 

     return modelTypeNames; 
    } 
} 

ICollection <> गुण, हम पहले एक नई जेनेरिक संग्रह का दृष्टांत की जरूरत से निपटने के लिए (मैं के साथ एक HashSet <> बनाने के लिए प्रतिबिंब का उपयोग कर रहा सही प्रकार तर्क), सभी मानों के माध्यम से पुनरावृत्त, प्रत्येक मान को असम्पीडित करें और इसे नए हैशसेट में जोड़ें, जिसे तब पीओसीओ की संपत्ति के मान के रूप में उपयोग किया जाता है।

private void SetCollectionPropertyOnPoco<T>(T poco, PropertyInfo property, dynamic proxyPropertyValue) where T : class 
{ 
    // Create a HashSet<> with the correct type 
    var genericTypeArguments = ((System.Type)(proxyPropertyValue.GetType())).GenericTypeArguments; 
    var hashSetType = typeof(System.Collections.Generic.HashSet<>).MakeGenericType(genericTypeArguments); 
    var hashSet = Activator.CreateInstance(hashSetType); 

    // Iterate through each item in the collection, unproxy it, and add it to the hashset. 
    foreach (var item in proxyPropertyValue) 
    { 
     object unproxiedValue = SafeUnproxy(item); 
     hashSetType.GetMethod("Add").Invoke(hashSet, new[] { unproxiedValue }); // Add the unproxied value to the new hashset 
    } 

    property.SetValue(poco, hashSet); // Set the new hashset as the poco property value.   
} 

ध्यान दें कि मैं Unproxy के बजाय SafeUnproxy को बुला रहा हूं। यह टाइप अनुमान के साथ एक अजीब मुद्दे की वजह से है। आम तौर पर जब आप प्रॉक्सी ऑब्जेक्ट को अनप्रोक्सी() पर पास करते हैं, तो टाइप करें अनुमान लगाएगा कि टी वह पॉको प्रकार है जिसे आप वास्तव में चाहते हैं, डेटाप्रोक्सी का प्रकार नहीं (वह जो आपके मॉडेलपोको टाइप 033 9ई043 ए 5559 डी04303 एम 3033 इत्यादि जैसा दिखता है)। हालांकि, कभी-कभी यह अनुमान टी dataproxy प्रकार है, जो

T poco = Entry(proxyObject).CurrentValues.ToObject() as T; 

लाइन चल रही है के रूप में, क्योंकि पोको वस्तु, प्रॉक्सी प्रकार में ढाला नहीं जा सकता है ऑपरेटर के रूप में अशक्त लौटने के लिए पैदा कर रहा है। इसे ठीक करने के लिए, SafeUnproxy अनुमान पर भरोसा करने के बजाय एक स्पष्ट प्रकार पैरामीटर के साथ अनप्रोक्सी विधि को कॉल करता है: यह आपके द्वारा पारित पैरामीटर के प्रकार की जांच करता है, और यदि नामस्थान System.Data.Entity.DynamicProxies है, तो यह प्रकार का उपयोग करेगा सामान्य टाइप तर्क के रूप में बेसटाइप (जो एक गतिशीलप्रकार प्रकार के मामले में संबंधित पीओसीओ प्रकार है)।

:

private void ExplicitlyLoadMembers(dynamic item) 
{ 
    foreach (var property in ((Type)item.GetType()).GetProperties()) 
    { 
     DbEntityEntry dbEntityEntry = Entry(item); 
     var dbMemberEntry = dbEntityEntry.Member(property.Name); 

     // If we're dealing with a Reference or Collection entity, explicitly load the properties if necessary. 
     if (dbMemberEntry is DbReferenceEntry) 
     { 
      if (!dbEntityEntry.Reference(property.Name).IsLoaded) 
      { 
       dbEntityEntry.Reference(property.Name).Load(); 
      } 
     } 
     else if (dbMemberEntry is DbCollectionEntry) 
     { 
      if (!dbEntityEntry.Collection(property.Name).IsLoaded) 
      { 
       dbEntityEntry.Collection(property.Name).Load(); 
      } 
     } 
    } 
} 

अंत में, IgnoreOnUnproxyAttribute चक्र से बचने के लिए इस्तेमाल किया:

private object SafeUnproxy(dynamic item) 
{ 
    // ProxyCreation is off, so any reference or collection properties may not yet be loaded. We need to make sure we explicitly load each property from the db first. 
    ExplicitlyLoadMembers(item); 

    // Figure out the right type to use as the explicit generic type argument 
    var itemType = item.GetType(); 
    Type requiredPocoType = (itemType.Namespace == "System.Data.Entity.DynamicProxies") ? 
                   itemType.BaseType : 
                   itemType; 

    // Call Unproxy using an explicit generic type argument 
    var unproxiedValue = typeof(VerhaalLokaalDbContext).GetMethod("UnProxy").MakeGenericMethod(requiredPocoType).Invoke(this, new[] { item }); 
    return unproxiedValue; 
} 

सुनिश्चित करें कि प्रत्येक संपत्ति डेटाबेस से भरी हुई है बनाना IsLoaded ऑब्जेक्ट के गुणों के माध्यम से पुनरावृत्ति और जाँच का विषय है

[System.AttributeUsage(AttributeTargets.Property | AttributeTargets.Field, Inherited = false, AllowMultiple = false)] 
sealed class IgnoreOnUnproxyAttribute : Attribute 
{   
} 

प्रयोग इस प्रकार है:

MyDbContext db = new MyDbContext(); 

public Story Get(int storyId) 
{ 
    var lazyStory = db.Stories.SingleOrDefault(s => s.Id == storyId); 
    var unproxied = db.UnProxy(lazyStory); 

    return unproxied; 
} 

प्रदर्शन सभी प्रतिबिंबों के चलते शानदार नहीं है, लेकिन निष्पादन समय औसत पर थोड़ा सा है (यानी। एक सेकंड से भी कम) जब आलसी लोडिंग एक इकाई, उसके सभी गुणों के माध्यम से पुनरावृत्ति, और उसके बाद गतिशीलप्रवाह स्वयं serializing। साथ ही, यह शामिल है() जो कि डरावनी धीमी और त्रुटि-प्रवण है, का उपयोग करने से कहीं अधिक तेज़ है।

उम्मीद है कि यह किसी की मदद करेगा।

संबंधित मुद्दे