2011-01-09 55 views
18

我正在使用實體框架4和POCO模板。EF4將DynamicProxy轉換爲基礎對象

我有一個列表,其中MyObject是動態代理。我想使用XmlSerializer來序列化這個列表,但我不希望它們序列化爲DynamicProxies,而是作爲底層POCO對象。

我知道ContextOptions.ProxyCreationEnabled,但我不想使用它。我只是想知道如何將一個代理對象轉換爲底層POCO來序列化。

+1

如果您使用的是WCF服務,本文應該可以幫助您:http://msdn.microsoft.com/en-us/library/ee705457.aspx – Devart 2011-01-10 13:10:18

+0

答案是[here](http://stackoverflow.com/問題/ 25770369/GET-底層實體對象,從實體框架代理/ 25774651#25774651)。 – 2014-09-17 13:41:14

回答

1

由於您不想關閉ProxyCreation,無論您將對象屬性的虛擬關鍵字(EF上下文繼承對象並用DynamicProxy對象替換虛擬屬性),都會卡住DynamicProxy對象。這些DynamicProxy對象不會從您的POCO實體繼承,它們只具有相同的屬性,可以用來代替您的POCO。如果你真的必須轉換爲POCO對象(我不相信有人會想出一個方法來施放它),你可以試着通過編寫拷貝構造函數來解決這個問題,該構造函數將從傳遞的參數中拷貝所有的屬性(不是很聰明從性能的角度來看,但你必須做的,你必須做的),或者在包含動態代理而不是poco的父對象中使用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

成員是否連接到DbContext? – 2012-05-20 07:56:45

0

我在EF 5.面臨着同樣的問題,我想我的序列化實體對象到XML:因爲它是那樣簡單。 @Koreyam的回答給了我一個提示。我開發了一點點。 某處在我的代碼我打電話這樣

string objXML = EntitySerializer.Serialize(entity); 

Serialize方法序列化是通用的。所以方法的標題是這樣的:

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來獲得原始實體類型的代理代表在:

1

免責聲明:我創建了一個有些通用的解決了這個問題。在尋找解決方案時,我發現了這個老問題,所以我想我會在這裏分享我的解決方案,以幫助任何可能在同一問題上爲他或她的腳趾頭髮茬的人。

我遇到了同樣的問題:我需要從Entity Framework中獲取一些東西,然後使用ASP.NET Web Api將其序列化爲XML。我試過禁用延遲加載和代理創建並使用Include(),但除了最基本的類層次結構之外,導致需要花費幾分鐘才能執行的巨大SQL查詢。我發現使用延遲加載和遞歸引用每個屬性的速度要比一次加載樹快很多倍,所以我想我需要一種方式來延遲加載所有內容,以POCO的形式獲取它,然後序列化它。

我已經使用Gert Arnold的this answer作爲此解決方案的基礎,然後從那裏開始工作。

我在DBContext中創建了一個Unproxy方法,它需要一個(代理的)類實例(例如,您將從DbContext.Find(id)返回的實例),並將該實體作爲實際的POCO類型返回每個屬性,子屬性等完全加載並準備好序列化。

的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 <>與正確的類型參數),遍歷所有值,unproxy每個值並將其添加到新的HashSet,然後將其用作POCO屬性的值。

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.   
} 

請注意,我打電話給SafeUnproxy而不是Unproxy。這是因爲類型推斷存在一個奇怪的問題。通常,當您將代理對象傳遞給Unproxy()時,類型推斷會推斷出T是您實際需要的POCO類型,而不是數據類型的類型(看起來像YourModelPocoType_D0339E043A5559D04303M3033等)。但是,有時它確實推斷 T作爲dataproxy類型,炸燬

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

線,因爲POCO對象不能被強制轉換爲代理類型,導致作爲操作員返回null。爲了解決這個問題,SafeUnproxy使用顯式類型參數調用Unproxy方法,而不是依賴推理:它檢查傳遞它的參數的類型,如果命名空間是System.Data.Entity.DynamicProxies,它將使用類型的BaseType(在dynamicproxy類型的情況下是相應的POCO類型)作爲泛型類型參數。

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:

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用於避免循環:

[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; 
} 

由於所有的反射都在進行,所以性能並不出衆,但執行時間平均只有輕微的變化。小於一秒),比延遲加載實體,遍歷其所有屬性,然後序列化dynamicproxy本身更長。此外,它比使用Include()的速度快得多,速度極慢並且容易出錯。

希望它有助於某人。