Смесване на MarshalByRefObject и Serializable

Различни източници обясняват това

Когато даден обект произлиза от MarshalByRefObject, препратка към обект ще бъде предадена от един домейн на приложение към друг, а не самия обект. Когато даден обект е маркиран с [Serializable], обектът ще бъде автоматично сериализиран, транспортиран от един домейн на приложение в друг и след това десериализиран, за да се създаде точно копие на обекта във втория домейн на приложение. След това имайте предвид, че докато MarshalByRefObject предава препратка, [Serializable] кара обектът да бъде копиран. [източник]

Проектирам първото си приложение, което използва AppDomains, и се чудя какво се случва, когато поставите препратки към MarshalByRefObjects в сериализируеми обекти, които не имплементират MarshalByRefObject, защото досега не мога да намеря никаква документация по темата.

Например, какво се случва, ако се опитам да върна List<MBR> където MBR : MarshalByRefObject през границата на AppDomain? Получавам ли копие на List<MBR>, където всяко MBR е TransparentProxy спрямо оригиналния обект? И има ли някаква документация за техническите подробности за смесването на двата механизма?


person Qwertie    schedule 07.01.2012    source източник


Отговори (2)


Току-що направих бърз тест с List<MBR> и изглежда работи, както се надявах:

public class MBR : MarshalByRefObject
{
    List<MBR> _list;
    public MBR() { _list = new List<MBR> { this }; }
    public IList<MBR> Test() { return _list; }
    public int X { get; set; }
}

// Later...
var mbr = AppDomainStarter.Start<MBR>(@"C:\Program Files", "test", null, true);
var list = mbr.Test();
list[0].X = 42;
list.Clear();
Debug.WriteLine(string.Format("X={0}, Count={1}", mbr.X, mbr.Test().Count));

Резултатът е X=42, Count=1 и програмата за отстраняване на грешки показва, че List<MBR> съдържа __TransparentProxy. Толкова ясно, че MarshalByRefObject е успешно маршалиран чрез референция в друг обект, който е маршалиран по стойност.

Все пак бих искал да видя документация или технически подробности, ако някой може да ги намери.

За всеки, който е любопитен, написах този удобен пясъчник AppDomainStarter:

/// <summary><see cref="AppDomainStarter.Start"/> starts an AppDomain.</summary>
public static class AppDomainStarter
{
    /// <summary>Creates a type in a new sandbox-friendly AppDomain.</summary>
    /// <typeparam name="T">A trusted type derived MarshalByRefObject to create 
    /// in the new AppDomain. The constructor of this type must catch any 
    /// untrusted exceptions so that no untrusted exception can escape the new 
    /// AppDomain.</typeparam>
    /// <param name="baseFolder">Value to use for AppDomainSetup.ApplicationBase.
    /// The AppDomain will be able to use any assemblies in this folder.</param>
    /// <param name="appDomainName">A friendly name for the AppDomain. MSDN
    /// does not state whether or not the name must be unique.</param>
    /// <param name="constructorArgs">Arguments to send to the constructor of T,
    /// or null to call the default constructor. Do not send arguments of 
    /// untrusted types this way.</param>
    /// <param name="partialTrust">Whether the new AppDomain should run in 
    /// partial-trust mode.</param>
    /// <returns>A remote proxy to an instance of type T. You can call methods 
    /// of T and the calls will be marshalled across the AppDomain boundary.</returns>
    public static T Start<T>(string baseFolder, string appDomainName, 
        object[] constructorArgs, bool partialTrust)
        where T : MarshalByRefObject
    {
        // With help from http://msdn.microsoft.com/en-us/magazine/cc163701.aspx
        AppDomainSetup setup = new AppDomainSetup();
        setup.ApplicationBase = baseFolder;

        AppDomain newDomain;
        if (partialTrust) {
            var permSet = new PermissionSet(PermissionState.None);
            permSet.AddPermission(new SecurityPermission(SecurityPermissionFlag.Execution));
            permSet.AddPermission(new UIPermission(PermissionState.Unrestricted));
            newDomain = AppDomain.CreateDomain(appDomainName, null, setup, permSet);
        } else {
            newDomain = AppDomain.CreateDomain(appDomainName, null, setup);
        }
        return (T)Activator.CreateInstanceFrom(newDomain, 
            typeof(T).Assembly.ManifestModule.FullyQualifiedName, 
            typeof(T).FullName, false,
            0, null, constructorArgs, null, null).Unwrap();
    }
}
person Qwertie    schedule 08.01.2012

Доколкото разбирам, само обектът от най-високо ниво, който е предаден, може да бъде MBR. Във вашия сценарий, тъй като List не е MBR, когато бъде прехвърлен през границата, ще получите сериализирани копия.

Този раздел в документацията на MSDN обяснява това поведение:

MarshalByRefObject е основният клас за обекти, които комуникират през границите на домейна на приложението чрез обмен на съобщения с помощта на прокси. Обектите, които не наследяват от MarshalByRefObject, са имплицитно маршалирани по стойност. Когато отдалечено приложение препраща към маршал по стойност обект, копие на обекта се предава през границите на домейна на приложението.

Така че, тъй като предаваният клас (списък) не е MBR, той ще бъде сериализиран заедно със съдържанието си.

Освен това, въпреки че не е пряко приложимо към въпроса, следното поведение е много важно да се отбележи:

... членовете на обекта не могат да се използват извън домейна на приложението, където са създадени.

person competent_tech    schedule 07.01.2012
comment
Така че, ако MBR не може да се сериализира, ще има изключение от някакъв вид, когато .NET Framework се опита да го сериализира? Какво ще кажете за връщането на MBR [] масиви? System.Array може да се сериализира и не е извлечен от MarshalByRefObject. - person Qwertie; 08.01.2012
comment
re не може да се сериализира: да, ще възникне изключение по време на изпълнение. re MBR масиви: Имате същия проблем като List, тъй като array е класът от най-високо ниво. - person competent_tech; 08.01.2012
comment
Експериментът показва, че MBR без възможност за сериализиране вътре в контейнер с възможност за сериализиране се предава по препратка без изключение. - person Ivan Krivyakov; 31.07.2013