2013-06-11 1 views
0

Скажем, у меня есть этот простой класс в модуле называется ClassLibrary1:Как я могу десериализовать объект, содержащий DataTable с значениями перечисления, используя DataContractSerializer?

[DataContract] 
public class Class1 
{ 
    [DataMember] 
    private DataTable _data; 

    public void SetData(string key, object value) 
    { 
     _data = new DataTable("SomeName"); 
     _data.Columns.Add("Key", typeof(String)); 
     _data.Columns.Add("Value", value.GetType()); 
     _data.Rows.Add(key, value); 
    } 
} 

и в отдельном модуле, называемом ClassLibrary2, у меня есть следующий класс:

[DataContract] 
public class Configuration 
{ 
    [DataMember] 
    private Class1 _obj; 

    public Configuration() 
    { 
     _obj = new Class1(); 
     _obj.SetData("Key", MyEnum.Value2); 
    } 
} 

Кроме того, модуль ClassLibrary2 определяет перечисление называемый MyEnum, который не помечен как общедоступный, т. е. он является внутренним для модуля (и используется в приведенном выше коде, как вы можете видеть).

Теперь, в моем основном модуле, я не хочу зависеть от ClassLibrary2, но мне нужно загрузить его динамически во время выполнения. То есть, я использую, затем найдите тип, который мне нужно использовать (в моем простом примере я просто ищу тип с именем «Конфигурация») и создаю экземпляр, используя Activator.CreateInstance. Затем я сериализую экземпляр так:

var ser = new DataContractSerializer(config.GetType()); 
var outstream = new FileStream("c:\\test.xml", FileMode.Create); 
ser.WriteObject(outstream, config); 
outstream.Close(); 

Пока все работает. Но когда я пытаюсь десериализации это так:

var instream = new FileStream("c:\\test.xml", FileMode.Open); 
var conf = ser.ReadObject(instream); 

я получаю ArgumentException, с сообщением «Колонка требует действительного типа данных.» Я обнаружил, что это не произойдет, если я либо

  • Определение MyEnum в главном модуле вместо
  • Заменить DataTable с помощью простого String key; Object value
  • использовать что-то другое вместо перечисления MyEnum (например, строка)

Но ни один из этих вариантов не является очень желательным в моем реальном приложении. Первый вариант создаст сильную связь между модулями, которые не должны зависеть друг от друга. Второй вариант возможен, но немного много работы (я уже использую DataTable во многих местах по всему классу и только недавно обнаружил, что мне пришлось выполнять сериализацию), а третий вариант тоже беспорядочен.

Есть ли другие способы, которыми я могу заставить это работать? Я также попытался добавить MyEnum в список известных типов сериализатора, как через конструктор DataContractSerializer, так и добавив [KnownType(typeof(MyEnum))] перед объявлением класса Configuration, но это не помогло.

+0

Вы можете разместить свое объявление Enum? – aiapatag

+0

Уверен: enum MyEnum {Value1, Value2, Value3} –

+0

Вы пытались пометить свое перечисление как '[DataContract]' и каждый член как '[EnumMemberAttribute]'? – aiapatag

ответ

0

Исходный код просто не может найти полное имя сборки ClassLibrary2, которое было записано в файле test.xml автоматически. Ему нужна помощь.

Вы можете обойти это, если вы зацепите AppDomain.AssemblyResolve Event перед десериализации, что-то вроде этого:

AppDomain.CurrentDomain.AssemblyResolve += CurrentDomainAssemblyResolve; 
... 
var instream = new FileStream("test.xml", FileMode.Open); 
var conf = ser.ReadObject(instream); 
... 

private static Assembly CurrentDomainAssemblyResolve(object sender, ResolveEventArgs args) 
{ 
    if (args.Name == "ClassLibrary2, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null") // adapt to your needs of course 
     return Assembly.LoadFrom(@"mypath\ClassLibrary2.dll"); 

    return null; // don't know for this one 
} 
+0

ОК, это сработало, но я не понимаю, почему. У меня уже есть 'Assembly.LoadFrom (@" c: \ ClassLibrary2.dll ");' в самом начале моей программы. Итак, зачем мне нужно снова загружать этот файл в систему? –

+0

Это не работает так, потому что базовый код сериализации вызывает нечто вроде Type.GetType («ClassLibrary2.MyEnum, ClassLibrary2, Version = 1.0.0.0, Culture = neutral, PublicKeyToken = null») (имя типа с квалификацией сборки), но это может работать, только если ClassLibrary2 может быть разрешен автоматически (например, если он находится в пути .EXE). Если вы скопируете ClassLibrary2.dll в пути, он также будет работать. Подробнее здесь: http://blogs.msdn.com/b/suzcook/archive/2003/05/30/using-type-gettype-typename.aspx –

+0

Хорошо, спасибо большое! –