0

Im Folgenden finden Sie einen sehr kurzen UWP-Komponententest, der versucht, eine Klasse namens Car mit einem DataContractSerializer zu serialisieren und anschließend zu deserialisieren. Ich plane, diese Art von Code in einer UWP-App zu verwenden, um den Sitzungsstatus zu speichern, wenn die App angehalten wird. Da ich nicht jeden Typ der KnownTypes-Auflistung hinzufügen möchte, habe ich einen einfachen benutzerdefinierten DataContractResolver aus einem MSDN-Blog gestohlen. Es sollte funktionieren, wenn Serialisierung und Deserialisierung innerhalb derselben App stattfinden (und somit Typen und Assemblies teilen). Alles funktioniert perfekt, wenn der Code mit dem vollständigen .NET Framework 4.6.2 ausgeführt wird. Aber die WEIRD THING ist, dass der genau gleiche Code fehlschlägt, es ist Teil eines universellen Windows-Projekts, wenn ich auch "Kompiliere mit .NET Native Tool Chain" aktivieren.Probleme mit DataContractSerializer + DataContractResolver auf UWP, .NET Native Problem?

Warum funktioniert der exakt gleiche Code nicht auf einer UWP-App, OHNE die .NET Native-Werkzeugkette zu verwenden? .NET Native sollte Probleme bei der Serialisierung verursachen, daher erscheint es sehr seltsam, dass mein Code nur mit UWP funktioniert, wenn .NET Native verwendet wird. Wie bekomme ich es mit einer UWP-Anwendung ohne Verwendung von .NET Native arbeiten - Kompilierung verlangsamt sich dramatisch in meinen DEBUG-Builds, wenn es aktiviert ist.

Hier ist eine GitHub-Verbindung zu einer vollständigen Lösung mit beiden Komponententests. Hier https://github.com/jmagaram/CustomResolver

ist die Einheit Testcode:

using System; 
using Microsoft.VisualStudio.TestPlatform.UnitTestFramework; 
using System.Runtime.Serialization; 
using System.Xml; 
using System.Reflection; 
using System.Collections.Generic; 
using System.IO; 

namespace ResolverTest { 
    [TestClass] 
    public class SerializerTestUniversal { 
     [TestMethod] 
     public void CanRoundtripComplexTypeWithNoKnownTypesAndCustomResolver() { 
      // prepare object for serialization 
      var car = new Car { Year = 2000, Model = "Ford" }; 
      var rootToSerialize = new Dictionary<string, object> { ["car"] = car }; 

      // serialize with DataContractSerializer and NO known types 
      // hopefully the custom DataContractResolver will make it work 
      var serializer = new DataContractSerializer(
       typeof(Dictionary<string, object>), 
       new DataContractSerializerSettings { DataContractResolver = new SharedTypedResolver() }); 
      var memoryStream = new MemoryStream(); 
      serializer.WriteObject(memoryStream, rootToSerialize); 

      // deserialize 
      memoryStream.Position = 0; 
      var output = (Dictionary<string, object>)(serializer.ReadObject(memoryStream)); 
      var outputCar = (Car)output["car"]; 

      // check that the data got roundtripped correctly 
      Assert.AreEqual(car.Year, outputCar.Year); 
      Assert.AreEqual(car.Model, outputCar.Model); 
     } 

     public class Car { 
      public int Year { get; set; } 
      public string Model { get; set; } 
     } 

     // To be used when serializing and deserializing on same machine with types defined in a shared assembly 
     // Intended to used for suspend/resume serialization in UWP apps 
     // Code from https://blogs.msdn.microsoft.com/youssefm/2009/06/05/configuring-known-types-dynamically-introducing-the-datacontractresolver/ 
     public class SharedTypedResolver : DataContractResolver { 
      public override Type ResolveName(string typeName, string typeNamespace, Type declaredType, DataContractResolver knownTypeResolver) { 
       return knownTypeResolver.ResolveName(typeName, typeNamespace, declaredType, null) ?? Type.GetType($"{typeName}, {typeNamespace}"); 
      } 

      public override bool TryResolveType(Type dataContractType, Type declaredType, DataContractResolver knownTypeResolver, out XmlDictionaryString typeName, out XmlDictionaryString typeNamespace) { 
       if (!knownTypeResolver.TryResolveType(dataContractType, declaredType, null, out typeName, out typeNamespace)) { 
        XmlDictionary dictionary = new XmlDictionary(); 
        typeName = dictionary.Add(dataContractType.FullName); 
        typeNamespace = dictionary.Add(dataContractType.GetTypeInfo().Assembly.FullName); 
       } 
       return true; 
      } 
     } 
    } 
} 

Hier ist die vollständige rd.xml Dateiinhalte auf UWP erforderlich, damit es funktioniert, wenn .NET indiaeingeschaltet ist.

