Profile picture Schedule a Meeting
c a n d l a n d . n e t

Using AppDomains to run a process outside of the ASP.NET Context

Dusty Candland | |

There a few reasons why you might want to run a process in an AppDomain.

For example, if you need to be able to unload and update the loaded

assemblies. Or in my case, use an assembly which actively blocks

execution in your currentAppDomain which may be under the ASP.NET context.

In

this simple example the same security evidence and configuration of the

current domain is used. Further the current assembly happens to be the

one we want to execute, this doesn’t need to be the case however. TheAppDomain.CreateDomain method creates a new AppDomain

and returns a reference to it. This will be used to call create the

processing class and unload the domain when it’s no longer needed.

Next an interface is needed (suggested) for the object that is going to be executed. Here it’s IMyProcess

which defines a Process method. The implementation needs to inherit

from MarshalByRefObject. This allows the framework to handle garbage

collection and life cycle management of the object across the two

domains. Also any object passed across the domain should inherit from

MarshalByRefObject. If the process is really long running, some of the

life cycle properties of MarshalByRefObject will need to be changed.

A

context object is used here to pass data to the processing class, this

isn’t explicitly needed but makes for a little cleaner code. It is also

a MarshalByRefObject. TheCreateInstanceAndUnwrap method creates a proxy

to the specified type and Unwraps it making it usable as if the object

was created in the current domain. The full assembly name, the type

name,BindingFlags, args (for the constructor in this case), CultureInfo, and security evidence need to be passed to the CreateInstanceAndUnwrap

method. The returned object needs to be cast to the Interface type,

done with the as keyword here. Now the Process method of theIMyProcess can be called.

Lastly, the created domain should be unloaded using the AppDomain.Unload method, passing the domain reference from the CreateDomain method. This should be in a finally block to make sure the domain is unloaded. Creating AppDomains

is an expensive process and should be done with this consideration in

mind. Below is most likely not the best way to minimize that expense.



using System;
using System.Globalization;
using System.IO;
using System.Reflection;
using System.Security.Policy;

namespace AppDomainSample
{
static class Program
{
[STAThread]
static void Main()
{
Execute(1, "connection...");
Console.ReadKey();
}

public static void Execute(int id, string connectionString)
{
Assembly assembly = Assembly.GetExecutingAssembly();
string assemblyName = assembly.FullName;
string basePath = new Uri(Path.GetDirectoryName(assembly.CodeBase)).LocalPath;

AppDomain currentDomain = AppDomain.CurrentDomain;
Evidence evidence = currentDomain.Evidence;

AppDomainSetup ads = new AppDomainSetup();
ads.ConfigurationFile = currentDomain.SetupInformation.ConfigurationFile;
ads.ApplicationBase = basePath;
ads.PrivateBinPath = basePath;
ads.ShadowCopyFiles = "true";

AppDomain d = AppDomain.CreateDomain("AppDomainSampleName", evidence, ads);
try { MyContext context = new MyContext(id, connectionString);

d.Load(assemblyName);
object[] args = new object[] { context };
IMyProcess r =
d.CreateInstanceAndUnwrap(assemblyName, "AppDomainSample.MyProcessImpl", false,
BindingFlags.CreateInstance, null, args, CultureInfo.CurrentCulture, null, evidence) as IMyProcess; r.Process(); } finally { AppDomain.Unload(d); } } } internal class MyProcessImpl : MarshalByRefObject, IMyProcess
{
private readonly MyContext _context;

public MyProcessImpl(MyContext context)
{
_context = context;
}

public void Process()
{
Console.WriteLine("ConnectionString: {0} from {1} domain.", _context.ConnectionString,
AppDomain.CurrentDomain.FriendlyName);
}
}

internal interface IMyProcess
{
void Process();
}

internal class MyContext : MarshalByRefObject
{
private readonly int _id;
private readonly string _connectionString;

public MyContext(int id, string connectionString)
{
_id = id;
_connectionString = connectionString;
}

public int Id
{
get { return _id; }
}

public string ConnectionString
{
get { return _connectionString; }
}
}
}

Webmentions

These are webmentions via the IndieWeb and webmention.io. Mention this post from your site: