Using MEF I can create and load a type like this:
var view = Container.GetExportedValue<MyView>();
Now what I want to do is this:
Type t = typeof(MyView);
var view = Container.GetExportedValue<t>();
(of course the type might contain something different than MyView).
This is not possible using the generics GetExportedValue<> - is there some other way to achieve this?
You can use reflection.
Here is an example:
using System;
using System.ComponentModel.Composition;
using System.ComponentModel.Composition.Hosting;
using System.Linq;
using System.Reflection;
namespace WindowsFormsApplication1
{
static class Program
{
[STAThread]
static void Main()
{
AggregateCatalog catalog = new AggregateCatalog();
catalog.Catalogs.Add(new AssemblyCatalog(typeof(IMessage).Assembly));
CompositionContainer container = new CompositionContainer(catalog);
Type t = typeof(IMessage);
var m = container.GetExportedValue(t);
}
}
public static class CompositionContainerExtension
{
public static object GetExportedValue(this ExportProvider container, Type type)
{
// get a reference to the GetExportedValue<T> method
MethodInfo methodInfo = container.GetType().GetMethods()
.Where(d => d.Name == "GetExportedValue"
&& d.GetParameters().Length == 0).First();
// create an array of the generic types that the GetExportedValue<T> method expects
Type[] genericTypeArray = new Type[] { type };
// add the generic types to the method
methodInfo = methodInfo.MakeGenericMethod(genericTypeArray);
// invoke GetExportedValue<type>()
return methodInfo.Invoke(container, null);
}
}
public interface IMessage
{
string Message { get; }
}
[Export(typeof(IMessage))]
public class MyMessage : IMessage
{
public string Message
{
get { return "test"; }
}
}
}
Related
I use the following method to load a new Assembly and create an instance of a class into a new AppDomain.
private static object CreateInstanceFromBinary(AppDomain appDomain, string typeName)
{
Assembly entryAssembly = Assembly.GetEntryAssembly();
byte[] assemblyBinary = LoadAssemblyBinary();
Assembly loadedAssembly = appDomain.Load(assemblyBinary);
if (loadedAssembly != null)
{
return loadedAssembly.CreateInstance(typeName);
}
return null;
}
Which get's called like so.
AppDomain appDomain = AppDomain.CreateDomain(domainName);
appDomainHelper = CreateInstanceFromBinary(appDomain, typeof(MyClass).FullName) as MyClass;
Looking into the loadedAssembly I can see that MyClass exists inside of the DefinedTypes and it's name matches typeName. However, when the code runs
loadedAssembly.CreateInstance(typeName)
it returns null.
This code was working, however, I recently moved this class into the same dll as the one that calls it and now it has started returning null.
Any ideas on how to fix this?
For some short(ish) reproducible code you can use the following. In this code ClassLibrary1 has CopyLocal set to false and then included as an EmbeddedResource to mimic what I have in my live project in case that matters.
Inside of ConsoleApplication1 I have Program.
using ClassLibrary1;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
namespace ConsoleApplication1
{
class Program
{
static Program()
{
AppDomain.CurrentDomain.AssemblyResolve += Resolve;
}
static void Main(string[] args)
{
WorkerClass workerClass = new WorkerClass();
workerClass.DoWork();
Console.WriteLine("\r\nPress enter to exit...");
Console.ReadLine();
}
static System.Reflection.Assembly Resolve(object sender, ResolveEventArgs args)
{
if (!args.Name.Contains(","))
{
return null;
}
List<string> rn = System.Reflection.Assembly.GetEntryAssembly().GetManifestResourceNames()
.Where(r => r.EndsWith(".dll", StringComparison.OrdinalIgnoreCase))
.ToList();
string assemblyName = rn.FirstOrDefault(r => r.EndsWith(args.Name.Substring(0, args.Name.IndexOf(",")) + ".dll"));
if (!String.IsNullOrEmpty(assemblyName))
{
using (Stream stream = System.Reflection.Assembly.GetEntryAssembly().GetManifestResourceStream(assemblyName))
{
byte[] assemblyBinary = new byte[stream.Length];
stream.Read(assemblyBinary, 0, assemblyBinary.Length);
System.Reflection.Assembly assembly = System.Reflection.Assembly.Load(assemblyBinary);
if (Environment.UserInteractive)
{
Console.WriteLine("Loaded Assembly: " + assembly.FullName);
}
return assembly;
}
}
if (Environment.UserInteractive)
{
Console.WriteLine($"** Failed to find an assembly with name: {args.Name} ** ");
}
return null;
}
}
}
Inside of ClassLibrary1 there is WorkerClass.
using System;
namespace ClassLibrary1
{
public class WorkerClass
{
public void DoWork()
{
try
{
HelperClass hc = HelperClass.Create("Name");
Console.WriteLine("Created");
}
catch (Exception ex)
{
Console.WriteLine("Failed to create: " + ex.ToString());
}
}
}
}
and HelperClass.
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Security.Permissions;
namespace ClassLibrary1
{
[Serializable]
public class HelperClass : MarshalByRefObject
{
public AppDomain Domain { get; private set; }
public HelperClass()
{
}
[SecurityPermission(SecurityAction.Demand, Flags = SecurityPermissionFlag.Infrastructure)]
public override object InitializeLifetimeService()
{
return null;
}
public static HelperClass Create(string domainName)
{
AppDomain appDomain = AppDomain.CreateDomain(domainName);
HelperClass helperClass = CreateInstanceFromBinary(appDomain, typeof(HelperClass).AssemblyQualifiedName) as HelperClass;
if (helperClass == null)
{
throw new Exception("Unable to create instance from binary resource.");
}
helperClass.Domain = appDomain;
return helperClass;
}
private static object CreateInstanceFromBinary(AppDomain appDomain, string typeName)
{
Assembly entryAssembly = Assembly.GetEntryAssembly();
IList<string> rn = entryAssembly.GetManifestResourceNames().Where(r => r.EndsWith(".dll")).ToList();
string assembly = rn.FirstOrDefault(r => r.EndsWith($"{typeof(HelperClass).Assembly.GetName().Name}.dll"));
if (!String.IsNullOrEmpty(assembly))
{
using (Stream stream = entryAssembly.GetManifestResourceStream(assembly))
{
byte[] assemblyBinary = new byte[stream.Length];
stream.Read(assemblyBinary, 0, assemblyBinary.Length);
Assembly loadedAssembly = appDomain.Load(assemblyBinary);
if (loadedAssembly != null)
{
return loadedAssembly.CreateInstance(typeName);
}
}
}
return null;
}
}
}
Where it is return loadedAssembly.CreateInstance(typeName); that returns null.
In your function public static HelperClass Create(string domainName) you are passing the AssemblyQualifiedName as the type of the class to create.
I think you just want to pass the type name, ie: ClassLibrary1.HelperClass
//HelperClass helperClass = CreateInstanceFromBinary(appDomain, typeof(HelperClass).AssemblyQualifiedName) as HelperClass;
HelperClass helperClass = CreateInstanceFromBinary(appDomain, typeof(HelperClass).ToString()) as HelperClass;
I tried some variations, each time passing in the Assembly Qualified Name failed, just the type name worked as expected.
Variations tried, and failed:
// Do not work
var x = loadedAssembly.CreateInstance(typeName); //AssemblyQualifiedName
var loadedType = loadedAssembly.GetType(typeName); //AssemblyQualifiedName
// Work
var x = Activator.CreateInstance(typeof(HelperClass)); // Works
var x = loadedAssembly.CreateInstance("ClassLibrary1.HelperClass");
var loadedType = loadedAssembly.GetType("ClassLibrary1.HelperClass");
var x = Activator.CreateInstance(loadedType);
I have the following code, using Mono.Cecil:
{
ModuleDefinition module = ModuleDefinition.ReadModule("library.dll");
TypeDefinition type1 = module.Types.Where(t => "Namespace.Destination".Equals(t.FullName)).Single();
TypeDefinition type2 = module.Types.Where(t => "Namespace.Target".Equals(t.FullName)).Single();
MethodDefinition method1 = type1.Methods.Where(m => "Test".Equals(m.Name)).Single();
MethodDefinition method2 = type2.Methods.Where(m => "Test".Equals(m.Name)).Single();
var processor = methodTesta1.Body.GetILProcessor();
var newInstruction = processor.Create(OpCodes.Call, methodTesta2);
var firstInstruction = methodTesta1.Body.Instructions[0];
processor.Replace(firstInstruction, newInstruction);
}
namespace Namespace
{
public class Destination
{
public String Test()
{
Console.Write("Destination method");
}
}
public class Target
{
public String Test()
{
Console.Write("Target Method");
}
}
}
I'd not like to create a new "dll" file or overwrite the current, I want to modify class only at runtime.
How can I "persist" the modification and create a new instance of Destination class with modified method?
Is there a way to do it?
EDIT: The objective is execute a different method body, when certain method is called, wich return a certain type.
I am trying to use GetType method for my custom class . I have the name of the class as string and i want to get type of it dynamically. I have the same name for two different classes which are located in different directories.
For Example:
MyClass.cs in Folder1:
namespace ConsoleApplication1.Folder1
{
public class MyClass : IClass
{
public void PrintMe()
{
System.Console.WriteLine("I am Folder 1 Class");
}
}
}
MyClass.cs in Folder2:
namespace ConsoleApplication1.Folder2
{
public class MyClass : IClass
{
public void PrintMe()
{
System.Console.WriteLine("I am Folder 2 Class");
}
}
}
Namespace is ConsoleApplication1
different classes with the same name are in the Folder1 and Folder2.
I want to get it's type from such a string:
var runtimeString = "Folder1.MyClass"
There is method mentioned in MSDN named GetType(string fileName)
How can i get type of the file and resolve it from the serviceLocator with type on runtime like:
var typeOfMyClass = GetType(runtimeString);
var instanceOfMyClass = ServiceLocator.Resolve<TypeOfMyClass>();
You appear to be describing a need for a factory method, something along the lines of:
public class MyClassFactory : IMyClassFactory
{
private Dictionary<string, Action<IClass>> _factory =
new Dictionary<string, Action<IClass>>
{
["Folder1.MyClass"] = () => new ConsoleApplication1.Folder1.MyClass(),
["Folder2.MyClass"] = () => new ConsoleApplication1.Folder2.MyClass(),
...
};
public IClass GetClassInstance(string myClassName)
{
if (_factory.Contains(myClassName))
{
return _factory[myClassName]();
}
throw NoSuchClassException(myClassName);
}
}
I believe the following is what you are trying to accomplish:
static void Main(string[] args)
{
var runtimeString = "Folder1.MyClass";
IClass instanceOfMyClass = (IClass)CreateInstance(runtimeString);
instanceOfMyClass.PrintMe();
Console.ReadKey();
}
private static object CreateInstance(string className)
{
var type = Assembly.GetExecutingAssembly().GetTypes()
.First(t => t.FullName.EndsWith(className));
return Activator.CreateInstance(type);
}
You may use Activator.CreateInstance() method to create an object of a class from its name string as below.
Create the Type object:
Type type1 = typeof(MyClass);
or
Type type1 = Type.GetType("MyClass");
Create an instance of that type:
Object o = Activator.CreateInstance(type1);
I am using the following interface as a ToFactory() binding:
public interface ISamplerFactory
{
ISampler Create(Action<EventHandler<ValueChangedEventArgs>> register, Action<EventHandler<ValueChangedEventArgs>> unregister, Func<decimal?> valueGetter);
}
When I bind ToFactory() I can successfully create the class but I then get a memory leak whereby the register, unregister and valueGetter parameters are held by a ConstructorArgument inside Ninject, which reference a target/parameter object inside the delegates. This keeps that target object from getting GC'd. I am using ContextPreservation extension too if that makes a difference. (See complete sample code below)
When I remove the "ToFactory()" bind and create a standard factory class, it works.
public class SamplerFactory : ISamplerFactory
{
private readonly IDistributionResolver _resolverFactory;
public SamplerFactory(IDistributionResolverFactory resolverFactory)
{
_resolverFactory = resolverFactory;
}
ISampler Create(Action<EventHandler<ValueChangedEventArgs>> register, Action<EventHandler<ValueChangedEventArgs>> unregister, Func<decimal?> valueGetter)
{
return new SpecificSampler(_resolverFactory, register, unregister, valueGetter);
}
}
And my target objects inside the delegates are GC'd successfully.
Is there something I am doing wrong or isn't the Factory extension meant to handle these more complex arguments? I assume if I used the .WithConstructorArgument I would get the same outcome.
EDIT: Added all necessary bindings and rewrote my sample code:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace ConsoleApplication
{
using System;
using Ninject;
using Ninject.Extensions.ContextPreservation;
using Ninject.Extensions.Factory;
public class Program
{
static void Main(string[] args)
{
new Program().Run();
}
public void Run()
{
var kernel = new StandardKernel(new NinjectSettings() { LoadExtensions = false });
kernel.Load(new FuncModule());
kernel.Load(new ContextPreservationModule());
kernel.Bind<IDistributionResolver>().To<DistributionResolver>(); // This is a constructor-less object.
kernel.Bind<ISampler>().To<SpecificSampler>();
kernel.Bind<ISamplerFactory>().ToFactory();
kernel.Bind<IInjected>().To<Injected>();
kernel.Bind<IDistributionResolver>().To<DistributionResolver>();
kernel.Bind<IDistributionResolverFactory>().ToFactory();
var s = new SomeObject();
var weakS = new WeakReference(s);
var factory = kernel.Get<ISamplerFactory>();
var sampler = CreateInstance(factory, s);
s = null;
factory = null;
sampler = null;
GC.Collect();
if (weakS.IsAlive)
throw new Exception();
}
private ISampler CreateInstance(ISamplerFactory factory, SomeObject someObject)
{
var x = factory.Create(y => someObject.Do += y, z => someObject.Do -= z, () => someObject.Query());
if (x == null)
throw new Exception();
return x;
}
public class SomeObject
{
public event EventHandler<ValueChangedEventArgs> Do;
public decimal? Query()
{
return 0;
}
}
public class SpecificSampler : ISampler
{
private readonly IDistributionResolverFactory resolver;
private readonly Action<EventHandler<ValueChangedEventArgs>> register;
private readonly Action<EventHandler<ValueChangedEventArgs>> unregister;
private Func<decimal?> _valueGetter;
public SpecificSampler(
IDistributionResolverFactory resolver, // This is injected
Action<EventHandler<ValueChangedEventArgs>> register, // The rest come from the factory inputs
Action<EventHandler<ValueChangedEventArgs>> unregister,
Func<decimal?> valueGetter)
{
this.resolver = resolver;
this.register = register;
this.unregister = unregister;
_valueGetter = valueGetter;
// Do Stuff;
}
}
public class ValueChangedEventArgs : EventArgs
{
}
public interface ISamplerFactory
{
ISampler Create(Action<EventHandler<ValueChangedEventArgs>> register, Action<EventHandler<ValueChangedEventArgs>> unregister, Func<decimal?> valueGetter);
}
public interface IDistributionResolverFactory
{
IDistributionResolver Create(IDictionary<string, string> picked);
}
public interface IDistributionResolver
{
}
private class DistributionResolver : IDistributionResolver
{
readonly IInjected _i;
readonly IDictionary<string, string> _picked;
public DistributionResolver(IInjected i, IDictionary<string, string> picked)
{
_i = i;
_picked = picked;
}
}
public interface ISampler
{
}
}
public interface IInjected
{
}
class Injected : IInjected
{
}
}
I tracked this down to a memory leak in Ninject Core:
https://github.com/ninject/ninject/issues/74
I need to dynamically load an interface assembly that I use on client-side remoting. Something like this.
static void Main(string[] args)
{
TcpClientChannel clientChannel = new TcpClientChannel();
ChannelServices.RegisterChannel(clientChannel, false);
Assembly interfaceAssembly = Assembly.LoadFile("RemotingInterface.dll");
Type iTheInterface =
interfaceAssembly.GetType("RemotingInterface.ITheService");
RemotingConfiguration.RegisterWellKnownClientType(iTheInterface,
"tcp://localhost:9090/Remotable.rem");
object wellKnownObject = Activator.GetObject(iTheInterface,
"tcp://localhost:9090/Remotable.rem");
}
Only I can't seem to grasp how to call any methods as I can't cast the Activator.GetObject. How can I create a proxy of ITheService without knowing the interface at compile-time?
Got an answer from MSDN forums.
static void Main(string[] args)
{
TcpClientChannel clientChannel = new TcpClientChannel();
ChannelServices.RegisterChannel(clientChannel, false);
Assembly interfaceAssembly = Assembly.LoadFile("RemotingInterface.dll");
Type iTheInterface = interfaceAssembly.GetType("RemotingInterface.ITheService");
RemotingConfiguration.RegisterWellKnownClientType(iTheInterface,
"tcp://localhost:9090/Remotable.rem");
object wellKnownObject = Activator.GetObject(iTheInterface,
"tcp://localhost:9090/Remotable.rem");
MethodInfo m = iTheInterface.GetMethod("MethodName");
m.Invoke(wellKnownObject, new object[] { "Argument"});
}
The returned object implements the interface, so you can use reflection to get its member methods and invoke them.
Or, in C#4, you can use dynamic:
dynamic wellKnownObject = Activator.GetObject(iTheInterface,
"tcp://localhost:9090/Remotable.rem");
wellKnownObject.SomeMethod(etc ..);
First, inspect the available methods/interfaces of your object:
object wellKnownObject =
Activator.GetObject(iTheInterface, "tcp://localhost:9090/Remotable.rem");
var objType = wellKnownObject.GetType();
var methods = objType.GetMethods();
var interfaces = objType.GetInterfaces();
After you're sure about the method you want to invoke,
Consider using DLR and/or wrap the dynamic object in a DynamicObject container.
Use methods[i].Invoke on the object.
Here are some examples:
namespace ConsoleApplication1
{
using System;
class Program
{
static void Main()
{
//Using reflection:
object obj = GetUnknownObject();
var objType = obj.GetType();
var knownInterface = objType.GetInterface("IA");
var method = knownInterface.GetMethod("Print");
method.Invoke(obj, new object[] { "Using reflection" });
//Using DRL
dynamic dObj = GetUnknownObject();
dObj.Print("Using DLR");
//Using a wrapper, so you the dirty dynamic code stays outside
Marshal marshal = new Marshal(GetUnknownObject());
marshal.Print("Using a wrapper");
}
static object GetUnknownObject()
{
return new A();
}
} //class Program
class Marshal
{
readonly dynamic unknownObject;
public Marshal(object unknownObject)
{
this.unknownObject = unknownObject;
}
public void Print(string text)
{
unknownObject.Print(text);
}
}
#region Unknown Types
interface IA
{
void Print(string text);
}
class A : IA
{
public void Print(string text)
{
Console.WriteLine(text);
Console.ReadKey();
}
}
#endregion Unknown Types
}
Can I get the Interface information from the remoting URL like http://localhost:8080/xxx.rem?wsdl.
As WebService, I can get the interface information from the service url, http://xXX.xx.xxx.xx/url.svc?wsdl, and compile the assembly by myself code, and invoke methods via reflection.