Creating dynamic class from a string containing the class definition - c#

I have a stored procedure which accepts the table name, then it reads the table structure and returns me the table structure in the form of a class definition in a string.
E.g.:
string myString =
"
public class TableName
{
public int Column1 { get; set; }
}
"
Is it possible a create a Class/Type from the string containing the class definition ?
For eg:-
Type type = GenerateType(myString);
I have to pass this type variable to my further piece of code so please help me to create class/type from the string containing the class definition.

You can use the CSharpCodeProvider to compile your result at runtime and then use the Activator - Class to create an object from your generated code.
// compile your piece of code to dll file
Microsoft.CSharp.CSharpCodeProvider cSharpCodeProvider = new Microsoft.CSharp.CSharpCodeProvider();
System.CodeDom.Compiler.CompilerParameters compilerParameters = new System.CodeDom.Compiler.CompilerParameters();
compilerParameters.GenerateInMemory = true;
compilerParameters.GenerateExecutable = false;
System.CodeDom.Compiler.CompilerResults cResult = cSharpCodeProvider.CompileAssemblyFromSource(compilerParameters, "using System; namespace Tables { 'put here your class definition' }");
// then load your dll file, get type and object from class
Assembly assembly = cResult.CompiledAssembly;
Type myTableType = assembly.GetType("Tables.Tablename");
var finalResult = Activator.CreateInstance(myTableType);

Related

Using Roslyn to create partial classes - one compiled at runtime

I have a class (HelloWorld.cs):
public partial class HelloWorld
{
public void SayHello()
{
var message = "Hello, World!";
var length = message.Length;
Console.WriteLine("{1} {0}", message, length);
}
}
The above class the property BuildAction = Compile.
I have another class in a separate file (HelloWorldExtend.cs):
public partial class HelloWorld
{
public void SayHelloExtend()
{
var message = "Hello, World Extended!";
var length = message.Length;
Console.WriteLine("{1} {0}", message, length);
}
}
But the properties of the class are: BuildAction = None and Copy to output directory = Copy if newer
Now the main method:
Its using Roslyn.
static void Main(string[] args)
{
var code = File.ReadAllText("HelloWorldExtend.cs");
var tree = SyntaxFactory.ParseSyntaxTree(code);
var compilation = CreateCompilation(tree);
var model = compilation.GetSemanticModel(tree);
ExecuteCode(compilation);
Console.ReadLine();
}
private static void ExecuteCode(CSharpCompilation compilation)
{
using (var stream = new MemoryStream())
{
compilation.Emit(stream);
var assembly = Assembly.Load(stream.GetBuffer());
var type = assembly.GetType("HelloWorld");
var greeter = Activator.CreateInstance(type);
var methodextend = type.GetMethod("SayHelloExtend");
methodextend.Invoke(HelloWorld, null);
//Works perfect
var method = type.GetMethod("SayHello");
method.Invoke(greeter, null);
//method is returned null and gives an error : {"Object reference
not set to an instance of an object."}
}
}
IS it possible to use roslyn to give the same effect as a regular partial class to an existing class where one class is compiled during build and another is compiled at runtime in the same assembly.
Short answer: No.
The original assembly had already been compiled. The class definition for HelloWorld is already converted to IL and at compile time there was no additional source file to make up the other parts of the partial class.
You can create a new assembly containing its own version of HelloWorld by supplying it both parts of the partial file as source.
However
It looks like you may be able to simply extend the original class and optionally make the currently compiled class an abstract class.
public abstract class HelloWorldBase
{
public void SayHello()
{
var message = "Hello, World!";
var length = message.Length;
Console.WriteLine("{1} {0}", message, length);
}
}
Set above class the property BuildAction = Compile.
public class HelloWorld : HelloWorldBase
{
public void SayHelloExtend()
{
var message = "Hello, World Extended!";
var length = message.Length;
Console.WriteLine("{1} {0}", message, length);
}
}
Make sure that as part of your compilation, you reference the assembly containing HelloWorldBase before actually compiling the sources:
compilation.AddReferences(new MetadataFileReference(typeof(HelloWorldBase).Assembly.location));
That should work.
No, as indicated in this answer, partial classes are a "purely language feature". On the CLR level, there is only one class. Since Roslyn will eventually just emit an assembly, you cannot "amend" your class like that.
In fact, there is no such think as partial classes. The real language feature is partial class definitions.
As you can see on the documentation:
It is possible to split the definition of a class or a struct, an interface or a method over two or more source files. Each source file contains a section of the type or method definition, and all parts are combined when the application is compiled.

