2010-01-22 6 views
13

Я получаю:BinaryFormatter Deserialize дает SerializationException

System.Runtime.Serialization.SerializationException: Не удается найти сборку «myNameSpace, Version = 1.0.0.0, культура = нейтральной, PublicKeyToken = нуль

При попытке десериализовать некоторые данные в другой программе, чем программа, с которой она была сериализована.

После некоторого поиска в Интернете я обнаружил, что, по-видимому, это можно сделать только с помощью общей сборки.

Однако моя база данных заполнена этими сериализованными объектами, и мне нужна служебная программа, чтобы получить их. Есть ли способ переопределить это поведение и просто передать ему тот же самый класс и заставить его десериализовать?


Я уже нашел этот фрагмент, но я не понимаю, как и где я должен его использовать/использовать.

static constructor() { 
     AppDomain.CurrentDomain.AssemblyResolve += new ResolveEventHandler(CurrentDomain_AssemblyResolve); 
    } 

    static Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args) { 
     Assembly ayResult = null; 
     string sShortAssemblyName = args.Name.Split(',')[0]; 
     Assembly[] ayAssemblies = AppDomain.CurrentDomain.GetAssemblies(); 
     foreach (Assembly ayAssembly in ayAssemblies) { 
      if (sShortAssemblyName == ayAssembly.FullName.Split(',')[0]) { 
       ayResult = ayAssembly; 
       break; 
      } 
     } 
     return ayResult; 
    } 
+1

Реферирования оригинальной сборки в вашей утилите приложении не вариант? –

+0

@eric: некоторые только еще не получили ответа ... поэтому я не могу их принять. Но я просмотрю их снова, чтобы узнать, добавлены ли новые ответы/комментарии – Toad

+0

любое окончательное решение с полным образцом исходного кода? – Kiquenet

ответ

6

Вы должны предоставить ссылку на оригинальный тип как-то так, что утилита умеет десериализации его.

Простым способом является только добавление библиотеки DLL, изначально заданной в качестве ссылки на проект утилиты.

Код, который вы опубликовали, позволяет динамически загружать ту же самую DLL, когда десериализатор определяет, что он не может найти тип. Это более сложный подход (но не , что сложно), но в обоих случаях вам понадобится DLL, которая определяет типы ... поэтому, вероятно, проще всего статически связать, добавив ссылку.

Если ваши типы в настоящее время не находятся в DLL (например, если они находятся в EXE), я предлагаю вам вывести классы из EXE в новую DLL и ссылаться на эту DLL как из исходного проекта, так и из использовать проект.

+0

Отлично! Я на самом деле просто ссылался на весь файл .exe, чтобы убедиться, что это сработало, и вот, я мог бы ссылаться на пространство имен и классы. Спасибо за совет! – Toad

13

Вы можете обойти эту проблему, не нуждаясь в DLL, если вы знаете, объект ...

http://spazzarama.com/2009/06/25/binary-deserialize-unable-to-find-assembly/

http://msdn.microsoft.com/en-us/library/system.runtime.serialization.serializationbinder(VS.71).aspx

Используйте класс «System.Runtime.Serialization.SerializationBinder» , К , наследующим от этого класса, можно перенаправить все запросы для типов из двоичного форматирования в зависимости от выбранного вами типа.

Вот пример, который позволит типы можно найти в текущей сборке независимо от того, какой версии сборки изначально было создана сериализованной поток:

sealed class AllowAllAssemblyVersionsDeserializationBinder : System.Runtime.Serialization.SerializationBinder 
{ 
    public override Type BindToType(string assemblyName, string typeName) 
    {  
     String currentAssembly = Assembly.GetExecutingAssembly().FullName; 

     // In this case we are always using the current assembly 
     assemblyName = currentAssembly; 

     // Get the type using the typeName and assemblyName 
     Type typeToDeserialize = Type.GetType(String.Format("{0}, {1}", 
      typeName, assemblyName)); 

     return typeToDeserialize; 
    } 
} 

public static MyRequestObject Deserialize(byte[] b) 
{ 
    MyRequestObject mro = null; 
    var formatter = new System.Runtime.Serialization.Formatters.Binary.BinaryFormatter(); 
    using (var ms = new System.IO.MemoryStream(b)) 
    { 
     // To prevent errors serializing between version number differences (e.g. Version 1 serializes, and Version 2 deserializes) 
     formatter.Binder = new AllowAllAssemblyVersionsDeserializationBinder(); 

     // Allow the exceptions to bubble up 
     // System.ArgumentNullException 
     // System.Runtime.Serialization.SerializationException 
     // System.Security.SecurityException 
     mro = (MyRequestObject)formatter.Deserialize(ms); 
     ms.Close(); 
     return mro; 
    } 
} 
+0

Спасибо! Я начал с этого, но мне пришлось сильно изменить код для моего варианта использования. См. Мой ответ здесь: http://stackoverflow.com/a/25412474/1339280 – shoelzer

+1

Почему 'ms.Close()'? Не поддерживает ли это заявление? –

