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.
Related
I need to run some code in a new app domain. So I am trying to create an instance of my object in the new app domain... Here te code I am using:
public static class Program
{
private static ITemplateEngineProvider _templateEngineProvider;
static Program()
{
AppDomain ad = AppDomain.CreateDomain("New domain");
ObjectHandle handle = ad.CreateInstance(
assemblyName: typeof(RazorTemplateEngineProvider).Assembly.FullName,
typeName: "RazorTemplateEngineProvider"
//ignoreCase: false,
//bindingAttr: BindingFlags.CreateInstance,
//binder: null,
//args: new object[] { new string[] { templatePath, layoutPath } },
//culture: CultureInfo.InvariantCulture,
//activationAttributes: null
);
_templateEngineProvider = (RazorTemplateEngineProvider)handle.Unwrap();
}
}
RazorTemplateEngineProvider is a custom public class that has a public constructor. It has been implemented in a class library (MyCustomLib.dll) I referenced inside the azure function. The class implements an interfaces defined in another class library (IMyCustomLib.dll) referenced only by the previous class library, not by azure function.
At the moment there is no code inside the RazorTemplateEngineProvider class:
public class RazorTemplateEngineProvider : MarshalByRefObject, ITemplateEngineProvider
{
public RazorTemplateEngineProvider()
{ }
}
When I try to do ad.CreateInstance a FileNotFoundException has been thrown:
Could not load file or assembly 'MyCustomLib.dll, Version=1.0.0.0, Culture=neutral, PublicKeyToken=...' or one of its dependencies. The system cannot find the file specified.
But the file exists and it should be already correctly loaded... Infact if I run this 'query'
IEnumerable<string> loadedAssemblies = AppDomain.CurrentDomain.GetAssemblies()
.Where(a => !a.IsDynamic && !a.FullName.Contains("Version=0.0.0.0") && File.Exists(a.Location) && !a.Location.Contains("CompiledRazorTemplates.Dynamic") && a.FullName.Contains("My"))
.Select(f => f.FullName)
.ToArray();
I see both my dll. So, why do I get that error?
Thank you
UPDATE
I think problem is azure because I copied and pasted my code in a console application and thereit works.
UPDATE
I am watching the fusionlong and it seem it is trying to load the assembly from a "wrong" path: file:///C:/Users/csimb/AppData/Local/Azure.Functions.Cli/1.0.12/MyCustomLib.dll.. I expected the path was the bin folder...
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).
This is my first AutoMapper project and may be obvious to some but the tutorials and examples are not clicking with me. I am trying to understand where and to a certain degree how to register(I think I want profiles) my maps for use. There are plenty of MVC examples saying to use the global asax and this makes sense but what is the equivalent in a library project?
In my sandbox I have a winform app and a core library. The winform app calls methods made available by the library and it is one of these library methods that makes use of automapper.
So for some background here is my map:
(and to be clear the mapping is in the SAME core library project)
public class Raw_Full_Map
{
public Raw_Full_Map()
{
Mapper.CreateMap<IEnumerable<RawData>, FullData>()
.ForMember(d => d.Acres, m => m.ResolveUsing(new RawLeadDataNameResolver("Acres")));
//this is clearly just a snip to show it's a basic map
}
}
This is the core library method being called: (note it is a static..which means I won't have a constructor...if this is the problem am I to understand then that AutoMapper can't be utilized by static helper classes...that doesn't make sense....so likely I'm just not doing it right.
public static class RawDataProcessing
{
public static FullData HTMLDataScrape(string htmlScrape)
{
HtmlDocument doc = new HtmlDocument();
doc.LoadHtml(htmlScrape);
var list = Recurse(doc.DocumentNode);
//HTML agility stuff that turns my html doc into a List<RawData> object
return Mapper.Map<FullData>(list);
}
My test harness calls it like this:
var _data = RawDataProcessing.HTMLDataScrape(rawHTML);
This of course errors because the map isn't "registered".
If I do this in the test harness:
var x = new RawData_FullData();
var _data = RawDataProcessing.HTMLDataScrape(rawHTML);
Then everything works as my map get's registered albeit I think in a really bogus way...but it does work.
So the question is how do I register my mapping in the core library project...so that ANY method can use it...there isn't really an equivalent global.asax in a dll is there?
Thank you for helping me connect the missing pieces.
Put it in the static constructor of either the source or the target type of the mapping.
public class FullData
{
static FullData()
{
Mapper.CreateMap<IEnumerable<RawData>, FullData>()
.ForMember(d => d.Acres, m => m.ResolveUsing(new RawLeadDataNameResolver("Acres")));
}
}
The static constructor will automatically get called the first time you try to use the type FullData for anything (for example a mapping).
You can use PreApplicationStartMethod for any class and it's method in your class library which will be referenced from your startup project if you want automatically to call this on startup. And then you can register all your mappings in that method. By the way, I suggest to use AddProfile for registering all mappings.
[assembly: PreApplicationStartMethod(typeof(MyClassLibrary.Startup), "Start")]
namespace MyClassLibrary
{
public class Startup
{
// Automatically will work on startup
public static void Start()
{
Mapper.Initialize(cfg =>
{
Assembly.GetExecutingAssembly().FindAllDerivedTypes<Profile>().ForEach(match =>
{
cfg.AddProfile(Activator.CreateInstance(match) as Profile);
});
});
}
}
}
You just need to create new classes which derived from Profile class and then override it's Configure() method:
...
public class FooMapperProfile:Profile
{
protected override void Configure()
{
Mapper.CreateMap<OtherFoo, Foo>()
.ForMember(...
... // so on
}
}
public class AnotherFooMapperProfile:Profile
{
protected override void Configure()
{
Mapper.CreateMap<OtherFoo, AnotherFoo>()
.ForMember(...
... // so on;
}
}
...
// and so on
Additional information:
If you have seen I have initialized all mappings with that code:
Mapper.Initialize(cfg =>
{
Assembly.GetExecutingAssembly().FindAllDerivedTypes<Profile>().ForEach(match =>
{
cfg.AddProfile(Activator.CreateInstance(match) as Profile);
});
});
It will automatically find all types derived from Profile and will add all profiles after createing their new instances.
Update1:
As #Scott Chamberlain commented, PreApplicationStartMethod only works for ASP.NET applications. This would not work with a desktop app. If you are working with Wpf, then you can use Application.OnStartup method. Or just call Start.Startup (); in load event.
Update2:
FindAllDerivedTypes extension method:
public static class AssemblyExtensions
{
public static List<Type> FindAllDerivedTypes<T>(this Assembly assembly)
{
var derivedType = typeof(T);
return assembly.GetTypes()
.Where(t => t != derivedType && derivedType.IsAssignableFrom(t))
.ToList();
}
}
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?
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?