Reflection using generics and late-binding. How to cast at run-time?

I am trying to use Generics with Reflection in c# to build a method that can handle multiple classes. I use a 3rd-party DLL that has a bunch of classes and on those classes, there is a method I call. They all return different return types, but I do the same processing once I get the object back (in my example below, that would be AreaA and AreaB).
Basically, I want to develop a method that takes in the class name and the expected return type as Generic variables and then calls the correct method (methodName) which is supplied as a parameter to this method.
The program below compiles fine and runs without error, but the issue is the expected type of the 'area' variable. In the below statements, the first line is type casted to (TArea), and if I hover over it In Visual Studio, the intellisense shows the property 'name', but typing area.name doesn't give me the value. I have to type ((AreaA)area).name.
Problem is the type 'AreaA' could be another type at run-time. In this example, 'AreaB' so I can hard-code a cast.
How can I accomplish casting the 'area' variable to the appropriate type allowing me to see the public methods/properties of the 3rd-parties class?
NOTE: In my example, eveything is in the same class, but in reality the definitions for ServiceA, ServiceB, AreaA and AreaB will be in a 3rd party DLL.
As always, thanks in advance!
Fig 1 - 'area' variable can only get 'name' property if casted to 'AreaA'
area = (TArea)dfpMethod.Invoke(instance, new object[] { "Area123" });
AreaA areaa = (AreaA)dfpMethod.Invoke(instance, new object[] { "Area123" });
Fig 2. - Complete Program
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Reflection;
using System.IO;
namespace ReflectionExample
{
class Sample
{
class ServiceA
{
public int size {get; set;}
public string name {get; set;}
public ServiceA()
{
name = "TestA";
size = 100;
}
public AreaA doWork(string name)
{
return new AreaA(name);
}
}
class AreaA
{
public string name { get; set;}
public AreaA(string name)
{
this.name = name;
}
public AreaA()
{
}
}
class ServiceB
{
public int size { get; set; }
public string name { get; set; }
public ServiceB()
{
name = "TestB";
size = 50;
}
public AreaB doWork(string name)
{
return new AreaB(name);
}
}
class AreaB
{
public string name { get; set; }
public AreaB(string name)
{
this.name = name;
}
public AreaB()
{
}
}
static void Main(string[] args)
{
runService<ServiceA, AreaA>("doWork");
}
private static void runService<TService, TArea>(string methodName)
where TService : class, new()
where TArea : class, new()
{
//Compile time processing
Type areaType = typeof(TArea);
Type serviceType = typeof(TService);
//Print the full assembly name and qualified assembly name
Console.WriteLine("AreaType--Full assembly name:\t {0}.", areaType.Assembly.FullName.ToString()); // Print the full assembly name.
Console.WriteLine("AreaType--Qualified assembly name:\t {0}.", areaType.AssemblyQualifiedName.ToString()); // Print the qualified assembly name.
Console.WriteLine("ServiceType--Full assembly name:\t {0}.", serviceType.Assembly.FullName.ToString()); // Print the full assembly name.
Console.WriteLine("ServiceType--Qualified assembly name:\t {0}.", serviceType.AssemblyQualifiedName.ToString()); // Print the qualified assembly name.
//This is done because in my code, the assembly doesn't reside in the executiy assembly, it is only setup as a reference
var assembly = Assembly.Load(serviceType.Assembly.FullName);
//Initialize the generic area
TArea area = default(TArea);
//Get an instance of the service so I can invoke the method later on
var instance = Activator.CreateInstance(serviceType);
//Get the methodInfo for the methodName supplied to the runService method
MethodInfo dfpMethod = serviceType.GetMethod(methodName, BindingFlags.Public | BindingFlags.Instance);
//area is type casted to (TArea), the intellisense shows the property 'name', but typing area.name doesn't give me the value
//I have to type ((AreaA)area).name. Problem is the type 'AreaA' could be another type. In this example, 'AreaB'
area = (TArea)dfpMethod.Invoke(instance, new object[] { "Area123" });
AreaA areaa = (AreaA)dfpMethod.Invoke(instance, new object[] { "Area123" });
Console.WriteLine();
}
}
}
The source of your error is that you're casting all of your return values to type TArea with the statement:
TArea area = (TArea)dfpMethod.Invoke(instance, new object[] { "Area123" });
Per your generic specification, the only thing promised to you by the type TArea is that its a class. Therefore, TArea doesn't give you access to anything but members of the 'object' type.
Instead, do away with the TArea generic argument in favor of using the 'dynamic' keyword:
var area = (dynamic)dfpMethod.Invoke(instance, new object[] { "Area123" });
return area.name; // no error
Note, this is only relevant if the actual types AreaA and AreaB are defined in third party libraries (as you say) and you can't modify them. If you can't modify the classes, you can't introduce an interface (which is what you really need here). If you can't introduce an interface, but all the types expose identical signatures, you can assume the existence of the relevant members using the dynamic type.
If you need to do a lot of work with AreaA/AreaB and you don't want the performance overhead of all the dynamic operations, define your own generalized class that exposes all the signatures you need:
public class MyGenericArea
{
public MyGenericArea(string name)
{
this.Name = name;
}
public string Name {get; set;}
}
Then populate the class using the dynamic casting and return that class type instead:
var area = (dynamic)dfpMethod.Invoke(instance, new object[] { "Area123" });
return new MyGenericArea(area.name);
I think the problem you will have here is mixing dynamically loaded types (Assembly.Load()) with types that are referenced directly from your project and you can see in intellisense, i.e. AreaA.
If you are dynamically loading entire assemblies using reflection, intellisense will do nothing to help you view the class members, as that information needs to be known at compile time, and by definition you are loading assemblies at runtime.
If you just want to view a list of all of the public properties available to your type, then you can use this:
var areaProperties = area.GetType().GetProperties();
But again, this is all done at run time so it wont help you when writing code.
You can dynamically read the value of the "name" property using:
var nameValue = area.GetType().GetProperty("name").GetValue(area);
Essentially, if you want intellisense, reference the dll's directly from your Visual Studio project rather than using Assembly.Load().
Hope that helps.