+0

да ... это определенно избыточно, догадываясь, скопировавшись из неиспользуемого утверждения и никогда не удаляясь – JTtheGeek

1

Если у вас нет доступа к оригинальная сборка, которая сериализовала данные, тогда вы можете использовать SerializationBinder или SerializationSurrogate. Эти два интерфейса позволяют вам контролировать, как типы конвертируются между собой при десериализации.

4

я столкнулся с подобной проблемой, и я получил это работает с помощью следующей ссылки: BinaryFormatterDeserialize-not-finding-a-type

В основном то, что вам нужно сделать, это подписаться на событие AssemblyResolve ПЕРЕД десериализации. Тогда отписать после десериализации ..

AppDomain.CurrentDomain.AssemblyResolve += 
       new ResolveEventHandler(CurrentDomain_AssemblyResolve); 
// CODE TO DESERIALIZE HERE 

AppDomain.CurrentDomain.AssemblyResolve -= new ResolveEventHandler(CurrentDomain_AssemblyResolve); 

Вот метод, который я использовал для решения Ассамблеи:

static Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args) 
{ 
    try 
    { 
     if(args.Name == "MY ASSEMBLY NAME")) 
     { 
      //Load my Assembly 
      Assembly assem = Assembly.LoadFrom("MY ASSEMBLY PATH"); 
      if(assem != null) 
       return assem; 
     } 
    } 
    catch { ;} 

    return Assembly.GetExecutingAssembly(); 
} 
+0

эта техника работала для меня, когда у меня не было доступа к конкретной версии сборки для десериализации, но я знал, что текущая версия была совместима с этим. – Mike

0

я следовал раствор ответил на JTtheGeek и для того, чтобы получить это работает для меня, я должен был добавить следующий раз перед утверждением assemblyName = currentAssembly;:

typeName = "yourNamespace.yourClassName"; 

После этого он работал отлично!

4

JTtheGeek's answer Правильно, что обычай SerializationBinder - это способ справиться с этой проблемой. Однако примерный код, приведенный в этом ответе, недостаточно для моего варианта использования. Мне нужно было что-то, что мог:

  1. ручка несогласованной версия номер и пространств имен.
  2. Посмотрите на все сборки, а не только на текущий.
  3. По-прежнему достаточно быстр, чтобы звонить 100 раз в секунду.

Это то, что я придумал:

using System; 
using System.Collections.Generic; 
using System.Linq; 

namespace Company.Product.Common.Serialize 
{ 
    /// <summary> 
    /// A Serialization Binder that allows inexact matches (version number or namespace). 
    /// </summary> 
    public sealed class AllowInexactMatchSerializationBinder : System.Runtime.Serialization.SerializationBinder 
    { 
     static private Dictionary<string, Type> typeBindings = new Dictionary<string, Type>(); 

     /// <summary> 
     /// When overridden in a derived class, controls the binding of a serialized object to a type. 
     /// </summary> 
     /// <param name="assemblyName">Specifies the <see cref="T:System.Reflection.Assembly" /> name of the serialized object.</param> 
     /// <param name="typeName">Specifies the <see cref="T:System.Type" /> name of the serialized object.</param> 
     /// <returns> 
     /// The type of the object the formatter creates a new instance of. 
     /// </returns> 
     public override Type BindToType(string assemblyName, string typeName) 
     { 
      Type type; 
      var assemblyQualifiedTypeName = String.Format("{0}, {1}", typeName, assemblyName); 

      // use cached result if it exists 
      if (typeBindings.TryGetValue(assemblyQualifiedTypeName, out type)) 
      { 
       return type; 
      } 

      // try the fully qualified name 
      try { type = Type.GetType(assemblyQualifiedTypeName); } 
      catch { type = null; } 

      if (type == null) 
      { 
       // allow any assembly version 
       var assemblyNameWithoutVersion = assemblyName.Remove(assemblyName.IndexOf(',')); 
       var assemblyQualifiedTypeNameWithoutVersion = String.Format("{0}, {1}", typeName, assemblyNameWithoutVersion); 
       try { type = Type.GetType(assemblyQualifiedTypeNameWithoutVersion); } 
       catch { type = null; } 
      } 

      if (type == null) 
      { 
       // check all assemblies for type full name 
       try 
       { 
        type = AppDomain.CurrentDomain.GetAssemblies() 
         .SelectMany(a => a.ExportedTypes) 
         .Where(a => a.FullName == typeName) 
         .FirstOrDefault(); 
       } 
       catch { type = null; } 
      } 

      if (type == null) 
      { 
       // check all assemblies for type name 
       var name = typeName.Split('.').Last(); 
       try 
       { 
        type = AppDomain.CurrentDomain.GetAssemblies() 
         .SelectMany(a => a.ExportedTypes) 
         .Where(a => a.Name == name) 
         .FirstOrDefault(); 
       } 
       catch { type = null; } 
      } 

      typeBindings[assemblyQualifiedTypeName] = type; 
      return type; 
     } 
    } 
} 
Смежные вопросы