i'm trying to generate Code at runtime which uses a custom class from another Namespace.
Here´s my code:
namespace Test.Programm.Network
{
class Handler
{
public void CreateAssembly()
{
string[] code =
{
#"using System;
using System.Collections;
namespace Test.Programm.Network
{
class HandleMessage
{
protected static internal Queue _queue;
public static void Received(string message)
{
lock (_queue)
{
_queue.Enqueue(message);
}
}
public HandleMessage()
{
_queue = new Queue();
}
}
}"
};
CompilerParameters parms = new CompilerParameters();
parms.GenerateExecutable = false;
parms.GenerateInMemory = true;
CodeDomProvider compiler = null;
compiler = CodeDomProvider.CreateProvider("CSharp");
CompilerResults compilerResults = compiler.CompileAssemblyFromSource(parms, code);
var cls = compilerResults.CompiledAssembly.GetType("Test.Programm.Network.HandleMessage");
Assembly assembly = compilerResults.CompiledAssembly;
var newHandler = assembly.CreateInstance(compilerResults.CompiledAssembly.GetType("Test.Programm.Network.HandleMessage").ToString());
}
}
}
But i don´t want to pass a string to my function, i want to pass an own type to that function.
Now i have a simple message class like that:
namespace Test.Programm.Messages
{
public class Message<T>
{
string _message;
}
}
if i want too add a using Test.Programm.Messages to the code i want to generate, i´m getting error that this Namespace doesn´t exist, missing reference...
I tried to add parms.ReferencedAssemblies.Add("Grid.Node.Messages"); to the code Generation, but this doesnt work. searching the web and SO haven´t given an answer yet -.-
Thanks for your help.
You should reference the assembly instead of the namespace. Something like this:
parms.ReferencedAssemblies.Add("path_to.dll");
Where path_to.dll is a path to the assembly file containing type Message<T>.
Related
I'm writing a C# source generator (similar to analyzer) and I'm trying to determine if a local variable (ILocalSymbol) is readonly due to being declared with a using directive. Specifically the following case:
using System;
struct Container : IDisposable
{
public void Dispose() {}
}
public class C {
public void M() {
using (var container = new Container())
{
var otherCon = new Container();
// I want to detect when the following would throw an error
//container = otherCon;
}
}
}
Roslyn doesn't seem to have any public APIs for this as far as I can tell. LocalSymbol has IsUsing but that is an internal type. Same deal with DeclarationKind.
The only way I found to do this was by examining the declaring syntax for the variable:
var isWritable = true;
var declaringSyntax = symbol.OriginalDefinition.DeclaringSyntaxReferences.FirstOrDefault();
if (declaringSyntax?.GetSyntax().Parent is VariableDeclarationSyntax
variableDeclarationSyntax && variableDeclarationSyntax?.Parent is UsingStatementSyntax)
isWritable = false;
I am trying to make a (my first MEF) system in which plugins can be recursive, i.e. my main system calls a MEF plugin with a standard interface, which on its own can then call another (or several) plugin(s), and so on.
When testing though, my plugin does not call the underlying plugin, but starts processing itself (creating a loop).
Any idea how I can prevent this?
Interface:
public interface IConnector
{
XDocument Run(object serviceCredentials, object connectorIds, object connectorKeys);
}
My main plugin inherits the interface, and defines the import for the next (The subplugin has the same definition):
[Export(typeof(IConnector))]
public class Connector : IConnector
{
[Import(typeof(IConnector))]
private IConnector connector;
....
The called plugin is initiated (in the Run method of the main plugin):
public XDocument Run(object serviceCredentials, object connectorIds, object connectorKeys)
{
string calledConnector = Path.Combine(AssemblyDirectory, "subplugin.dll");
AssemblyCatalog assembyCatalog = new AssemblyCatalog(Assembly.LoadFrom(calledConnector));
CompositionContainer container = new CompositionContainer(assembyCatalog);
container.ComposeParts(this);
....
The container should now contain just one plugin, the subplugin.dll.
I call the method 'Run' which is in the interface to invoke the subplugin method:
XDocument something = connector.Run(serviceCredentials, connectorids, connectorkeys);
But, instead of running the subplugin code, the 'Run' method in my main plugin activates, which keeps activating itself.
When I remove the [Export(typeof(iConnector)] in the main plugin, the subplugin is activated, but I want my main plugin to be able to be called in the same manner.
Being new to MEF I am stuck as to how to solve this. Any help would be much appreciated!
You should use Contracts and specify your intent, otherwise MEF will go into an infinite loop or pick the Connector as it exposes IConnector itself.
Some more info from MSDN.
For example
[Export("Container", typeof(IConnector))]
public class Connector : IConnector
{
[Import("Component", typeof(IConnector))]
private IConnector connector;
....
UPDATE
So after giving it some thought, here is an example of metadata based approach, and one that also limits the number of expensive catalog operations.
The IConnector
using System.Xml.Linq;
namespace Common
{
public interface IConnector
{
XDocument Run(object serviceCredentials, object connectorIds, object connectorKeys);
void Identify();
}
}
The metadata attribute ConnectorMetadata
using System;
using System.Collections.Generic;
using System.ComponentModel.Composition;
namespace Common
{
[MetadataAttribute]
[AttributeUsage(AttributeTargets.Class)]
public class ConnectorMetadata : ExportAttribute
{
public string Name { get; private set; }
public ConnectorMetadata(string name):base(typeof(IConnector))
{
Name = name;
}
public ConnectorMetadata(IDictionary<string, object> metaData) : base(typeof (IConnector))
{
Name = Convert.ToString(metaData["Name"]);
}
}
}
The lazy singleton for PluginsCatalog
using System;
using System.ComponentModel.Composition;
using System.ComponentModel.Composition.Hosting;
using System.IO;
using System.Linq;
using System.Reflection;
using Common;
namespace Common
{
public class PluginsCatalog
{
[ImportMany]
public Lazy<IConnector, ConnectorMetadata>[] Connectors;
private static readonly Lazy<PluginsCatalog> LazyInstance = new Lazy<PluginsCatalog>(() => new PluginsCatalog());
private PluginsCatalog()
{
var assemblyCatalog = new AssemblyCatalog(Assembly.GetExecutingAssembly());
var path = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location) ?? Directory.GetCurrentDirectory();
var directoryCatalog = new DirectoryCatalog(path, "*plugin.dll");
var aggregateCatalog = new AggregateCatalog(assemblyCatalog, directoryCatalog);
var container = new CompositionContainer(aggregateCatalog);
container.SatisfyImportsOnce(this);
}
public static PluginsCatalog Instance { get { return LazyInstance.Value; } }
public IConnector GetConnector(string name)
{
var match = Connectors.SingleOrDefault(s => s.Metadata.Name.Equals(name));
return match == null ? null : match.Value;
}
}
}
The "Primary" IConnector
using System;
using System.Xml.Linq;
using Common;
namespace Common
{
[ConnectorMetadata("Primary")]
public class Connector : IConnector
{
public XDocument Run(object serviceCredentials, object connectorIds, object connectorKeys)
{
PluginsCatalog.Instance.GetConnector("Sub").Identify();
return default(XDocument);
}
public void Identify()
{
Console.WriteLine(GetType().FullName);
}
}
}
The "Sub" IConnector
using System;
using System.Xml.Linq;
using Common;
namespace SubPlugin
{
[ConnectorMetadata("Sub")]
public class SubConnector:IConnector
{
public XDocument Run(object serviceCredentials, object connectorIds, object connectorKeys)
{
return default(XDocument);
}
public void Identify()
{
Console.WriteLine(GetType().FullName);
}
}
}
and finally the program itself:
namespace SOMEF
{
class Program
{
static void Main(string[] args)
{
var connector = PluginsCatalog.Instance.GetConnector("Primary");
connector.Identify();
connector.Run(null, null, null);
}
}
}
Which prints:
SOMEF.Connector
SubPlugin.SubConnector
Hope this helps ... :)
You might want to read this https://msdn.microsoft.com/en-us/library/ee155691(v=vs.110).aspx
With exmplanation how named exports are used
public class MyClass
{
[Import("MajorRevision")]
public int MajorRevision { get; set; }
}
public class MyExportClass
{
[Export("MajorRevision")] //This one will match.
public int MajorRevision = 4;
[Export("MinorRevision")]
public int MinorRevision = 16;
}
I am programming an application in visual studio 2010 and i want to add a library to references using c# ,so i will add the using library and the invocation in file of a business class
public void addreference()
{
//needed code
}
public addInvocation()
{
//write in the file of business class
}
it is like select add reference using the mouse but i am wishing use c# to do it
how can i do it ?
Critical solution
i tried to use the solution but i found a problem , i instantiated successfully the class of the library but i can not use their methods
first i created an Interface called Interface1
second i created a class called Classe1
then I generated the .dll
The code:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace ClassLibrary1
{
public interface Interface1
{
int add();
int sub();
}
public class Class1 : Interface1
{
Class1()
{
}
#region Interface1 Members
public int add()
{
return 10;
}
public int sub()
{
return -10;
}
#endregion
}
}
the i tied to instantiate the class1
the code
string relative = "ClassLibrary1.dll";
string absolute = Path.GetFullPath(relative);
Assembly assembly = Assembly.LoadFile(absolute);
System.Type assemblytype = assembly.GetType("ClassLibrary1.Class1");
object a = assembly.CreateInstance("ClassLibrary1.Class1", false, BindingFlags.CreateInstance, null,null,null, null);
Now i wish invoke the method add how can i do it
var a = Activator.CreateInstance(assemblytype, argtoppass);
System.Type type = a.GetType();
if (type != null)
{
string methodName = "methodname";
MethodInfo methodInfo = type.GetMethod(methodName);
object resultpath = methodInfo.Invoke(a, argtoppass);
res = (string)resultpath;
}
Reflection seems the obvious option. Start with
foreach (FileInfo dllFile in exeLocation.GetFiles("*.dll"))
{
Assembly assembly = Assembly.LoadFile(dllFile.FullName);
...
then:
Type[] exportedTypes = assembly.GetExportedTypes();
foreach (Type exportedType in exportedTypes)
{
//look at each instantiable class in the assembly
if (!exportedType.IsClass || exportedType.IsAbstract)
{
continue;
}
//get the interfaces implemented by this class
Type[] interfaces = exportedType.GetInterfaces();
foreach (Type interfaceType in interfaces)
{
//if it implements IMyPlugInterface then we want it
if (interfaceType == typeof(IMyPlugInterface))
{
concretePlugIn = exportedType;
break;
}
}
finally
IMyPlugInterface myPlugInterface = (IMyPlugInterface) Activator.CreateInstance(concretePlugIn);
...or something like that. It won't compile but yet get the jist.
I keep getting the following error with the following code:
Error: "No exports were found that match the constraint:
ContractName MefTestSample.Contracts.ICanDoSomethingImportant"
Program.cs is as follows:
namespace MefTestSample
{
class Program
{
private static CompositionContainer m_Container;
static void Main(string[] args)
{
UseMockedUpTypes();
ICanDoSomethingImportant cool = m_Container.GetExport<ICanDoSomethingImportant>().Value;
cool.DoSomethingClever();
Console.ReadLine();
}
private static void UseMockedUpTypes()
{
//The commented out section works just by itself.
/*
m_Container =
new CompositionContainer(
new AssemblyCatalog(
typeof(MefTestSample.Mocks.ClassesDoNotNecessarly).Assembly));
*/
//This fails if i dont comment out the [ImportingConstructor] block.
var assemblyCatalog1 = new AssemblyCatalog(typeof (MefTestSample.Mocks.ClassesDoNotNecessarly).Assembly);
var myassembly = new AssemblyCatalog(typeof (ServiceLibrary.MoonService).Assembly);
var aggregateCatalog = new AggregateCatalog(assemblyCatalog1, myassembly);
m_Container = new CompositionContainer(aggregateCatalog);
}
}
}
Below is the code for ClassesDoNotNecessarly:
namespace MefTestSample.Mocks
{
[Export(typeof(ICanDoSomethingImportant))]
public class ClassesDoNotNecessarly : ICanDoSomethingImportant
{
//private IServicesContract _isc;
#region ICanDoSomethingImportant Members
/* This seems to be causing the problem.
[ImportingConstructor]
public ClassesDoNotNecessarly([Import("Moon")]IServicesContract isc)
{
string temp = isc.DisplayMessage();
Console.WriteLine(temp);
}
*/
public void DoSomethingClever()
{
Console.WriteLine("Hehe, I'm actually procrastinating!");
}
#endregion
}
}
MoonService is as follows:
namespace ServiceLibrary
{
[Export(typeof(IServicesContract))]
public class MoonService : IServicesContract
{
public string DisplayMessage()
{
return "Moon services were accessed.";
}
}
}
What i believe the problem is. If i leave program.cs as it is, and comment out the [ImportingConstructor] attribute + constructor in the ClassesDoNotNecessarly class, the program will display the text in that class.
If i uncomment the [ImportingConstructor] attribute n constructor, i then start getting the error shown at the top of this question.
Any ideas would be appreciated.
Remove the [Import("Moon")] in the constructor, the Export is not named, so the Import cannot be named in return.
I am trying to make an instance of a class based on a string that will be retrieved from the User Interface, and then I want to access the properties of the instance of the class.
Here is an overview of what I have so far -
namespace MamdaAdapter
{
public interface IExchange
{
string GetTransport();
}
}
namespace MamdaAdapter
{
public class Exchange
{
public class Arca : IExchange
{
private const string _Transport = "tportname";
public string GetTransport()
{
return _Transport;
}
}
public static IExchange DeriveExchange(string ExchangeName)
{
IExchange SelectedExchange = (IExchange)Activator.CreateInstance(Type.GetType(ExchangeName));
return SelectedExchange;
}
}
}
namespace MyUserInterface
{
public class MainForm
{
private void simpleButton1_Click(object sender, EventArgs e)
{
IExchange SelectedExchange = Exchange.DeriveExchange("Exchange.Arca");
Console.WriteLine(SelectedExchange.GetTransport());
}
}
}
UPDATE:
Right now, I'm getting an Exception that says the "Value cannot be null" which to me means that it is unable to create the instance of the class given the string provided -
The problem here is how you specify the name of your class:
First, specify the namespace. Second, since Arca is an inner class you must use '+' instead of '.'
(...) = Exchange.DeriveExchange("MamdaAdapter.Exchange+Arca");
Assuming you UI doesnt expose the full type name, you typically want a dictionary to associate the display name to the type:
Dictionary<string, Type> _associations = new Dictionary<string, Type>();
Then, you simply instantiate the new object:
if(_associations.ContainsKey(someString))
{
Type selectedType = _associations[someString];
return Activator.CreateInstance(selectedType) as IExchange;
}
throw new ApplicationException("No type defined for that string yo");
If the string is not known at compile time, you basically need to check for the existance of the type:
var type = Type.GetType(someString);
if(type != null)
{
// Do Stuff
}
I wrote a small c# console application to simulate your need, tested ok, hope it helps:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using MamdaAdapter;
using System.Reflection;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
IExchange SelectedExchange = Exchange.DeriveExchange("MamdaAdapter.Arca");
Console.WriteLine(SelectedExchange.GetTransport());
}
}
}
namespace MamdaAdapter
{
public interface IExchange
{
string GetTransport();
}
}
namespace MamdaAdapter
{
public class Arca : IExchange
{
private const string _Transport = "tportname";
public string GetTransport()
{
return _Transport;
}
}
}
namespace MamdaAdapter
{
public class Exchange
{
public static IExchange DeriveExchange(string ExchangeName)
{
IExchange SelectedExchange = (IExchange)Assembly.GetAssembly(typeof(IExchange)).CreateInstance(ExchangeName, false, BindingFlags.CreateInstance, null, null, null, null);
return SelectedExchange;
}
}
}
If the Type you are looking for is not defined in the same assembly that is executing Type.GetType you must use the AssemblyQualifiedName (something like MyNamespace.MyClass, MyAssembly, Version=1.3.0.0, Culture=neutral, PublicKeyToken=b17a5c561934e089), even the FullName is not enough. Otherwise you could first get the assembly containing the class and then execute the GetType method of the Assembly class.