Ошибка десериализации объекта, имеющего поле типа, объявленное в другой сборке, загруженной в AssemblyResolve - PullRequest
2 голосов
/ 19 мая 2011

У меня есть приложение, которое внедряет (через BuildAction: Embedded Resource) ссылочную сборку (называемую ClassLibrary1) внутри себя и загружает его по событию AppDomain.CurrentDomain.AssemblyResolve. Основная сборка определяет класс Class1:

public class Class1
{        
    public Class2 MyField { get; set; }    
}

У него есть свойство типа Class2, определенное в ClassLibrary1. Определение класса 2:

public class Class2
{
    public int A { get; set; }
}

В основном методе я создаю новый XmlSerializer (typeof (Class1)):

    static void Main()
    {
        SubscribeAssemblyResolver();
        MainMethod();
    }

    private static void MainMethod()
    {
        XmlSerializer xs2 = new XmlSerializer(typeof(Class1));
        Class1 cl = new Class1();
    }

При выполнении программы я получаю следующую ошибку:

Невозможно создать временный класс (результат = 1). ошибка CS0012: тип 'ClassLibrary1.Class2' определен в сборке, на которую нет ссылок. Необходимо добавить ссылку на сборку «ClassLibrary1, версия = 1.0.0.0, культура = нейтральная, PublicKeyToken = c06f123f2868e8c8». Ошибка CS0266: невозможно неявное преобразование типа «объект» в «ClassLibrary1.Class2». Существует явное преобразование (вам не хватает приведения?)

Есть идеи?

Остальной код:

    private static void SubscribeAssemblyResolver()
    {
        AppDomain.CurrentDomain.AssemblyResolve += new ResolveEventHandler(CurrentDomain_AssemblyResolve);            
    }

    static Dictionary<String, Assembly> _assemblies = new Dictionary<String, Assembly>(StringComparer.OrdinalIgnoreCase);

    static System.Reflection.Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args)
    {
        return ResolveAssembly(args.Name);
    }

    private static Assembly ResolveAssembly(string argsName)
    {
        Assembly dll;
        var name = "WindowsFormsApplication1.Libs." + new AssemblyName(argsName).Name + ".dll";
        if (!_assemblies.TryGetValue(name, out dll))
        {
            Assembly res = typeof(Program).Assembly;
            using (var input = res.GetManifestResourceStream(name))
            {
                if (input == null)
                {
                    //TODO: log
                    return null;
                }
                Byte[] assemblyData = new Byte[input.Length];
                input.Read(assemblyData, 0, assemblyData.Length);
                if (null == (dll = Assembly.Load(assemblyData)))
                {
                    //TODO: log
                    return null;
                }
                //TODO: log
                _assemblies[name] = dll;
                return dll;
            }
        }
        return dll;
    }

ОБНОВЛЕНИЕ: Создано BUG на сайте Microsoft Connect. Вы также можете загрузить пример решения Visual Stuido 2010 (просто разверните группу полей Подробности), чтобы воспроизвести его.

Ответы [ 4 ]

0 голосов
/ 23 января 2014

Я бы попробовал конструктор XmlSerializer (Type, Type []) и предоставил Class2 в качестве дополнительного типа, используя второй параметр. У меня мало опыта работы с XmlSerializer, но для DataContractSerializer это помогает.

0 голосов
/ 19 мая 2011

На данный момент у меня есть два несколько плохих решения:

  1. Хотя вы не можете создать экземпляр XmlSerializer для типа Class1, вы все равно можете создать его длятип Class2 из основной сборки.Это означает, что если вы переместите Class1 в ClassLibrary1 или Class2 в основную сборку - он будет десериализован без ошибок.Это работает, но не возможно использовать это решение везде, плюс это идеологически неправильно.
  2. Используйте ILMerge , чтобы объединить эти сборки в одну.Но это работает только для не-wpf, плюс вы должны управлять ситуацией с атрибутами сборок (возможны конфликты).

И одна очень плохая идея:

  1. Создайте ClassLibrary1.XmlSerializer.dll с помощью sgen.exe.
  2. Также вставьте его в основную сборку.
  3. Явно загрузите его в кэш XmlSerializer, вызвав один из его внутренних методов через отражение.

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

0 голосов
/ 26 апреля 2012

Я решил похожую проблему, сохранив сборку во временной папке

    public static byte[] ReadFully(Stream input)
    {
        var buffer = new byte[16 * 1024];
        using (var ms = new MemoryStream())
        {
            int read;
            while ((read = input.Read(buffer, 0, buffer.Length)) > 0)
            {
                ms.Write(buffer, 0, read);
            }
            return ms.ToArray();
        }
    }

    public App()
    {
        AppDomain.CurrentDomain.AssemblyResolve += (sender, args) =>
        {
            var assemblyName = new AssemblyName(args.Name);

            if (assemblyName.Name != "Omikad.Core")
                return null;

            var resourceName = "Terem." + assemblyName.Name + ".dll";

            using (var stream = Assembly.GetExecutingAssembly().GetManifestResourceStream(resourceName))
            {
                if (stream == null)
                    return null;

                var assemblyData = ReadFully(stream);
                var tmp = Path.Combine(Path.GetTempPath(), "Omikad.Core.dll");
                File.WriteAllBytes(tmp, assemblyData);
                return Assembly.LoadFrom(tmp);
            }
        };
    }
0 голосов
/ 19 мая 2011

Попробуйте добавить атрибут:

[XmlInclude(typeof(Class2))]
public class Class1
{        
   public Class2 MyField { get; set; }    
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...