2013-08-22 21 views
20

Mam prosty model danych obejmujący chwasty i rodziny chwastów.Kołowe odwołania uniemożliwiające serializację wykresu obiektu

WeedFamily <-1---*-> Weed (WeedFamily i Weed mają relację jeden do wielu)

Ja próbuje zakończyć mój pierwszy ApiController tak, że można łatwo odzyskać moje dane jako JSON dla angularjs aplikacji. Po uzyskaniu dostępu do adresu URL /WeedAPI/ w mojej aplikacji pojawia się następujący błąd. Jestem pewien, że problem polega na tym, że mam kołowe odwołania między Weed i WeedFamily.

Jak powinien zmienić mój model danych, aby serializacja JSON działała przy zachowaniu dwukierunkowej jakości relacji Weed - WeedFamily?

(tj. Chcę nadal być w stanie zbudować wyrażenia jak następuje:

WeedData.GetFamilies()["mustard"].Weeds.Count 

i

WeedData.GetWeeds()[3].Family.Weeds 

)

Błąd:

<Error> 
    <Message>An error has occurred.</Message> 
    <ExceptionMessage> 
     The 'ObjectContent`1' type failed to serialize the response body for content type 'application/xml; charset=utf-8'. 
    </ExceptionMessage> 
    <ExceptionType>System.InvalidOperationException</ExceptionType> 
    <StackTrace/> 
    <InnerException> 
     <Message>An error has occurred.</Message> 
     <ExceptionMessage> 
      Object graph for type 'WeedCards.Models.WeedFamily' contains cycles and cannot be serialized if reference tracking is disabled. 
     </ExceptionMessage> 
     <ExceptionType> 
      System.Runtime.Serialization.SerializationException 
     </ExceptionType> 
     <StackTrace> 
      at System.Runtime.Serialization.XmlObjectSerializerWriteContext.OnHandleReference(XmlWriterDelegator xmlWriter, Object obj, Boolean canContainCyclicReference) at WriteWeedToXml(XmlWriterDelegator , Object , XmlObjectSerializerWriteContext , ClassDataContract) at System.Runtime.Serialization.ClassDataContract.WriteXmlValue(XmlWriterDelegator xmlWriter, Object obj, XmlObjectSerializerWriteContext context) at System.Runtime.Serialization.XmlObjectSerializerWriteContext.WriteDataContractValue(DataContract dataContract, XmlWriterDelegator xmlWriter, Object obj, RuntimeTypeHandle declaredTypeHandle) at System.Runtime.Serialization.XmlObjectSerializerWriteContext.SerializeWithoutXsiType(DataContract dataContract, XmlWriterDelegator xmlWriter, Object obj, RuntimeTypeHandle declaredTypeHandle) at System.Runtime.Serialization.XmlObjectSerializerWriteContext.InternalSerialize(XmlWriterDelegator xmlWriter, Object obj, Boolean isDecl...etc 
     </StackTrace> 
    </InnerException> 
</Error> 

Moje dane:

public class WeedData 
{ 
    public static Dictionary<string,WeedFamily> GetFamilies(){ 
     return new Dictionary<string,WeedFamily> 
     { 
      {"mustard",new WeedFamily("Mustard","Brassicaceae")} 
      ,{"pigweed",new WeedFamily("Pigweed","Amaranthus")} 
      ,{"sunflower",new WeedFamily("Sunflower","Asteraceae")} 
     }; 
    } 

    public static List<Weed> GetWeeds(){ 
     var Families = GetFamilies(); 
     return new List<Weed> 
     { 
      new Weed("Hairy Bittercress","Cardamine hirsuta",Families["mustard"]) 
      ,new Weed("Little Bittercress","Cardamine oligosperma",Families["mustard"]) 
      ,new Weed("Shepherd's-Purse","Capsella bursa-pastoris",Families["mustard"]) 
      ,new Weed("Wild Mustard","Sinapis arvensis/Brassica kaber",Families["mustard"]) 
      ,new Weed("Wild Radish","Raphanus raphanistrum",Families["mustard"]) 
      ,new Weed("Radish","Raphanus sativus",Families["mustard"]) 
      ,new Weed("Redroot Pigweed","Amaranthus retroflexus",Families["pigweed"]) 
      ,new Weed("Prickly Lettuce","Lactuca serriola",Families["sunflower"]) 
      ,new Weed("Spiny Sowthistle","Sonchus asper",Families["sunflower"]) 
      ,new Weed("Annual Sowthistle","Sonchus oleraceus",Families["sunflower"]) 

     }; 
    } 
} 

moich klas model:

[Serializable] 
public class Weed 
{ 
    public string CommonName; 
    public string LatinName; 
    public List<WeedPhoto> Photos; 
    public WeedFamily Family; 

    public Weed(string commonName, string latinName) 
    { 
     CommonName = commonName; 
     LatinName = latinName; 
    } 

    public Weed(string commonName, string latinName, WeedFamily family) 
    { 
     CommonName = commonName; 
     LatinName = latinName; 
     Family = family; 
     Family.Weeds.Add(this); 
    } 

    override public string ToString() 
    { 
     return CommonName + " (" + LatinName + ")"; 
    } 
} 

i

[Serializable] 
public class WeedFamily 
{ 
    public string CommonName; 
    public string LatinName; 
    public List<Weed> Weeds; 

    public WeedFamily(string commonName, string latinName) 
    { 
     CommonName = commonName; 
     LatinName = latinName; 
     Weeds = new List<Weed>(); 
    } 
} 

Wreszcie ApiController:

public class WeedAPIController : ApiController 
{ 
    // 
    // GET: /WeedAPI/ 

    public IEnumerable<Weed> GetAllWeeds() 
    { 
     return WeedData.GetWeeds(); 
    } 

} 
+1

Czemu nadrzędnymi 'toString()'? Prawdopodobnie po prostu chcesz użyć atrybutu '[DebuggerDisplay]'. – ANeves

Odpowiedz

35

Dodaj [DataContract(IsReference = true)] do obiektów, które mają odnośniki okrągły.

[Serializable] 
[DataContract(IsReference = true)] 
public class WeedFamily 

[Serializable] 
[DataContract(IsReference = true)] 
public class Weed 

Zobacz http://msdn.microsoft.com/en-us/library/vstudio/hh241056(v=vs.100).aspx

+0

Czy muszę importować, aby użyć tego atrybutu? "Nie można znaleźć nazwy typu ani przestrzeni nazw" DataContractAttribute "' –

+1

Tak, jest w przestrzeni nazw 'System.Runtime.Serialization' – Hack

+1

Okazuje się, że muszę dodać" System.Runtime.Serialization.dll "jako odniesienie w moim projekcie VS : http://stackoverflow.com/questions/7401795/namespace-for-datacontract Po tym zrobiłem, że moja serializacja działała poprawnie! Dzięki. –