<Directives xmlns="http://schemas.microsoft.com/netfx/2013/01/metadata"> 
    <Application> 
    <Assembly Name="*Application*" Dynamic="Required All" /> 
    <Type Name="ResolverTest.SerializerTestUniversal.Car" Browse="Required Public" DataContractSerializer="Required All"/> 
    </Application> 
</Directives> 

Und schließlich, das ist die Ausnahme, die, wenn .NET india auftritt ausgeschaltet ist:

Result Message: Test method ResolverTest.SerializerTestUniversal.CanRoundtripComplexTypeWithNoKnownTypesAndCustomResolver threw exception: 
System.Runtime.Serialization.SerializationException: Type 'ResolverTest.SerializerTestUniversal+Car' with data contract name 'SerializerTestUniversal.Car:http://schemas.datacontract.org/2004/07/ResolverTest' is not expected. Add any types not known statically to the list of known types - for example, by using the KnownTypeAttribute attribute or by adding them to the list of known types passed to DataContractSerializer. 

== == AKTUALISIERT

ich war in der Lage, es zu bekommen mit einem arbeiten verschiedene DataContractResolver. Siehe den folgenden Code. Ich änderte auch den Test, um eine neue Instanz des DataContractSerializers für die Deserialisierung zu verwenden, da der neue Resolver Status/Information während der Serialisierung aufbaut. Die Kommentare erklären, wie es aussieht, dass der DataContractResolver von UWP und .NET 4.6.2 anders verwendet wird. Ich weiß immer noch nicht, warum der ursprüngliche Code fehlgeschlagen ist, wenn .NET Native nicht aktiviert wurde.

public class SharedTypeResolver : DataContractResolver { 
    Type _mostRecentResolvedType = null; 

    // When an object is serialized using the Universal Windows Platform (as of version 
    // 5.2.2), the ResolveName method is called for each type it encounters immediately after 
    // calling TryResolveType. The Microsoft API specification says the ResolveName method is 
    // used to 'map the specified xsi:type name and namespace to a data contract type during 
    // deserialization', so it is a bit surprising this method is called during 
    // serialization. If ResolveName does not return a valid type during serialization, 
    // serialization fails. This behavior (and the failure) seems to be unique to the UWP. 
    // ResolveName is not called during serialization on the .Net Framework 4.6.2. 
    // 
    // During serialization it is difficult to force ResolveName to return a valid type 
    // because the typeName and typeNamespace do not include the assembly, and 
    // Type.GetType(string) can only find a type if it is in the currently executing assembly 
    // or it if has an assembly-qualified name. Another challenge is that the typeName and 
    // typeNamespace parameters are formatted differently than Type.FullName, so string 
    // parsing is necessary. For example, the typeNamespace parameter looks like 
    // http://schemas.datacontract.org/2004/07/namespace and the typeName parameter is 
    // formatted as className+nestedClassName. Type.FullName returns a single string like 
    // namespace.class+nestedClass. But even worse, generic types show up in ResolveName 
    // during serialization with names like 'StackOfint'. So the HACK approach I've taken 
    // here is to cache the last Type seen in the TryResolveType method. Whenever a 
    // typeNamespace appears in ResolveName that does not look like a real assembly name, 
    // return the cached type. 
    // 
    // During deserialization it is very easy for this method to generate a valid type name because the XML 
    // file that was generated contains the full assembly qualified name. 
    public override Type ResolveName(string typeName, string typeNamespace, Type declaredType, DataContractResolver knownTypeResolver) { 
     if (typeNamespace.StartsWith("http://schemas.datacontract.org")) { 
      // Should only happen on UWP when serializing, since ResolveName is called 
      // immediately after TryResolveType. 
      return _mostRecentResolvedType; 
     } 
     else { 
      // Should happen when deserializing and should work with all types serialized 
      // with thie resolver. 
      string assemblyQualifiedTypeName = $"{typeName}, {typeNamespace}"; 
      return Type.GetType(assemblyQualifiedTypeName); 
     } 
    } 

    public override bool TryResolveType(Type dataContractType, Type declaredType, DataContractResolver knownTypeResolver, out XmlDictionaryString typeName, out XmlDictionaryString typeNamespace) { 
     _mostRecentResolvedType = dataContractType; 
     XmlDictionary dictionary = new XmlDictionary(); 
     typeName = dictionary.Add(dataContractType.FullName); 
     typeNamespace = dictionary.Add(dataContractType.GetTypeInfo().Assembly.FullName); 
     return true; 
    } 
} 

Antwort

0

Dies war aufgrund eines Fehlers in .NETCore 5.2.2. Ich denke, es ist in 5.2.3 behoben. Ein Ingenieur im Team hat mir dabei geholfen. Es schien zu funktionieren, als ich die Beta-Version der Assemblys heruntergeladen habe.

https://github.com/dotnet/corefx/issues/10155

+0

Thx dieses Problem melden und die Details hier teilen :) –