[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 нет никаких проблем при передаче в другой домен)
Спасибо...
D>Кто-нибудь может объяснить, почему так происходит и как заставить правильно работать второй конструктор?
Где-то вы пытаетесь передать в другой домен лямбду с замыканием на переменную. Такие делегаты не сериализуются.
Здравствуйте, 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 не используются
Другие наследники этого класса тоже передаются без ошибок, правда, по ссылке (там нужно значения возвращать)
Здравствуйте, mDmitriy, Вы писали:
D>Фактически в другом домене делегат и Dictionary не используются
Если не используются, пометьте их как несериализуемые...
Здравствуйте, mDmitriy, Вы писали:
D>Фактически в другом домене делегат и Dictionary не используются D>Другие наследники этого класса тоже передаются без ошибок, правда, по ссылке (там нужно значения возвращать)
[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 предложил в принципе правильное решение, но если есть возможность — лучше вылечить саму проблему и организовать нормальное взаимодействие между доменами. Ошибка сериализации — только симптом, у вас ещё много чего может вылезти вплоть до нечаянной подгрузки сборок не в тот домен.
Здравствуйте, Nikolay_Ch, Вы писали:
D>>Фактически в другом домене делегат и Dictionary не используются N_C>Если не используются, пометьте их как несериализуемые...
не подходит этот способ — иногда объекты этих типов возвращаются другого домена (в этом случае тоже все работает)
Здравствуйте, Sinix, Вы писали:
S>Вот вам простейший пример:
Действительно, не работает... впрочем, мой класс с Func тоже не работало, а с использованием делегата стало работать S>Ну и вообще идея с передачей логики (не данных) из домена в домен сериализуемым классом... не делают так по куче причин, эта — одна из. S>Обычно создают простенький сериализуемый класс параметров и передают из домена в домен только его, через marshal-by-ref — посредник.
У меня так и было реализовано изначально, потом понадобилась логика проверки данных в исходном домене. S>P.S. Nikolay_Ch предложил в принципе правильное решение, но если есть возможность — лучше вылечить саму проблему и организовать нормальное взаимодействие между доменами. Ошибка сериализации — только симптом, у вас ещё много чего может вылезти вплоть до нечаянной подгрузки сборок не в тот домен.
Я сделал так, как вы предложили — оставил только данные, проверочная логика реализуется через extension, спасибо...
P.S. Но вопрос так и остался открытым — почему с одним конструктором это работало, а с другим нет?
Притом что лямбды и Dictionary в сериализуемом объекте одни и те же
Здравствуйте, mDmitriy, Вы писали:
S>>Обычно создают простенький сериализуемый класс параметров и передают из домена в домен только его, через marshal-by-ref — посредник. D>У меня так и было реализовано изначально, потом понадобилась логика проверки данных в исходном домене.
Так код не будет выполняться в исходном домене.
Если делегат не содержит замыканий, то сборка, которая содержит метод, на который показывает делегат будет подгружена в текущий домен, при вызове делегата код выполнится в нём.
Если содержит — всё обломится на попытке передать делегат в другой домен. Замыкания не сериализуются.
Мы в похожей ситуации делали так: два marshal by ref посредника, один для домена-хоста, другой — для "недоверенного" домена. Первый посредник передаётся второму через параметры конструктора / параметры метода.
Короче, надо сделать пару шагов назад и аккуратно расписать взаимодействие между доменами. Представьте, что вместо доменов у вас два разных компа. Разделение и ограничения должны быть примерно теми же.
D>Я сделал так, как вы предложили — оставил только данные, проверочная логика реализуется через extension, спасибо...
Ок!
D>P.S. Но вопрос так и остался открытым — почему с одним конструктором это работало, а с другим нет? D>Притом что лямбды и Dictionary в сериализуемом объекте одни и те же
Так второй не заполнял словарь, или я не на тот код смотрю?
Здравствуйте, Sinix, Вы писали: S>Так код не будет выполняться в исходном домене. S>Если делегат не содержит замыканий, то сборка, которая содержит метод, на который показывает делегат будет подгружена в текущий домен, при вызове делегата код выполнится в нём.
э-э... ну да, я имел ввиду основной домен приложения S>Если содержит — всё обломится на попытке передать делегат в другой домен. Замыкания не сериализуются.
это я понял, спасибо S>Мы в похожей ситуации делали так: два marshal by ref посредника, один для домена-хоста, другой — для "недоверенного" домена. Первый посредник передаётся второму через параметры конструктора / параметры метода. S>Короче, надо сделать пару шагов назад и аккуратно расписать взаимодействие между доменами. Представьте, что вместо доменов у вас два разных компа. Разделение и ограничения должны быть примерно теми же.
я к этому тоже пришел — два объекта, унаследованных изначально от MarshalByRefObject, вызывает один другой S>Так второй не заполнял словарь, или я не на тот код смотрю?
не на тот — заполненный словарь был изначально в базовом классе (см. мое второе сообщение в ветке)
если бы оно сразу выругалось на лямбду — так и вопросов бы не было, я пошёл бы другим путем и все
а так я выстроил на этой логике (порочной, вы правы) целое дерево классов и все работало, пока не появился другой конструктор