I am presently working on my first production .NET Execution Environment (DNX) project as described here. In the previous world (e.g. .NET 4.6) I used to create application domains as a matter of routine. In the new DNX world though I have discovered the rules seem to have changed somewhat and I can't quite seem to get a new AppDomain off the ground properly.
I understand from this article that application domains aren't supported for the DNX core runtime, but my project targets plain dnx46 and I believe AppDomains should still work there.
Consider the following steps to reproduce (seemed like unit tests would be an easy way to demo this question):
- Using Visual Studio 2015 Enterprise Update 1 create a unit test project targeting plain .NET 4.6
[TestClass]
public class UnitTest1
{
public void RunIsolated(Action callback)
{
var currentDomainSetupInformation = AppDomain.CurrentDomain.SetupInformation;
AppDomainSetup setup = new AppDomainSetup()
{
ApplicationBase = currentDomainSetupInformation.ApplicationBase ,
ConfigurationFile = currentDomainSetupInformation.ConfigurationFile
};
AppDomain appDomain = AppDomain.CreateDomain(nameof(RunIsolated), AppDomain.CurrentDomain.Evidence, setup);
try
{
appDomain.SetData(nameof(callback), callback);
appDomain.DoCallBack
(
() =>
{
Action callbackAD = (Action)AppDomain.CurrentDomain.GetData(nameof(callback));
callbackAD();
}
);
}
finally
{
AppDomain.Unload(appDomain);
}
}
[TestMethod]
public void TestMethod1()
{
RunIsolated(()=> { });
}
}
Observe that the test 'TestMethod1' passes
Using Visual Studio 2015 Enterprise Update 1 create a DNX XUnit project targeting dnx46, dnx-clr-win-x86.1.0.0-rc1-update1 runtime with XUnit 2.1.0 with xunit.runner" 2.1.0-rc1-build204
project.json looks like this:
{
"version": "1.0.0-*",
"description": "",
"authors": [ "" ],
"tags": [ "" ],
"projectUrl": "",
"licenseUrl": "",
"dependencies": {
"xunit": "2.1.0",
"xunit.runner.dnx": "2.1.0-rc1-build204"
},
"commands": {
"test": "xunit.runner.dnx"
},
"frameworks": {
"dnx46": { }
}
}
The test looks like this:
public class UnitTest1
{
public void RunIsolated(Action callback)
{
var currentDomainSetupInformation = AppDomain.CurrentDomain.SetupInformation;
AppDomainSetup setup = new AppDomainSetup()
{
ApplicationBase = currentDomainSetupInformation.ApplicationBase ,
ConfigurationFile = currentDomainSetupInformation.ConfigurationFile
};
AppDomain appDomain = AppDomain.CreateDomain(nameof(RunIsolated), AppDomain.CurrentDomain.Evidence, setup);
try
{
appDomain.SetData(nameof(callback), callback);
appDomain.DoCallBack
(
() =>
{
Action callbackAD = (Action)AppDomain.CurrentDomain.GetData(nameof(callback));
callbackAD();
}
);
}
finally
{
AppDomain.Unload(appDomain);
}
}
[Fact]
public void TestMethod1()
{
RunIsolated(()=> { });
}
}
- Observe that the test 'TestMethod1' fails with this exception:
Test Name: UnitTest1.TestMethod1
Test FullName: UnitTestProject2.UnitTest1.TestMethod1
Test Source: c:\temp\UnitTestProject2\UnitTestProject2\UnitTest1.cs : line 41
Test Outcome: Failed
Test Duration: 0:00:00.009
Result StackTrace:
at System.AppDomain.SetData(String name, Object data):
at UnitTestProject2.UnitTest1.RunIsolated(Action callback) in c:\temp\UnitTestProject2\UnitTestProject2\UnitTest1.cs:line 23:
at UnitTestProject2.UnitTest1.TestMethod1() in c:\temp\UnitTestProject2\UnitTestProject2\UnitTest1.cs:line 43:
Result Message: Type is not resolved for member 'UnitTestProject2.UnitTest1+<>c,UnitTestProject2, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null'.
This code does create a new domain, so that's working - but it smells funny. For example the type "+<>c", what is that - is the domain trying to serialize an unintended closure or anonymous delegate? Also poking around the failed test with a debugger reveals some interesting facts. For example, the application base for the current domain (on my machine) is C:\Users\me.dnx\runtimes\dnx-clr-win-x86.1.0.0-rc1-update1\bin (of course since that's where dnx runs from). In the old world this would have pointed to the directory where the test assembly was located (e.g. c:\temp\UnitTestProject1\UnitTestProject1\bin\Debug). There are other differences as well but I'm not sure which ones are important.
In short I would be grateful if someone could explain to me what the new best practices are for creating application domains in the .NET Execution Environment dnx46 (not dnx core just plain dnx46).