I'm having issues with CreateInstanceAndUnwrap at the moment for some reason (was working prior).
My process is this:
I dynamically generate some code and it loads DLL's from a subdirectory via MEF. These applications then load different pieces (on demand) from those DLL's. I had to update my code to now include an AppDomainSetup that contains the path of the calling assembly.
I create the new AppDomain correctly -- no issues. When I try to run this code:
object runtime = domain.CreateInstanceAndUnwrap(
typeof(CrossDomainApplication).Assembly.FullName,
typeof(CrossDomainApplication).FullName);
I have massive problems -- the runtime (variable above) no longer can cast to CrossDomainApplication or ICrossDomainApplication.
The actual object looks like:
public class CrossDomainApplication : MarshalByRefObject, ICrossDomainApplication
And the interface looks like:
public interface ICrossDomainApplication
{
void Run(CrossDomainApplicationParameters parameters);
}
And the parameters look like:
[Serializable]
public class CrossDomainApplicationParameters : MarshalByRefObject
{
public object FactoryType { get; set; }
public Type ApplicationType { get; set; }
public string ModuleName { get; set; }
public object[] Parameters { get; set; }
}
The native type of runtime appears to be MarshalByRefObject -- and it doesn't like converting to anything else.
Any thoughts on what could be wrong?
EDIT: Here's the error I get when I run it as the following:
ICrossDomainApplication runtime = (ICrossDomainApplication)domain.CreateInstanceAndUnwrap(
typeof(CrossDomainApplication).Assembly.FullName,
typeof(CrossDomainApplication).FullName);
//Exception before reaching here
runtime.Run(parameters);
System.InvalidCastException: Unable to cast transparent proxy to type 'Infrastructure.ICrossDomainApplication'.
Here's what the domain looks like, as I create it:
AppDomain domain = AppDomain.CreateDomain(
Guid.NewGuid().ToString(),
null,
new AppDomainSetup()
{
ApplicationBase = GetPath(),
ConfigurationFile = AppDomain.CurrentDomain.SetupInformation.ConfigurationFile,
ApplicationName = AppDomain.CurrentDomain.SetupInformation.ApplicationName,
LoaderOptimization = LoaderOptimization.MultiDomainHost
});
and GetPath() looks like this:
private string GetPath()
{
Uri path = new Uri(Assembly.GetCallingAssembly().CodeBase);
if (path.IsFile)
{
path = new Uri(path, Path.GetDirectoryName(path.AbsolutePath));
}
return path.LocalPath.Replace("%20", " ");
}
In the desire to help some other poor, poor person out, I finally figured it out after trying SO MANY other combinations.
I had to change a few things... the first of which:
AppDomain domain = AppDomain.CreateDomain(
Guid.NewGuid().ToString(),
AppDomain.CurrentDomain.Evidence,
new AppDomainSetup()
{
ApplicationBase = AppDomain.CurrentDomain.SetupInformation.ApplicationBase,
ConfigurationFile = AppDomain.CurrentDomain.SetupInformation.ConfigurationFile,
LoaderOptimization = LoaderOptimization.MultiDomainHost,
PrivateBinPath = GetPrivateBin(AppDomain.CurrentDomain.SetupInformation.ApplicationBase)
});
PrivateBinPath is the real trick here that enabled everything else to (finally) start working. PrivateBinPath (read the documentation) ABSOLUTELY needs to be a relative path. If you have a subfolder called "assemblies" then the PrivateBinPath should be "assemblies". If you precede it with a \ or an absolute path, it will not work.
By doing this, it caused the AssemblyResolve event to finally fire. I did that with the following (before creating the new, child AppDomain):
AppDomain.CurrentDomain.AssemblyResolve += ResolveAssembly;
and the ResolveAssembly method looks like this:
private Assembly ResolveAssembly(object sender, ResolveEventArgs args)
{
var loadedAssemblies = AppDomain.CurrentDomain.GetAssemblies();
foreach (var assembly in loadedAssemblies)
{
if (assembly.FullName == args.Name)
{
return assembly;
}
}
return null;
}
The method can be written much easier with Linq, but I kept it this way to try and make it somewhat obvious for myself what was being parsed and loaded for debugging purposes.
And, per always, make sure that you disconnect your event (if you're loading an AppDomain at a time):
AppDomain.CurrentDomain.AssemblyResolve -= ResolveAssembly;
That fixed everything. Thank you to everyone who helped!
This is not an answer, just sample code to share
Hmm, could it be something else? This sample, not exactly 1:1 with what you described, works.
#region
using System;
using System.Reflection;
using System.Threading;
#endregion
internal class Program
{
#region Methods
private static void Main(string[] args)
{
// Get and display the friendly name of the default AppDomain.
string callingDomainName = Thread.GetDomain().FriendlyName;
Console.WriteLine(callingDomainName);
// Get and display the full name of the EXE assembly.
string exeAssembly = Assembly.GetEntryAssembly().FullName;
Console.WriteLine(exeAssembly);
// Construct and initialize settings for a second AppDomain.
var ads = new AppDomainSetup
{
ApplicationBase = Environment.CurrentDirectory,
DisallowBindingRedirects = false,
DisallowCodeDownload = true,
ConfigurationFile = AppDomain.CurrentDomain.SetupInformation.ConfigurationFile
};
// Create the second AppDomain.
AppDomain ad2 = AppDomain.CreateDomain("AD #2", null, ads);
// Create an instance of MarshalbyRefType in the second AppDomain.
// A proxy to the object is returned.
var mbrt = (MarshalByRefType)ad2.CreateInstanceAndUnwrap(exeAssembly, typeof(MarshalByRefType).FullName);
// Call a method on the object via the proxy, passing the
// default AppDomain's friendly name in as a parameter.
mbrt.SomeMethod(new MarshalByRefParameter{ModuleName = callingDomainName});
// Unload the second AppDomain. This deletes its object and
// invalidates the proxy object.
AppDomain.Unload(ad2);
try
{
// Call the method again. Note that this time it fails
// because the second AppDomain was unloaded.
mbrt.SomeMethod(new MarshalByRefParameter { ModuleName = callingDomainName });
Console.WriteLine("Successful call.");
}
catch (AppDomainUnloadedException)
{
Console.WriteLine("Failed call; this is expected.");
}
}
#endregion
}
public interface IMarshalByRefTypeInterface
{
void SomeMethod(MarshalByRefParameter parameter);
}
public class MarshalByRefType : MarshalByRefObject, IMarshalByRefTypeInterface
{
// Call this method via a proxy.
#region Public Methods and Operators
public void SomeMethod(MarshalByRefParameter parameter)
{
// Get this AppDomain's settings and display some of them.
AppDomainSetup ads = AppDomain.CurrentDomain.SetupInformation;
Console.WriteLine("AppName={0}, AppBase={1}, ConfigFile={2}", ads.ApplicationName, ads.ApplicationBase, ads.ConfigurationFile);
// Display the name of the calling AppDomain and the name
// of the second domain.
// NOTE: The application's thread has transitioned between
// AppDomains.
Console.WriteLine("Calling from '{0}' to '{1}'.", parameter.ModuleName, Thread.GetDomain().FriendlyName);
}
#endregion
}
[Serializable]
public class MarshalByRefParameter : MarshalByRefObject
{
public string ModuleName { get; set; }
}
But then, I am just grabbing entry assembly, while you have one which gets dynamically compiled. What error and where do you actually get?
Related
Having an issue with akka.net. I need to access an actor which I have already created with a specific name. I can retrieve the actor from IActorContext but I am struggling to access it from the ActorSystem.
I have created a method called GetOrCreateActor which attempts to get the actor using ActorSelection. If it doesn't exist, the catch creates a new actor with the name. If it does exist, I want it to return the reference. However, it never returns from '.Result'. Assuming this could be some sort of deadlocking issue.
public static IActorRef GetOrCreateActor<T>(this ActorSystem actorSystem, string actorPath, string name = null) where T : ActorBase
{
try
{
return actorSystem.ActorSelection(actorPath).ResolveOne(TimeSpan.FromSeconds(1)).Result;
}
catch
{
return actorSystem.ActorOf(actorSystem.DI().Props<T>(), name);
}
}
Edit
I've tried to include a simplified version of the calling code below.
The actor system is created in an IOC container using AutoFac (ExampleActor is the ReceiveActor I am trying to access):
containerBuilder.RegisterAssemblyTypes(typeof(ExampleActor).Assembly).Where(x => x.Name.EndsWith("Actor"));
var lazyContainer = new Lazy<IContainer>(() => containerBuilder.Build());
containerBuilder.Register(c =>
{
var system = ActorSystem.Create("ExampleActorSystem");
new AutoFacDependencyResolver(lazyContainer.Value, system);
return system;
}).As<ActorSystem>().SingleInstance();
return lazyContainer.Value;
ActorSystem is then injected into another class, where I call the GetOrCreateActor method (via the Execute method):
public class ExampleCommand : IExampleCommand
{
private readonly ActorSystem _actorSystem;
public ExampleCommand(ActorSystem actorSystem)
{
_actorSystem = actorSystem;
}
public void Execute()
{
SendMessage();
}
private void SendMessage()
{
string message = new Message();
_actorSystem.GetOrCreateActor<ExampleActor>("akka://ExampleActorSystem/user/ExampleActor", "ExampleActor").Tell(message);
}
}
The above command would be called from a RESTful endpoint
public ExampleGetModule(IExampleCommand exampleCommand)
{
Get["/api/createExample"] = parameters =>
{
exampleCommand.Execute();
};
}
Your deadlocking issue looks more like it has to do with how you're using your container than it does Akka.NET:
var lazyContainer = new Lazy<IContainer>(() => containerBuilder.Build());
containerBuilder.Register(c =>
{
var system = ActorSystem.Create("ExampleActorSystem");
new AutoFacDependencyResolver(lazyContainer.Value, system);
return system;
}).As<ActorSystem>().SingleInstance();
In terms of what can go wrong here, self-referential Lazy<T> types are an infamous source of race-conditions. You should not be calling lazyContainer.Value inside of this registration method if the output of containerBuilder.Build depends on the input of containerBuilder.Register.
Last thing is to use step-through debugging to make sure that your application actually calls into the ResolveOne method here - if you're not getting a timeout exception back then it means that your application is deadlocking on producing the actor system (because of how DI is configured).
I am using Ninject.Extensions.Conventions to add bindings dynamically. The .dll names to load are stored in configuration. If the configuration is incorrect and the .dll can not be loaded it would be good to know about that. Currently any failure to load a .dll is not bubbled up. For example, if I try to load a potato there is no error I can catch:
foreach (var customModule in customModuleConfigs)
{
KeyValuePair<string, KVP> module = customModule;
_kernel.Bind(scanner => scanner
.From(module.Value.Value)
.SelectAllClasses().InheritedFrom<ICamModule>()
.BindAllInterfaces());
// I need to know this failed
_kernel.Bind(scanner => scanner
.From("potato")
.SelectAllClasses().InheritedFrom<ICamModule>()
.BindAllInterfaces());
}
Is there a way to know I have a bad configuration? In the IntelliTrace window I see an exception thrown but caught before it bubbles up.
You'll need to do the loading of the Assembly yourself, then you can control whether there is an exception thrown. Use the
From(params Assembly[] assemblies) overload.
Load the assembly by either using Assembly.LoadFrom() or Assembly.Load.
You could create a wrapper around the AllInterfacesBindingGenerator class and use this to count the generated bindings:
public class CountingInterfaceBindingGenerator : IBindingGenerator
{
private readonly IBindingGenerator innerBindingGenerator;
public CountingInterfaceBindingGenerator()
{
this.innerBindingGenerator =
new AllInterfacesBindingGenerator(new BindableTypeSelector(), new SingleConfigurationBindingCreator());
}
public int Count { get; private set; }
public IEnumerable<IBindingWhenInNamedWithOrOnSyntax<object>> CreateBindings(Type type, IBindingRoot bindingRoot)
{
this.Count++;
return this.innerBindingGenerator.CreateBindings(type, bindingRoot);
}
}
Usage:
var kernel = new StandardKernel();
var bindingGenerator = new CountingInterfaceBindingGenerator();
kernel.Bind(b =>
{
b.From("potato")
.SelectAllClasses()
.InheritedFrom<ICamModule>()
.BindWith(bindingGenerator);
});
if (bindingGenerator.Count == 0)
// whatever
This is probably longer than your current code, but it would allow further customization of the bindings that have been created.
I am trying to run a WPF App from nunit. Since I only can run one App per AppDomain I instantiate a new AppDomain per acceptance test. When I do that, I run into serialization exceptions.
namespace Tests
{
[TestFixture, RequiresSTA, Serializable]
public class ApplicationTests
{
private MainWindow mainWindow;
private bool guiVisible;
private App app;
[TestCase("app domain name for instance of App")]
[TestCase("app domain name for another instance of App")]
public void ApplicationTest(string name)
{
AppDomain appDomain = AppDomain.CreateDomain(name);
//appDomain.ExecuteAssembly(#"C:\Users\bp\Documents\Visual Studio 2013\Projects\WpfApplication1\WpfApplication1\bin\Debug\WpfApplication1.exe");
CrossAppDomainDelegate action = () =>
{
app = new App();
app.InitializeComponent();
app.Dispatcher.BeginInvoke(DispatcherPriority.Loaded, new Action(() => AppOnActivated(null, null)));
app.Run();
};
appDomain.DoCallBack(action);
}
private void AppOnActivated(object sender, EventArgs eventArgs)
{
if (!guiVisible)
{
mainWindow = (MainWindow)Application.Current.MainWindow;
mainWindow.ButtonViewModel = new ButtonViewModel();
mainWindow.ButtonViewModel.Name = "bla";
guiVisible = true;
}
app.Shutdown();
}
}
}
The exception I receive now:
System.Runtime.Serialization.SerializationException : Type is not
resolved for member 'Tests.ApplicationTests,Tests, Version=1.0.0.0,
Culture=neutral, PublicKeyToken=null'.
I made the test class [Serializable] which does not help either.
Help is very much appreciated. I just want to start my WPF application from an NUnit test so that I can write acceptance tests for my application. I keep running into different walls and eventually whatever path I choose seems to lead to a dead end...
Many thanks in advance,
Bas
AppDomains are software-isolated processes in .NET. This means you can't just reference objects belonging to one AppDomain from another. Objects can be either copied by value (serialization) or by reference using MarshalByRefObject. Since WPF's objects are neither of those, you can't move them around AppDomains.
For your your testing purposes, you could use a simpler approach: run everything within the new AppDomain, and use the SetData and GetData methods to transfer data to assert on.
[TestCase("app domain name for instance of App")]
[TestCase("app domain name for another instance of App")]
public void ApplicationTest(string name)
{
AppDomain appDomain = AppDomain.CreateDomain(name,
AppDomain.CurrentDomain.Evidence,
AppDomain.CurrentDomain.SetupInformation);
appDomain.DoCallBack(StartApp);
Assert.IsTrue((bool)appDomain.GetData("GuiVisible"));
AppDomain.Unload(appDomain);
}
// using a static method instead of a lambda makes sure
// you haven't captured anything
private static void StartApp()
{
app = new App();
app.InitializeComponent();
app.Dispatcher.BeginInvoke(DispatcherPriority.Loaded,
new Action(() => AppOnActivated()));
app.Run();
}
private static void AppOnActivated()
{
var mainWindow = (MainWindow)Application.Current.MainWindow;
mainWindow.ButtonViewModel = new ButtonViewModel();
mainWindow.ButtonViewModel.Name = "bla";
AppDomain.CurrentDomain.SetValue("GuiVisible") = true;
app.Shutdown();
}
I want to have a component register other components in the registry as / after it's constructed. Let's say I have the following components:
interface IConfiguration
{
string SourceDirectory { get; }
string TargetDirectory { get; }
// other primitive-typed configuration parameters
}
class FileConfiguration : IConfiguration
{
// read parameters from some config file
}
class SourceDirectoryWrapper
{
public byte[] ReadFile(string filename)
{
// read a file from the source directory
}
public string Directory { get; set; }
}
class TargetDirectoryWrapper
{
public byte[] WriteFile(string filename)
{
// write a file into the source directory
}
public string Directory { get; set; }
}
class DirectoryWrapperFactory
{
public DirectoryWrapperFactory(IConfiguration config)
{
var source = new SourceDirectoryWrapper {
Directory = config.SourceDirectory
};
var target = new TargetDirectoryWrapper {
Directory = config.SourceDirectory
};
}
}
The components FileConfiguration and DirectoryWrapperFactory can be registered as is usual.
However, what I'd like to accomplish is to somehow "outject" the source and target objects created in DirectoryWrapperFactory. The basic idea is that different environments might require different configuration providers. (And even if not, I think it's a good idea to put reading configuration parameters into a separate component.)
I'd also like to have SourceDirectoryWrapper and TargetDirectoryWrapper managed in the IoC container. In my case, mainly for convenience – I have an EventSource implementation that I need everywhere, so I inject it using property autowiring. Every object not in the IoC container needs to have it passed explicitly, which kind of bugs me.
So: is this possible with AutoFac? If so, how? I poked at the lifecycle events but most don't allow access to the registry after an object is built.
I don't quite understand why DirectoryWrapperFactory needs to exist. You could just register SourceDirectoryWrapper and TargetDirectoryWrapper directly as part of normal wireup:
builder.Register(c => new SourceDirectoryWrapper {
Directory = c.Resolve<IConfiguration>().SourceDirectory
});
builder.Register(c => new TargetDirectoryWrapper {
Directory = c.Resolve<IConfiguration>().SourceDirectory
});
using System;
using System.IO;
using System.Reflection;
using System.Text;
using MyApp.Logging;
namespace MyApp.SmsService.Common
{
public class MyAppAppDomain:MarshalByRefObject
{
private readonly AppDomainSetup domaininfo;
private readonly AppDomain appDomain;
public static string libDllPath;
public MyAppAppDomain(string appDomainName) //Constructor
{
//Setup the App Domain Parameters
domaininfo = new AppDomainSetup();
domaininfo.ApplicationBase = AppDomain.CurrentDomain.SetupInformation.ApplicationBase;
domaininfo.DisallowBindingRedirects = false;
domaininfo.DisallowCodeDownload = true;
domaininfo.ConfigurationFile = AppDomain.CurrentDomain.SetupInformation.ConfigurationFile;
// Create the application domain.
appDomain = AppDomain.CreateDomain(appDomainName, null, domaininfo);
//Set the dll path using Static class ref.
//Dependency resolution handler
appDomain.AssemblyResolve += LoadFromLibFolder; /*Exception*/
}
private static Assembly LoadFromLibFolder(object sender, ResolveEventArgs args)
{
if (libDllPath != null)
{
string assemblyPath = Path.Combine(libDllPath, new AssemblyName(args.Name).Name + ".dll");
if (File.Exists(assemblyPath) == false)
{
return null;
}
Assembly assembly = Assembly.LoadFrom(assemblyPath);
//Assembly dependancy resolved.
return assembly;
}
return null;
}
public Object getNewInstanceOf(string fullyQualifiedTypeName)
{
return appDomain.CreateInstanceAndUnwrap(Assembly.GetExecutingAssembly().FullName, fullyQualifiedTypeName);
}
public Type getTypeOf(string fullyQualifiedTypeName)
{
return getNewInstanceOf(fullyQualifiedTypeName).GetType();
}
public void unloadDomain()
{
AppDomain.Unload(appDomain);
}
}
}
The above class is a wrapper that I want to create to setup and teardown an application domain. However, in my webservice I am getting a FileNotFoundException [Failed to load a dll xyz.dll] whenever I am instantiating the object of MyAppAppDomain.
Following is the ex:
MyAppAppDomain.libDllPath = appDllLibPath; //Some directory other than bin.
pluginDomain = new MyAppAppDomain("SmsServicePlugins"); //Throws FileNotFoundException.
When I debug, I see that the line that caused the exception is the one marked above as /exception/, inside the constructor of MyAppAppDomain.
What is going wrong?
EDIT:
I was going through other articles and I read that objects cannot be visible across domains. This can only happen when the object can be serialized in both domains (use of MarshalByRefObject) and then can be accessed through proxies.
Would be of great help if someone can point out the issues in the above code. In the meanwhile I am attempting to learn more on Marshalling and Proxy-ing.
First problem is you are trying to doing everything inside app domain wrapper, it is really two different components. The first component you need is your object that you want to create inside your new app domain which inherits from MarshalByRefObject (or marked as Serializable). The other lives inside your current AppDomain and is used to create the new AppDomain.
Have a look at the msdn example to see what I mean.
However your error is probably more than likely that your path is wrong to your DLL. Could you show the full stack trace to show what line is throwing the exception?