MEF and AppDomain

Jul 17, 2010 at 3:03 PM

Hi there,


I use MEF to extend the functionality of my application. My application is build on services. Each extensions can consume these services to extend the main application. You can imagine the extensions like an AddIn, Plugin, AddOn or something like that. If an extension now throws an exception or has an error, the whole application crashes. My idea is to use AppDomain and to load all extensions into an seperate AppDomain. If now an extension crashes, the main application will still work. I have searched for an example or information about this topic. But no, I have not found any informations about my problem. :(


Are there any implementations, example or inforamtions? Is this possible? How do I implement my plan? Are there other ways to realize my plan?


int

Jul 17, 2010 at 7:44 PM
Hi int

MEF doesn't do anything with regards to app domains. However, there is some work that folks in the community have done integrating the managed addin framework with MEF. You can find one post on how to do this at Kent Boogaart's blog here: http://kentb.blogspot.com/2009/02/maf-and-mef.html

Thanks
Glenn

From: int [notifications@codeplex.com]
Sent: Saturday, July 17, 2010 8:04 AM
To: Glenn Block
Subject: MEF and AppDomain [MEF:219771]

From: int

Hi there,


I use MEF to extend the functionality of my application. My application is build on services. Each extensions can consume these services to extend the main application. You can imagine the extensions like an AddIn, Plugin, AddOn or something like that. If an extension now throws an exception or has an error, the whole application crashes. My idea is to use AppDomain and to load all extensions into an seperate AppDomain. If now an extension crashes, the main application will still work. I have searched for an example or information about this topic. But no, I have not found any informations about my problem. :(


Are there any implementations, example or inforamtions? Is this possible? How do I implement my plan? Are there other ways to realize my plan?


int

Jul 17, 2010 at 11:46 PM

Hi Glenn,


That is very sad. Are there no workarounds or other approaches? It don't have to be an AppDomain. I'll appreciate other ways to solve my problem, too. I think mixing MEF and MAF together is not really a good idea.


Why is this not considered while developing MEF? I think that this is an really importing point. I know that the main purpose of MEF is not the extensibility and isolation like in MAF. If the isolation with extensions - like in MAF - is possible, then can you please post or release a code snippet? Others who using MEF also and want this functionality too are being probably grateful.


int

Jul 19, 2010 at 3:31 PM

Hi int,

When I used MEF to implement a recomposable UI in Silverlight, I wanted to create functionality that would allow me to deploy DLL updates (via .XAP file), and the UI would recompose itself with the new control versions.  In Silverlight there is no ability to create a new app domain so I found that MEF would recreate the UI using the old assemblies that were loaded into memory.  MEF did trigger the recomposition when the DLLs updates were deployed, which in my opinion means that MEF was working as intended.  What caused the issue where the assembly was loaded. Unfortunately, Silverlight doesn't support creating app domains yet, or I might have a solution for you.

Could you lay out what MEF would need to do to support AppDomains?  Though I'm kind of new to using multiple app domains my thoughts are...

For what your trying to do, it seems like you would want to create a new app domain http://www.west-wind.com/Weblog/posts/601200.aspx, and then load your DLLs into it.  You'll probably need to use some of MEF's catalogs to load the Part info from the DLLs into MEF.  Maybe when an update is triggered from the directory you poll for addins (or other method if applicable) you could load those DLLs into your second AppDomain. 

Here is what I'm wondering about though...

It seems like somehow MEF would have to call AppDomain.CreateInstanceAndUnwrap as it creates instances of the type that might exist in the second app domain instead of loading the assembly into your main app domain.  Can MEF do this, or could it be extended to?  Additionally, what happens if I want a single MEF container to handle assemblies loaded in more than one app domain?

I'm curious about this as well. 

Thanks,
-Nick

Jul 19, 2010 at 3:38 PM

Here is some more interest in this subject: http://mefcontrib.codeplex.com/Thread/View.aspx?ThreadId=211323

Jul 19, 2010 at 10:44 PM

Hi int,

Try this out.  It's just a rough example of how you can accomplish what you're trying to do without any changes to the underlying MEF design.  Notice that I did not use  DirectoryCatalog as they were not correctly routing the CreateInstance call through my new AppDomain for some reason.  When I run this app with "ClassLibrary2.dll" version 1 in "../../AssemblyBin" I can click "SatisfyImportsButton" and then click "TestButton", which results in a message box with "1" in it.  Then I can go replace the "ClassLibrary2.dll" in the "../../AssemblyBin" with version 2.  After clicking "ReloadPlugginsButton" I get a MessageBox with "2".  It would seem that I'm able to handle multiple versions of a pluggin by throwing away the old app domain. 

This example is very rough, but hopefully it will aid you in figuring out what you are trying to do.  If I were to expand upon this, I'd create a "RecomposableDirectoryCatalog" to handle the adding/removing of AssemblyCatalogs.  The "RecomposableDirectoryCatalog" would probably use some polling service to check the last written date on each dll and reload the new version and throw away the appdomain if necessary.  FYI if you ever need to know it looks like "GetInstanceActivating" is where MEF actually calls Activator.CreateInstance.  This page was helpful with the AppDomain pieces: http://markegilbert.wordpress.com/2010/03/25/this-one-not-that-one-appdomains-dynamic-assembly-loading-and-the-assemblyresolve-event/.

'ClassLibrary1 Assembly:
<InheritedExport()>
Public Interface IPlugin
    Function GetNumber() As Integer
End Interface

'ClassLibrary2 Assembly Version 1:
Public Class Plugin
    Implements ClassLibrary1.IPlugin

    Public Function GetNumber() As Integer Implements ClassLibrary1.IPlugin.GetNumber
        Return 1
    End Function
End Class

'ClassLibrary2 Assembly Version 2:
Public Class Plugin
    Implements ClassLibrary1.IPlugin

    Public Function GetNumber() As Integer Implements ClassLibrary1.IPlugin.GetNumber
        Return 2
    End Function
End Class

'WinForms App Exe 1:
Public Class Form1
    Implements System.ComponentModel.Composition.IPartImportsSatisfiedNotification

    <ImportMany(AllowRecomposition:=True)>
    Public Property Pluggins As New List(Of ClassLibrary1.IPlugin)

    Private Sub SatisfyImportsButton_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles SatisfyImportsButton.Click
        CompositionInitializer.Compose(Me)
    End Sub

    Private Sub ReloadPlugginsButton_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles ReloadPlugginsButton.Click
        CompositionInitializer.Recompose()
    End Sub

    Private Sub TestButton_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles TestButton.Click
        MsgBox(Pluggins.First.GetNumber)
    End Sub

    Public Sub OnImportsSatisfied() Implements System.ComponentModel.Composition.IPartImportsSatisfiedNotification.OnImportsSatisfied
    End Sub
End Class

Public Class CompositionInitializer

#Region "Constructors"

#End Region
#Region "Properties"
    Private Const ExtensionsDir As String = "..\..\AssemblyBin"

    Private Shared m_ContainerSyncRoot As New Object
    Private Shared m_Container As CompositionContainer
    Public Shared ReadOnly Property Container As CompositionContainer
        Get
            SyncLock m_ContainerSyncRoot
                If m_Container Is Nothing Then
                    Dim catalog As New AssemblyCatalog(Reflection.Assembly.GetExecutingAssembly())
                    AggregateCatalog.Catalogs.Add(catalog)
                    Recompose()
                    Dim compositionContainer As New CompositionContainer(AggregateCatalog)
                    m_Container = compositionContainer
                End If
                Return m_Container
            End SyncLock
        End Get
    End Property

    Private Shared m_PlugginAppDomainSyncRoot As New Object
    Public Shared Property PlugginAppDomain As AppDomain
    Public Shared Property AggregateCatalog As New AggregateCatalog
    Public Shared Property AssemblyCatalogs As New List(Of AssemblyCatalog)
#End Region
#Region "Methods"
    Public Shared Sub ReloadCompositionAppDomain()
        If PlugginAppDomain IsNot Nothing Then
            RemoveHandler AppDomain.CurrentDomain.AssemblyResolve, AddressOf PlugginAppDomain_AssemblyResolve
            AppDomain.Unload(PlugginAppDomain)
        End If

        Dim name As String = String.Format("MyName_{0}", Guid.NewGuid.ToString)
        Dim setup As New AppDomainSetup With {.ApplicationName = name, .ApplicationBase = Environment.CurrentDirectory}
        PlugginAppDomain = AppDomain.CreateDomain(name, Nothing, setup)
        AddHandler AppDomain.CurrentDomain.AssemblyResolve, AddressOf PlugginAppDomain_AssemblyResolve
    End Sub

    Public Shared Function PlugginAppDomain_AssemblyResolve(ByVal sender As Object, ByVal e As ResolveEventArgs) As Reflection.Assembly
        Dim path As String = String.Empty
        For Each fileInfo As System.IO.FileInfo In GetDlls()
            If e.Name.Contains(fileInfo.Name.Remove(fileInfo.Name.IndexOf(fileInfo.Extension))) Then
                path = fileInfo.FullName
                Exit For
            End If
        Next

        If path = String.Empty Then
            Try
                Dim assembly As Reflection.Assembly = System.Reflection.Assembly.Load(e.Name)
                If assembly IsNot Nothing Then Return assembly
            Catch ex As Exception
                'Ignore
            End Try
        Else
            Dim rawAssembly As Byte() = System.IO.File.ReadAllBytes(path)
            Return Reflection.Assembly.Load(rawAssembly)
        End If
        Return Nothing
    End Function

    Private Shared Function GetDlls() As List(Of System.IO.FileInfo)
        Dim dlls As New List(Of System.IO.FileInfo)
        For Each file As String In My.Computer.FileSystem.GetFiles(ExtensionsDir)
            Dim fileInfo As System.IO.FileInfo = My.Computer.FileSystem.GetFileInfo(file)
            If fileInfo.Extension.ToUpper = ".DLL" Then dlls.Add(fileInfo)
        Next
        Return dlls
    End Function

    Public Shared Sub Recompose()
        For Each catalog As AssemblyCatalog In AssemblyCatalogs
            CompositionInitializer.AggregateCatalog.Catalogs.Remove(catalog)
        Next

        AssemblyCatalogs.Clear()
        CompositionInitializer.ReloadCompositionAppDomain()

        For Each fileInfo As System.IO.FileInfo In GetDlls()
            Dim catalog As New AssemblyCatalog(PlugginAppDomain_AssemblyResolve(Nothing, New ResolveEventArgs(fileInfo.Name)))
            AssemblyCatalogs.Add(catalog)
            CompositionInitializer.AggregateCatalog.Catalogs.Add(catalog)
        Next
    End Sub

    Public Shared Sub Compose(ByVal instance As Object)
        Container.ComposeParts(instance)
    End Sub
#End Region

End Class
-Nick
Jul 20, 2010 at 11:50 AM
Hi Nick do you can translate your code exsample into C#? I have no skills in VB. Or can you upload an executable so that I can look via the .NET Reflector? int
Jul 20, 2010 at 1:35 PM
Edited Jul 20, 2010 at 1:35 PM

Sorry, I do not have a C# version.  You should be able to create 3 VB projects with the code, compile it, and view in reflector.  The comments show which code goes in which assembly.  Another option would be to adapt the algorithm to your needs.   The Recompose and ReloadCompositionAppDomain methods are where most of the work happens.

-Nick

Jul 20, 2010 at 1:47 PM
The easiest way is if you upload or send me your executable(EXE and DLL files). After that I can view the code in the .NET Reflector in C#. Can you upload it, please? :)
Jul 21, 2010 at 10:54 AM

I translated the code to C# and ran it, but unless I am missing something here, the plugin assembly doesn't seem to be loaded in the created AppDomain, but on the main AppDomain instead.

Andrey

// Form1.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows.Forms;

namespace MEFAppDomain
{
    using System.ComponentModel.Composition;

    using Plugin;

    public partial class Form1 : Form
    {
        private List<IPlugin> _Pluggins;

        [ImportMany(AllowRecomposition = true)]
        public List<IPlugin> Pluggins
        {
            get
            {
                return this._Pluggins;
            }

            set
            {
                this._Pluggins = value;
            }
        }

        public Form1()
        {
            InitializeComponent();
        }

        private void SatisfyImportsButton_Click(object sender, EventArgs e)
        {
            CompositionInitializer.Compose(this);
        }

        private void ReloadPluginsButton_Click(object sender, EventArgs e)
        {
            CompositionInitializer.Recompose();
        }

        private void TestButton_Click(object sender, EventArgs e)
        {
            MessageBox.Show(this.Pluggins.First<IPlugin>().GetNumber().ToString(), @"Plugin number", MessageBoxButtons.OK);
        }
    }
}

// CompositionInitializer.cs

namespace MEFAppDomain
{
    using System;
    using System.Collections.Generic;
    using System.ComponentModel.Composition;
    using System.ComponentModel.Composition.Hosting;
    using System.Diagnostics;
    using System.IO;
    using System.Reflection;
    using System.Runtime.CompilerServices;

    public class CompositionInitializer
    {
        // Fields
        private static AggregateCatalog _AggregateCatalog;
        private static List<AssemblyCatalog> _AssemblyCatalogs;
        private static AppDomain _PlugginAppDomain;
        private const string ExtensionsDir = @"..\..\AssemblyBin";
        private static CompositionContainer m_Container;
        private static object m_ContainerSyncRoot = RuntimeHelpers.GetObjectValue(new object());
        private static object m_PlugginAppDomainSyncRoot = RuntimeHelpers.GetObjectValue(new object());

        // Methods
        public static CompositionContainer Container
        {
            get
            {
                lock (m_ContainerSyncRoot)
                {
                    if (m_Container == null)
                    {
                        AssemblyCatalog catalog = new AssemblyCatalog(Assembly.GetExecutingAssembly());
                        AggregateCatalog.Catalogs.Add(catalog);
                        Recompose();
                        CompositionContainer compositionContainer = new CompositionContainer(AggregateCatalog, new ExportProvider[0]);
                        m_Container = compositionContainer;
                    }
                    return m_Container;
                }
            }
        }

        static CompositionInitializer()
        {
            AggregateCatalog = new AggregateCatalog();
            AssemblyCatalogs = new List<AssemblyCatalog>();
        }

        public static AggregateCatalog AggregateCatalog
        {
            get
            {
                return _AggregateCatalog;
            }

            set
            {
                _AggregateCatalog = value;
            }
        }

        public static List<AssemblyCatalog> AssemblyCatalogs
        {
            get
            {
                return _AssemblyCatalogs;
            }

            set
            {
                _AssemblyCatalogs = value;
            }
        }

        public static AppDomain PlugginAppDomain
        {
            get
            {
                return _PlugginAppDomain;
            }

            set
            {
                _PlugginAppDomain = value;
            }
        }

        public static void Compose(object instance)
        {
            Container.ComposeParts(new object[] { RuntimeHelpers.GetObjectValue(instance) });
        }

        public static Assembly PlugginAppDomain_AssemblyResolve(object sender, ResolveEventArgs e)
        {
            string path = string.Empty;
            foreach (FileInfo fileInfo in GetDlls())
            {
                if (e.Name.Contains(fileInfo.Name.Remove(fileInfo.Name.IndexOf(fileInfo.Extension))))
                {
                    path = fileInfo.FullName;
                    break;
                }
            }
            if (path == string.Empty)
            {
                try
                {
                    Assembly assembly = Assembly.Load(e.Name);
                    if (assembly != null)
                    {
                        return assembly;
                    }
                }
                catch (Exception)
                {
                    Debug.WriteLine("Failed to load assembly: ", e.Name);
                }
            }
            else
            {
                return Assembly.Load(File.ReadAllBytes(path));
            }
            return null;
        }

        public static void Recompose()
        {
            foreach (AssemblyCatalog catalog in AssemblyCatalogs)
            {
                AggregateCatalog.Catalogs.Remove(catalog);
            }

            AssemblyCatalogs.Clear();
            ReloadCompositionAppDomain();
            var fileInfos = GetDlls();
            foreach (FileInfo fileInfo in fileInfos)
            {
                AssemblyCatalog catalog = new AssemblyCatalog(PlugginAppDomain_AssemblyResolve(null, new ResolveEventArgs(fileInfo.Name)));
                AssemblyCatalogs.Add(catalog);
                AggregateCatalog.Catalogs.Add(catalog);
            }
        }

        public static void ReloadCompositionAppDomain()
        {
            if (PlugginAppDomain != null)
            {
                AppDomain.CurrentDomain.AssemblyResolve -= PlugginAppDomain_AssemblyResolve;
                AppDomain.Unload(PlugginAppDomain);
            }

            string name = string.Format("MyName_{0}", Guid.NewGuid());
            AppDomainSetup setup = new AppDomainSetup
                {
                    ApplicationName = name, 
                    ApplicationBase = Environment.CurrentDirectory
                };
            PlugginAppDomain = AppDomain.CreateDomain(name, null, setup);
            AppDomain.CurrentDomain.AssemblyResolve += PlugginAppDomain_AssemblyResolve;
        }

        private static List<FileInfo> GetDlls()
        {
            List<FileInfo> dlls = new List<FileInfo>();
            var files = Directory.GetFiles(ExtensionsDir, "*.dll");
            foreach (string file in files)
            {
                FileInfo fileInfo = new FileInfo(file);
                if (fileInfo.Extension.ToUpper() == ".DLL")
                {
                    dlls.Add(fileInfo);
                }
            }

            return dlls;
        }
    }
}

// IPlugin.cs (ClassLibrary1)

namespace Plugin
{
    using System.ComponentModel.Composition;

    [InheritedExport]
    public interface IPlugin
    {
        // Methods
        int GetNumber();
    }
}

// APlugin.cs (ClassLibrary2)

namespace APlugin
{
    using global::Plugin;

    public class Plugin : IPlugin
    {
        public int GetNumber()
        {
            return 2;
        }
    }
}

Jul 23, 2010 at 12:12 AM

Andrey,

 

Your correct.  It seems that it was loading a new version into the main AppDomain each time. 

I'm investigating another solution using an Agent running in the second AppDomain, based on some

suggestions here: http://social.msdn.microsoft.com/Forums/en-US/vblanguage/thread/74d85ae0-b9bf-4e2b-bd83-e6ce0a855e56

As time permits I'll try to post a modified solution soon. Using the bits below I can get the assembly to load in my second AppDomain.

I may replace the Pluggins property with a reference to the Agent, and then add the Pluggins import property to my Agent.

It's still a work in progress though.

-Nick

         Dim name As String = String.Format("MyName_{0}", Guid.NewGuid.ToString)
        Dim evidence As New Security.Policy.Evidence(AppDomain.CurrentDomain.Evidence)

        PlugginAppDomain = AppDomain.CreateDomain(
            name,
            evidence,
            AppDomain.CurrentDomain.BaseDirectory,
            Path.GetDirectoryName(pathvar),
            True)

 

Dim agent As Agent = PlugginAppDomain.CreateInstanceAndUnwrap(GetType(Agent).Assembly.GetName().FullName, GetType(Agent).FullName)
agent.LoadAssemblies()

Public Class Agent
    Inherits MarshalByRefObject

    Sub LoadAssemblies()
        Dim rawAssembly As Byte() = System.IO.File.ReadAllBytes(path)
        Dim result As Reflection.Assembly = Reflection.Assembly.Load(rawAssembly)
    End Sub
End Class

 

Jul 23, 2010 at 10:47 PM
Edited Jul 23, 2010 at 10:48 PM

Hey guys,

Where to start...

It's possible to load the assembly exclusively into the second AppDomain with the method above.  I'm able to spin up a MEF container and compose parts that are marked as Imports on types within that AppDomain.  So if you added the Pluggins property from the initial example to the Agent class, mef could ComposeParts on Agent.  Where I'm running into trouble is either passing an object into the secondary AppDomain and calling ComposeParts on it, or passing composed parts back out of the Secondary AppDomain.  First, an object has to be marked as Serializable in the Secondary AppDomain to go inbetween, which keeps you from being able to do something like have a AssemblyCatalog passed back from the agent class, because it is not serializable (and rightly so due to the type specific info inside).  Second, you get a nice message like this when you try to access a type that isn't available in a loaded assembly in the current AppDomain "+  agent.Pluggins {"Type is not resolved for member 'ClassLibrary2.Plugin,ClassLibrary2, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null'."} System.Runtime.Serialization.SerializationException".

Maybe all you want to do is load pluggins in a seperate AppDomain, and have them execute all on their own with no influence on your main AppDomain.  If so, I've got a solution for you.  Otherwise, if your looking to compose them on a type from your main AppDomain, there are some problems to work through. 

Maybe there is a way to implement a RealProxy as the IPluggin interface type to expose to the main AppDomain and do some remoting to the secondary AppDomain where your pluggins are instantiated.  In addition, maybe there is a way to write a catalog (SecondaryAppDomainAssemblyCatalog) that allows you to compose parts in the secondary AppDomain, and expose them to the main AppDomain as the RealProxy.TransparentProxy type.  This seems like it "could" be possible due to the way I "think" MEF handles type information behind the scenes.

Still not sure, but maybe someone else is interested in this puzzle as well.

-Nick

Jul 27, 2010 at 10:58 PM

Here is a working example that actually loads the Pluggins in a Secondary AppDomain, and allows access to them in the Main AppDomain.  It also supports recomposition.  You will need to use the first Example above, but substitute the Form1 and CompositionInitializer classes with the one below.

Limitations include:
Imports must be simple types, either a single object reference, or an Array of objects.  List, Collection, etc. are not supported.
Importing type must inherit from MarshalByRefObject, and it must be marked as <Serializable>.
Situation where an Export exists within the Main App Domain AND the pluggins directory is not supported. 
A type might have to be composed twice... Once in the Main AppDomain and once in the Secondary AppDomain to ensure exports in both AppDomains are found.

Aside from all the limitations, it should allow you to seperate your pluggins into their own AppDomain, and you can write custom logic to throw the AppDomain away and recompose them if a newer version of a specific dlls becomes available.  You can use the System.IO.FileSystemWatcher to watch for directory updates and throw away / reload the AppDomain when new versions of DLLs become available (Note: If you decide to try this, sometimes the FileSystemWatcher will trigger multiple events for the same file Add or Replace due to antivirus software, which could cause redundant recompositions).

That's all for now.  I plan to continue exploring this issue with the end goal of creating a DirectoryCatalog that can be used in the Main AppDomain, which supports watching a folder for Pluggins and creating them loading their assemblies in a Second AppDomain.  This should eliminate all of the limitations and hide some of the complexity.  I assume that I'll need to implement my own ComposablePartCatalog and ComposablePart in order to take control of part creation and call CreateInstanceAndUnwrap in my new AppDomain.  I'll try to post here when I finish it.

-Nick

Imports System.Activities.Presentation.Toolbox
Imports System.Activities
Imports System.Activities.XamlIntegration
Imports System.Xaml
Imports System.IO
Imports System.CodeDom.Compiler

<Serializable()> _
Public Class Form1

    <ImportMany(AllowRecomposition:=True)>
    Public Property Pluggins As ClassLibrary1.IPlugin()

    Private Sub SatisfyImportsButton_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles SatisfyImportsButton.Click
        CompositionInitializer.Compose(Me)
    End Sub

    Private Sub ReloadPlugginsButton_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles ReloadPlugginsButton.Click
        CompositionInitializer.ReloadPluggins()
    End Sub

    Private Sub TestButton_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles TestButton.Click
        MsgBox(Pluggins.First.GetNumber)
    End Sub

    Private Sub RecomposeButton_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles RecomposeButton.Click
        CompositionInitializer.Recompose()
    End Sub

    Public Class CompositionInitializer

        Private Shared m_PlugginAppDomainSyncRoot As New Object
        Public Shared Property PlugginAppDomain As AppDomain
        Public Shared Property Agent As SecondaryAppDomainAgent
        Private Shared Property ComposedInstances As New List(Of Object)

        Public Shared Sub Recompose()
            Agent.Recompose()
        End Sub

        Public Shared Sub Compose(ByVal instance As Object)
            CreateSecondaryAppDomainAgentIfNecessary()
            Agent.Compose(instance)
            If Not ComposedInstances.Contains(instance) Then
                ComposedInstances.Add(instance)
            End If
        End Sub

        Public Shared Sub ReloadPluggins()
            AppDomain.Unload(PlugginAppDomain)
            PlugginAppDomain = Nothing
            For Each instance As Object In ComposedInstances
                Compose(instance)
            Next
        End Sub

        Private Shared Sub CreateSecondaryAppDomainAgentIfNecessary()
            If PlugginAppDomain Is Nothing Then
                Dim name As String = String.Format("MyName_{0}", Guid.NewGuid.ToString)
                Dim evidence As New Security.Policy.Evidence(AppDomain.CurrentDomain.Evidence)

                PlugginAppDomain = AppDomain.CreateDomain(
                    name,
                    evidence,
                    AppDomain.CurrentDomain.BaseDirectory,
                    Path.GetDirectoryName("AssemblyBin\ClassLibrary2.dll"),
                    True)

                Agent = PlugginAppDomain.CreateInstanceAndUnwrap(
                    GetType(SecondaryAppDomainAgent).Assembly.GetName().FullName,
                    GetType(SecondaryAppDomainAgent).FullName)
            End If
        End Sub
    End Class

    <Serializable()> _
    Public Class SecondaryAppDomainAgent
        Inherits MarshalByRefObject

        Public Sub Compose(ByVal instance As Object)
            Me.Container.ComposeParts(instance)
        End Sub

        Private m_Container As CompositionContainer
        Public ReadOnly Property Container() As CompositionContainer
            Get
                If m_Container Is Nothing Then
                    Dim rawAssembly As Byte() = System.IO.File.ReadAllBytes("AssemblyBin\ClassLibrary2.dll")
                    Dim assembly As Reflection.Assembly = Reflection.Assembly.Load(rawAssembly)
                    Dim assemblyCatalog As New AssemblyCatalog(assembly)
                    Me.AggregateCatalog = New AggregateCatalog(assemblyCatalog)
                    Dim compositionContainer As New CompositionContainer(Me.AggregateCatalog)
                    m_Container = compositionContainer
                End If
                Return m_Container
            End Get
        End Property
        Protected Property AggregateCatalog As AggregateCatalog
        Public Shared Property AssemblyCatalogs As New List(Of AssemblyCatalog)

        Public Sub Recompose()
            Dim rawAssembly As Byte() = System.IO.File.ReadAllBytes("AssemblyBin\ClassLibrary2.dll")
            Dim result As Reflection.Assembly = Reflection.Assembly.Load(rawAssembly)
            Dim catalog As New AssemblyCatalog(result)
            Dim aggCatalog As AggregateCatalog = DirectCast(m_Container.Catalog, AggregateCatalog)
            aggCatalog.Catalogs.Clear()
            aggCatalog.Catalogs.Add(catalog)
        End Sub

    End Class

End Class


Aug 7, 2013 at 5:53 AM
Reviving this...

Here's an article I wrote in regards to MEF and AppDomains.

MEF and AppDomain - Remove Assemblies On The Fly

-John