0

Ниже представлен очень короткий UWP-модуль, который пытается сериализовать и затем десериализовать класс под названием Car с помощью DataContractSerializer. Я планирую использовать этот тип кода в приложении UWP, чтобы сохранить состояние сеанса, когда приложение приостановлено. Поскольку я не хочу добавлять каждый тип в коллекцию KnownTypes, я украл простой пользовательский DataContractResolver из блога msdn; он должен работать, когда сериализация и десериализация происходят в одном приложении (и, таким образом, совместно используют типы и сборки). Все работает отлично, когда код работает с полной .NET Framework 4.6.2. Но WEIRD THING заключается в том, что тот же самый код не работает, он является частью универсального проекта Windows. UNLESS. Я также включаю «Компиляция с .NET-цепочкой инструментальных средств .NET».Проблемы с DataContractSerializer + DataContractResolver в UWP, .NET Основная проблема?

Почему не будет работать тот же самый код в приложении UWP БЕЗ использования основной цепи инструментов .NET? Предполагается, что .NET Native может вызвать осложнения для сериализации, поэтому очень странно, что мой код работает только в UWP, когда используется .NET Native. Как заставить его работать в UWP-приложении БЕЗ использования .NET Native - компиляция резко замедляется в моих сборках DEBUG при ее включении.

Вот ссылка GitHub на полное решение с помощью обоих модульных тестов. https://github.com/jmagaram/CustomResolver

Вот блок кода тест:

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

Вот полное содержание файла rd.xml необходимые на UWP, чтобы заставить его работать, когда .NET Native включен.

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

И, наконец, это исключение, которое возникает, когда .NET Native выключен:

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. 

== ОБНОВЛЕНО ==

я смог заставить его работать с другой DataContractResolver. См. Код ниже. Я также изменил тест, чтобы использовать новый экземпляр DataContractSerializer для десериализации, поскольку новый резольвер формирует состояние/информацию во время сериализации. В комментариях объясняется, как выглядит DataContractResolver по-разному с помощью UWP и .NET 4.6.2. Я до сих пор не знаю, почему исходный код был неудачным, если только .NET Native не был включен.

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

ответ

0

Это связано с ошибкой в ​​.NETCore 5.2.2. Я думаю, что это зафиксировано в 5.2.3. Мне помог инженер в команде. Казалось, это сработало, когда я загрузил бета-версию сборок.

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

+0

Thx сообщить об этой проблеме и поделиться подробностями здесь :) –

 Смежные вопросы

  • Нет связанных вопросов^_^