Странное поведение сериализации...
От: mDmitriy Россия  
Дата: 19.05.15 15:43
Оценка:
Всем привет!

Есть такой класс (.NET 4.5.1, VS 2013):
    [Serializable]
    public class AttributesRequestPlus : AttributesBase
    {
        public RegistrationConfig RegistrationConfig;

        public AttributesRequestPlus(string[] requestDlls, RegistrationConfig registrationConfig) : base()
        {
            ResponseDlls = requestDlls;
            RegistrationConfig = registrationConfig;
        }

        public AttributesRequestPlus(string requestDll, AttributesResponsePlus attributesResponseDll)
            : this(attributesResponseDll.ResponseDlls, new RegistrationConfig())
        {
            //ResponseDlls = attributesResponseDll.ResponseDlls;

            //RegistrationConfig = new RegistrationConfig();
            RegistrationConfig.InstallationFlags = InstallationFlags.Default | InstallationFlags.FindOrCreateTargetApplication | InstallationFlags.ReportWarningsToConsole;

            RegistrationConfig.AssemblyFile = requestDll;
            RegistrationConfig.ApplicationRootDirectory = Path.GetDirectoryName(requestDll); // устанавливаем каталог компонента как путь dll
            RegistrationConfig.Application = attributesResponseDll.ApplicationName;

            Errors.Add(() => string.IsNullOrEmpty(requestDll), "not request dll");
        }
    }

Объект этого класса передается в другой домен (для чего класс указан как сериализуемый) следующим методом:
        public static bool Create(AttributesRequestPlus attributesPlus, bool mode)
        {
            return Create<OtherDomainRegPlus>(r => r.SetRequests(attributesPlus), r => r.DoWork(mode));
        }

Беда в том, что объект, созданный с помощью первого конструктора, работает без проблем, а созданный с помощью второго — вылетает по следующей ошибке:

An unhandled exception of type 'System.Runtime.Serialization.SerializationException' occurred in Register.Common.ComPlus.dll

Additional information: Тип "Common.Registrations.AttributesRequestPlus+<>c__DisplayClass2" сборки " <тут полное имя сборки> " не помечен как сериализуемый.

Кто-нибудь может объяснить, почему так происходит и как заставить правильно работать второй конструктор?
(с объектами других наследников класса AttributesBase нет никаких проблем при передаче в другой домен)
Спасибо...
Re: Странное поведение сериализации...
От: Sinix  
Дата: 19.05.15 16:49
Оценка:
Здравствуйте, mDmitriy, Вы писали:


D>Кто-нибудь может объяснить, почему так происходит и как заставить правильно работать второй конструктор?

Где-то вы пытаетесь передать в другой домен лямбду с замыканием на переменную. Такие делегаты не сериализуются.
Re[2]: Странное поведение сериализации...
От: mDmitriy Россия  
Дата: 20.05.15 05:59
Оценка:
Здравствуйте, Sinix, Вы писали:
S>Где-то вы пытаетесь передать в другой домен лямбду с замыканием на переменную. Такие делегаты не сериализуются.
Ну вот базовый класс:
    [Serializable]
    public abstract class AttributesBase
    {
        public delegate bool Check();
        protected internal readonly Dictionary<Check, string> Errors = new Dictionary<Check, string>();

        public string[] ResponseDlls;

        public AttributesBase()
        {
            Errors.Add(() => ResponseDlls == null, "response list not initialize");
            Errors.Add(() => ResponseDlls.Length == 0, "not response dll's");
        }
    // ...
    }

Если наследника создавать первым его (наследника) конструктором, то все прекрасно передается...
Фактически в другом домене делегат и Dictionary не используются
Другие наследники этого класса тоже передаются без ошибок, правда, по ссылке (там нужно значения возвращать)
Re[3]: Странное поведение сериализации...
От: Nikolay_Ch Россия  
Дата: 20.05.15 06:12
Оценка: 2 (1) +1
Здравствуйте, mDmitriy, Вы писали:

D>Фактически в другом домене делегат и Dictionary не используются

Если не используются, пометьте их как несериализуемые...
Re[3]: Странное поведение сериализации...
От: Sinix  
Дата: 20.05.15 06:24
Оценка: 3 (1) +1
Здравствуйте, mDmitriy, Вы писали:

D>Фактически в другом домене делегат и Dictionary не используются

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

Проблема с Errors,
            Errors.Add(() => string.IsNullOrEmpty(requestDll), "not request dll");


Вот вам простейший пример:
    [Serializable]
    class A { public Action Callback; }

    static void Main()
    {
        var word = "Hello!";
        var a = new A { Callback = () => Console.WriteLine(word) }; //  Console.WriteLine("abc") // (1)

        using (var ms = new MemoryStream())
        {
            var f = new BinaryFormatter();
            f.Serialize(ms, a);
            ms.Seek(0, SeekOrigin.Begin);
            var b = (A)f.Deserialize(ms);
            Console.WriteLine(Equals(a.Callback, b.Callback));
        }

        Console.WriteLine("Done.");
        Console.ReadKey();
    }