sending anonymous type to IronPython from C# throws MissingMemberException

The below code throws a 'MissingMemberException'
ScriptEngine engine = Python.CreateEngine();
ScriptRuntime runtime = engine.Runtime;
ScriptScope scope = runtime.CreateScope();
string code = "emp.Name==\"Bernie\"";
ScriptSource source =
engine.CreateScriptSourceFromString(code, SourceCodeKind.Expression);
var emp = new {Name = "Bernie"};
scope.SetVariable("emp", emp);
var res = (double)source.Execute(scope);
if I declare a type called 'Employee' and give it a member 'Name', and use this instead:
var emp = new Employee {Name = "Bernie"}
It works just as expected. Does anyone know why it doesn't work on anonymous types and is there a workaround?
Your problem is that anonymous types are internal. When the complier generates an anonymous type, it is approximately this:
internal class <>f__AnonymousType0`1'<'<Name>j__TPar'> //or whatever silly name the compiler uses
{
public string Name {get;set;}
}
You can replicate the error you are getting with a concrete class by changing it to be internal:
internal class Employee
{
public string Name { get; set; }
}
OK, so that's why it's happening. How do you fix it? Well, the best approach is to just use a concrete class that is public like you have already found.

C#: Dynamically instantiate different classes in the same statement?

Here is a simplified version of what I'm trying to do:
Without having multiple if..else clauses and switch blocks, can I mimic the behavior of Javascript's eval() shudder to instantiate a class in C#?
// Determine report orientation -- Portrait or Landscape
// There are 2 differently styled reports (beyond paper orientation)
string reportType = "Portrait";
GenericReport report;
report = new eval(reportType + "Report()"); // Resolves to PortraitReport()
The need stems from the fact that I have 6 types of Crystal Reports (that do the same thing, but look drastically different) for 50 states. There are 3 styles each, rather than entertain the notion of a giant switch block with nested if..else statements determining which of 900 reports to use, I was hoping for an eval-like solution.
You could use Activator.CreateInstance("myAssembly", "PortrainReport");. Although the more readable way would be to create a Portrait Factory, which would create the correct type for you.
As people specified above, you can use Activator class to create an instance of the class by its text name.
But, there is one more option.
When you told about using eval like function in c# i assumed, you not only want to create an instance of the class by its text name, but also fill it with properties from the same string.
For this purpose you need to use deserialization.
Deserialization converts string like representation of the class into its instance and restoring all its properties that was specified in the string.
Xml serialization. Its using XML file for converting into instance.
Here is small example:
public class Report1
{
public string Orientation {get;set;}
public string ReportParameter1 {get;set;}
public string ReportParameter2 {get;set;}
}
Above is the class that you want to instantiate and fill with parameters by string line.
Below is XML that can do that:
<?xml version="1.0"?>
<Report1>
<Orientation>Landscape</Orientation>
<ReportParameter1>Page1</ReportParameter1>
<ReportParameter2>Colorado</ReportParameter2>
</Report1>
To create an instance from the file use System.Xml.Serialization.XmlSerializer :
string xml = #"<?xml version=""1.0""?>
<Report1>
<Orientation>Landscape</Orientation>
<ReportParameter1>Page1</ReportParameter1>
<ReportParameter2>Colorado</ReportParameter2>
</Report1>";
///Create stream for serializer and put there our xml
MemoryStream str = new MemoryStream(ASCIIEncoding.ASCII.GetBytes(xml));
///Getting type that we are expecting. We are doing it by passing proper namespace and class name string
Type expectingType = Assembly.GetExecutingAssembly().GetType("ConsoleApplication1.Report1");
XmlSerializer ser = new XmlSerializer(expectingType);
///Deserializing the xml into the object
object obj = ser.Deserialize(str);
///Now we have our report instance initialized
Report1 report = obj as Report1;
In this way you can prepare appropriate xml as string concatenation. That xml will contain all parameters for your report.
Then, you can convert it into the proper type.
Look at the Activator create instance method
All the classes will need to adhere to an interface. Then make an Generic Method which will be your eval and requires that interface. Here is an example of this (call the Usage static to see it in action):
public interface IOperation
{
string OutputDirection { get; set; }
};
public class MyOperation: IOperation
{
public string OutputDirection { get; set; }
}
public static class EvalExample
{
public static T Eval<T>( string direction ) where T : IOperation
{
T target = (T) Activator.CreateInstance( typeof( T ) );
target.OutputDirection = direction;
return target;
}
// Example only
public static void Usage()
{
MyOperation mv = Eval<MyOperation>( "Horizontal" );
Console.WriteLine( mv.OutputDirection ); // Horizontal
}
}
Using the factory pattern, and reflection (as explained in this blog post), you would get:
static void Main(string[] args)
{
ReportFactory<Report> factory = new ReportFactory<Report>();
Report r1 = factory.CreateObject("LandscapeReport");
Report r2 = factory.CreateObject("PortraitReport");
Console.WriteLine(r1.WhoAmI());
Console.WriteLine(r2.WhoAmI());
}
Which would output "Landscape" and "Portrait", respectivley.
Of course, for the plumbing, you need an interface that all your reports are based off of (which I assume you already have).
For this example:
public interface Report
{
string WhoAmI();
}
And the two implemenations:
public class PortraitReport : Report
{
public string WhoAmI()
{
return "Portrait";
}
}
public class LandscapeReport : Report
{
public string WhoAmI()
{
return "Landscape";
}
}
The secret is in the ReportFactory, which uses Reflection to see what other classes are based on Report, and automatically register them for use, which I think is pretty cool:
public class ReportFactory<Report>
{
private Dictionary<string, Type> reportMap = new Dictionary<string, Type>();
public ReportFactory()
{
Type[] reportTypes = Assembly.GetAssembly(typeof(Report)).GetTypes();
foreach (Type reportType in reportTypes)
{
if (!typeof(Report).IsAssignableFrom(reportType) || reportType == typeof(Report))
{
// reportType is not derived from Report
continue;
}
reportMap.Add(reportType.Name, reportType);
}
}
public Report CreateObject(string ReportName, params object[] args)
{
return (Report)Activator.CreateInstance(reportMap[ReportName], args);
}
}
So now all you have to do is just add any new implementations of Report in your assembly, and they will be available to the factory with no extra coding or changing other code files.

C# question about GetType of class

I have an assembly asdf.dll and it has a class 'Class1'.
How can I get the type of Class1?
string a = "Class1"; //Class1 is the name of class in asdf.dll
string typeString = typeof(Class1).FullName; // here I only have the string Class1 and not Class Class1
AssemblyName assemblyName = AssemblyName.GetAssemblyName("asdf.dll");
Type type = Type.GetType(typeString + ", " + assemblyName);
How can I get the type of a class from a string holding the class name?
Type t = Type.GetType("MyDll.MyClass,Mydll")
where MyDll.MyClass is the class Location of your desire class/Form. Mydll is the your dll name. which u want to Call.
typeof(Class1).FullName is already the fully qualified name.
Try just passing that, or using the Type.Name property instead.

Categories