Если избавитесь от замыкания (поменяете код лямбды на (1)) — всё будет ок.
Ну и вообще идея с передачей логики (не данных) из домена в домен сериализуемым классом... не делают так по куче причин, эта — одна из.

Обычно создают простенький сериализуемый класс параметров и передают из домена в домен только его, через marshal-by-ref — посредник.

P.S. Nikolay_Ch предложил в принципе правильное решение, но если есть возможность — лучше вылечить саму проблему и организовать нормальное взаимодействие между доменами. Ошибка сериализации — только симптом, у вас ещё много чего может вылезти вплоть до нечаянной подгрузки сборок не в тот домен.
Re[4]: Странное поведение сериализации...
От: mDmitriy Россия  
Дата: 20.05.15 10:40
Оценка:
Здравствуйте, Nikolay_Ch, Вы писали:

D>>Фактически в другом домене делегат и Dictionary не используются

N_C>Если не используются, пометьте их как несериализуемые...
не подходит этот способ — иногда объекты этих типов возвращаются другого домена (в этом случае тоже все работает)
Re[4]: Странное поведение сериализации...
От: mDmitriy Россия  
Дата: 20.05.15 12:09
Оценка:
Здравствуйте, Sinix, Вы писали:

S>Вот вам простейший пример:

Действительно, не работает... впрочем, мой класс с Func тоже не работало, а с использованием делегата стало работать
S>Ну и вообще идея с передачей логики (не данных) из домена в домен сериализуемым классом... не делают так по куче причин, эта — одна из.
S>Обычно создают простенький сериализуемый класс параметров и передают из домена в домен только его, через marshal-by-ref — посредник.
У меня так и было реализовано изначально, потом понадобилась логика проверки данных в исходном домене.
S>P.S. Nikolay_Ch предложил в принципе правильное решение, но если есть возможность — лучше вылечить саму проблему и организовать нормальное взаимодействие между доменами. Ошибка сериализации — только симптом, у вас ещё много чего может вылезти вплоть до нечаянной подгрузки сборок не в тот домен.
Я сделал так, как вы предложили — оставил только данные, проверочная логика реализуется через extension, спасибо...

P.S. Но вопрос так и остался открытым — почему с одним конструктором это работало, а с другим нет?
Притом что лямбды и Dictionary в сериализуемом объекте одни и те же
Re[5]: Странное поведение сериализации...
От: Sinix  
Дата: 20.05.15 12:26
Оценка:
Здравствуйте, mDmitriy, Вы писали:

S>>Обычно создают простенький сериализуемый класс параметров и передают из домена в домен только его, через marshal-by-ref — посредник.

D>У меня так и было реализовано изначально, потом понадобилась логика проверки данных в исходном домене.
Так код не будет выполняться в исходном домене.
Если делегат не содержит замыканий, то сборка, которая содержит метод, на который показывает делегат будет подгружена в текущий домен, при вызове делегата код выполнится в нём.

Если содержит — всё обломится на попытке передать делегат в другой домен. Замыкания не сериализуются.

Мы в похожей ситуации делали так: два marshal by ref посредника, один для домена-хоста, другой — для "недоверенного" домена. Первый посредник передаётся второму через параметры конструктора / параметры метода.
Короче, надо сделать пару шагов назад и аккуратно расписать взаимодействие между доменами. Представьте, что вместо доменов у вас два разных компа. Разделение и ограничения должны быть примерно теми же.


D>Я сделал так, как вы предложили — оставил только данные, проверочная логика реализуется через extension, спасибо...

Ок!


D>P.S. Но вопрос так и остался открытым — почему с одним конструктором это работало, а с другим нет?

D>Притом что лямбды и Dictionary в сериализуемом объекте одни и те же
Так второй не заполнял словарь, или я не на тот код смотрю?
Re[6]: Странное поведение сериализации...
От: mDmitriy Россия  
Дата: 20.05.15 12:43
Оценка:
Здравствуйте, Sinix, Вы писали:
S>Так код не будет выполняться в исходном домене.
S>Если делегат не содержит замыканий, то сборка, которая содержит метод, на который показывает делегат будет подгружена в текущий домен, при вызове делегата код выполнится в нём.
э-э... ну да, я имел ввиду основной домен приложения
S>Если содержит — всё обломится на попытке передать делегат в другой домен. Замыкания не сериализуются.
это я понял, спасибо
S>Мы в похожей ситуации делали так: два marshal by ref посредника, один для домена-хоста, другой — для "недоверенного" домена. Первый посредник передаётся второму через параметры конструктора / параметры метода.
S>Короче, надо сделать пару шагов назад и аккуратно расписать взаимодействие между доменами. Представьте, что вместо доменов у вас два разных компа. Разделение и ограничения должны быть примерно теми же.
я к этому тоже пришел — два объекта, унаследованных изначально от MarshalByRefObject, вызывает один другой
S>Так второй не заполнял словарь, или я не на тот код смотрю?
не на тот — заполненный словарь был изначально в базовом классе (см. мое второе сообщение в ветке)
если бы оно сразу выругалось на лямбду — так и вопросов бы не было, я пошёл бы другим путем и все
а так я выстроил на этой логике (порочной, вы правы) целое дерево классов и все работало, пока не появился другой конструктор
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.