Create Class Dynamically During Runtime - c#

Hi im trying to create a class dending on data gathered from a user input. Once its chosen id like to the create the field names and the data types based on that and fill that class with data from document effectively creating a list of that Class.
Eg I create a class called Class1 and give it 3 Properties : ID , Name , Weight and define there types as int , string , int
Then I want to fill it with data Eg : (Example in json to show structure)
ID:{
1,
2,
3
},
Name:{
A,
B,
c
},
Weight:{
10,
20,
30
}
Ive looked into Reflection and codeDom which both enable for me to make the Class but i cannot work out how to write to that new classes properties.
Code for codeDom Version:
string className = "BlogPost";
var props = new Dictionary<string, Type>() {
{ "Title", typeof(string) },
{ "Text", typeof(string) },
{ "Tags", typeof(string[]) }
};
createType(className, props);
I Create The Properties and their Types
static void createType(string name, IDictionary<string, Type> props)
{
var csc = new CSharpCodeProvider(new Dictionary<string, string>() { { "CompilerVersion", "v4.0" } });
var parameters = new CompilerParameters(new[] { "mscorlib.dll", "System.Core.dll" }, "Test.Dynamic.dll", false);
parameters.GenerateExecutable = false;
var compileUnit = new CodeCompileUnit();
var ns = new CodeNamespace("Test.Dynamic");
compileUnit.Namespaces.Add(ns);
ns.Imports.Add(new CodeNamespaceImport("System"));
var classType = new CodeTypeDeclaration(name);
classType.Attributes = MemberAttributes.Public;
ns.Types.Add(classType);
foreach (var prop in props)
{
var fieldName = "_" + prop.Key;
var field = new CodeMemberField(prop.Value, fieldName);
classType.Members.Add(field);
var property = new CodeMemberProperty();
property.Attributes = MemberAttributes.Public | MemberAttributes.Final;
property.Type = new CodeTypeReference(prop.Value);
property.Name = prop.Key;
property.GetStatements.Add(new CodeMethodReturnStatement(new CodeFieldReferenceExpression(new CodeThisReferenceExpression(), fieldName)));
property.SetStatements.Add(new CodeAssignStatement(new CodeFieldReferenceExpression(new CodeThisReferenceExpression(), fieldName), new CodePropertySetValueReferenceExpression()));
classType.Members.Add(property);
}
var results = csc.CompileAssemblyFromDom(parameters, compileUnit);
results.Errors.Cast<CompilerError>().ToList().ForEach(error => Console.WriteLine(error.ErrorText));
}
This is just code i found elsewhere but if this where the code i wanted id do something like
var a = new List<BlogPost>()
and then
a."Property1" = "Title 1"
Hope this is informative

You could use reflection.
Main method to create desired object and populate its properties:
public object GenerateObject(string fullyQualifiedClassName,
Dictionary<string, object> nameToValueMap)
{
var actualObject = GetInstance(fullyQualifiedClassName);
if (actualObject == null)
return actualObject;
foreach (var prop in nameToValueMap)
{
SetPropValue(actualObject, prop.Key, prop.Value);
}
return actualObject;
}
Method to create instance of the desired class, based on fully qualified class name:
public object GetInstance(string fullyQualifiedName)
{
Type type = Type.GetType(fullyQualifiedName);
if (type != null)
return Activator.CreateInstance(type);
foreach (var asm in AppDomain.CurrentDomain.GetAssemblies())
{
type = asm.GetType(fullyQualifiedName);
if (type != null)
return Activator.CreateInstance(type);
}
return null;
}
And last but not least, method to set property's value:
public bool SetPropValue<T>(T obj, string propName, object val)
{
if (string.IsNullOrEmpty(propName)) return false;
var prop = obj?.GetType()
.GetProperties()?
.FirstOrDefault(m => m.Name == propName);
if (prop != null)
{
prop.SetValue(obj, val);
return true;
}
return false;
}

Why not use dynamic object using expandoObject?
something like:
dynamic blogPost = new System.Dynamic.ExpandoObject();
blogPost.Tile = "Mary Water";
blogPost.Text= "your text here";

Related

C# CodeProvider Error: Constructor on type not found

I get the error: Constructor on type 'SimpleScript.Generator' not found.
I tried passing the correct parameters but i still get this error, this is my source code, and the script is a very simple piece of code that generates the Array of Element head and body. And also it is compiled successfully but it throws the error at the execution line.
string source = #"
using System;
using MSiteDLL;
namespace SimpleScript
{
public static class Generator
{
public static Document Generate(Data server)
{
"+script+ #"
Block[] blocks = {
new Block(""head"", head),
new Block(""body"", body),
};
return new Document(blocks);
}
}
}
";
Dictionary<string, string> providerOptions = new Dictionary<string, string>
{
{"CompilerVersion", "v4.0"}
};
CSharpCodeProvider provider = new CSharpCodeProvider(providerOptions);
CompilerParameters compilerParams = new CompilerParameters
{
GenerateInMemory = true,
GenerateExecutable = false,
ReferencedAssemblies = {
"System.dll",
"System.Core.dll",
"MSiteDLL.dll",
}
};
CompilerResults results = provider.CompileAssemblyFromSource(compilerParams, source);
if (results.Errors.Count != 0)
{
string output = "";
foreach (CompilerError y in results.Errors)
{
output += y.ErrorText + Environment.NewLine;
}
throw new Exception("Compile failed:" + output);
}
object o = results.CompiledAssembly.CreateInstance("SimpleScript.Generator");
MethodInfo mi = o.GetType().GetMethod("Generate");
Data[] parametersArray = new Data[] { server };
Document x = (Document)mi.Invoke(o, parametersArray);
return x;
Since your class is static, you should invoke the method in a static way.
So first, remove this line:
object o = results.CompiledAssembly.CreateInstance("SimpleScript.Generator");
And use those to invoke:
MethodInfo mi = Type.GetType("SimpleScript.Generator").GetMethod("Generate");
Data[] parametersArray = new Data[] { server };
Document x = (Document)mi.Invoke(null, parametersArray);

How to select multiple column from data base by getting column from user in .net

I have an object in my database, i.e. with 10 attributes.
Now I want to let the user select some of them (1 or 2 up to 10 of them) and then according by user's selection I make a list of object with the attributes selected by user
the scenario that I think about is this:
A page with check boxes that shows the attributes(columns) of that abject then user selects each of them he needs.
But here is my problem, how to make the selected check boxes run as query?
For example user selected col 1 , col 2, col 6 , col 10, how can I write a query responsible for user selection?
Example I wanna the meaningful phrase of this:
var file2 = file.Select(f => new { "attributes selected by user" }).OrderBy(what user wants)
they System.Linq.Dynamic library on Nuget is a way to go
[TestMethod]
public void StringyAndDangerous()
{
var fakePersonDbSet = new List<Person> { new Person() { FirstName = "Some", LastName = "Guy" } }.AsQueryable();
var attributes = new string[] { "FirstName", "LastName" };
var selectedFields = String.Join(",", attributes);
var exprssion = string.Format("new ({0})", selectedFields);
var result = fakePersonDbSet.Select(exprssion, attributes).Cast<dynamic>().First();
}
but you loose type safety and compile time checking. You might be better taking another approach
[TestMethod]
public void SlowerButSafer()
{
var fakePersonDbSet = new List<Person> { new Person() { FirstName = "Some", LastName = "Guy" } }.AsQueryable();
var attributes = new string[] { "FirstName", "LastName" };
var personPropertylist = CovertToKeyValuePair(fakePersonDbSet.First())
.Where(c=> attributes.Contains(c.Key))
.ToArray();
}
private IEnumerable<KeyValuePair<string, object>> CovertToKeyValuePair<T>(T #object)
{
var result = new List<KeyValuePair<string, object>>();
var properties = typeof (T).GetProperties();
foreach (var property in properties)
{
result.Add(new KeyValuePair<string, object>(property.Name, property.GetValue(#object, null)));
}
return result;
}
you'll take a performance hit both for pulling fields from the database that you don't need and for using reflection but the code will be less error prone and you won't end up with errors for trying to select columns that don't exist.
Use DynamicLinq. (link)
Extension methods:
public static T GetValue<T>(this DynamicClass dynamicObject, string propName)
{
if (dynamicObject == null)
{
throw new ArgumentNullException("dynamicObject");
}
var type = dynamicObject.GetType();
var props = type.GetProperties(BindingFlags.Public
| BindingFlags.Instance
| BindingFlags.FlattenHierarchy);
var prop = props.FirstOrDefault(property => property.Name == propName);
if (prop == null)
{
throw new InvalidOperationException("Specified property doesn't exist.");
}
return (T)prop.GetValue(dynamicObject, null);
}
public static string ToDynamicSelector(this IList<string> propNames)
{
if (!propNames.Any())
throw new ArgumentException("You need supply at least one property");
return string.Format("new({0})", string.Join(",", propNames));
}
Usage:
using System.Linq.Dynamic;
// ..
var columns = new[] { "col1", "col2", etc };
var result = context.Files.OrderBy(file => file.Id)
.Select(columns.ToDynamicSelector())
.Cast<DynamicClass>.ToList();
Result will be the collecion of DynamiClass instances wchich columns will contain selected properties.
To get single property from DynamicClass:
var columnValue = result.First().GetValue<string>("col1");
If you want to get values from IEnumerable:
var list = new List<File> { File1, File2, etc.. };
var result = list.AsQueryable().Select( /* the same as above */);

Generate POCO from dynamic in C#

Is there anything built into .NET 4.5 that will generate a string C# POCO from a dynamic with all auto-implemented properties?
If not, is there anything built into .NET that will give you (something like a) List<KeyValuePair<string, Type>> so that we can generate a POCO according to the pseudo-code:
foreach (var kvp in list)
{
builder.AppendFormat("public {0} {1} {{ get; set; }}", kvp.Value, kvp.Key);
}
Finally, are there any well-known libraries that can assist with this sort of very basic code generation?
You can use compileassemblyfromsource to compile your string,
http://msdn.microsoft.com/en-us/library/system.codedom.compiler.codedomprovider.compileassemblyfromsource(v=vs.110).aspx
var csc = new CSharpCodeProvider(new Dictionary<string, string>() { { "CompilerVersion", "v4.0" } });
var cp = new CompilerParameters()
{
GenerateExecutable = false,
GenerateInMemory = true
};
cp.ReferencedAssemblies.Add("mscorlib.dll");
cp.ReferencedAssemblies.Add("System.dll");
cp.ReferencedAssemblies.Add("System.Core.dll");
// The string can contain any valid c# code
// A valid class need to be created with its own properties.
var s = "public class POCOClass{ public int ID {get {return 1;}} }";
// "results" will usually contain very detailed error messages
var results = csc.CompileAssemblyFromSource(cp, s);
var type = results.CompiledAssembly.GetType("POCOClass");
var obj = (dynamic)Activator.CreateInstance(type);
var output = obj.ID;
// or
var output_ = obj.GetType().GetProperty("ID").GetValue(obj, null);
You need to define a class for your properties such as called POCOClass.
EDIT:
public static T CopyObjectFromExpando<T>(this object s) where T : class
{
var source = (ExpandoObject)s;
// Might as well take care of null references early.
if (source == null)
{
throw new ArgumentNullException("s");
}
var propertyMap = typeof(T).GetProperties().ToDictionary(p => p.Name.ToLowerInvariant(), p => p);
var destination = Activator.CreateInstance<T>();
// By iterating the KeyValuePair<string, object> of
// source we can avoid manually searching the keys of
// source as we see in your original code.
foreach (var kv in source)
{
PropertyInfo p;
if (propertyMap.TryGetValue(kv.Key.ToLowerInvariant(), out p))
{
var propType = p.PropertyType;
if (kv.Value == null)
{
if (!propType.IsNullable() && propType != typeof(string))
{
// Throw if type is a value type
// but not Nullable<>
throw new ArgumentException("not nullable");
}
}
else if (propType.IsEnum)
{
var enumvalue = Enum.ToObject(propType, kv.Value);
p.SetValue(destination, enumvalue, null);
continue;
}
else if (propType == typeof(bool) && kv.Value.GetType() != typeof(bool))
{
var boolvalue = Convert.ToBoolean(kv.Value);
p.SetValue(destination, boolvalue, null);
continue;
}
else if (propType.IsNullable())
{
var nullType = Nullable.GetUnderlyingType(propType);
var value = Convert.ChangeType(kv.Value, nullType);
p.SetValue(destination, value, null);
continue;
}
else if (kv.Value.GetType() != propType)
{
// You could make this a bit less strict
// but I don't recommend it.
throw new ArgumentException("type mismatch");
}
p.SetValue(destination, kv.Value, null);
}
}
return destination;
}
ImpromptuInterface, open source on Nuget
PM> Install-Package ImpromptuInterface
Has ActLikeProperties
by using ImpromptuInterface
Impromput.ActLikeProperties(dynObj, list.ToDictionary(k=>k.Key,v=>v.Value))
This emits a poco dlr proxy around the dynObj
The intent was to be able to bridge simple dynamic objects (like expando) to old code that uses reflection. But generally it's better to do what ImpromptuInterface's main function, which is wrap dynamic objects with statically declared interfaces.

Using a PropertyGrid to input method parameters

I'd like to use a PropertyGrid to input method parameters.
I have some application that will dynamically load user's DLLs and invoke methods with specific signature (a known return type).
I'd like to present the user the option to input the arguments to the called method easily with a PropertyGrid control.
Problem is -- PropertyGrid works on an Object, and not on a method.
I'd like to somehow "transform" the method at runtime into an object with properties reflecting its arguments, passing the input values to the method when invoking it.
Offcourse i'd like to have type validation, etc (if provided by the PropertyGrid, dont remember right now).
Is there any easy solution for this?
Thanks!
Well here is what I've written yesterday.
It is meant to be run in LinqPad, which is an awesome free tool to test linq queries or code snippets. (With an inexpensive upgrade to get intellisense)
The code should tell you how to deal with different kind of parameters (ref, out) and whether you are calling an instance method or not. (flip the comments in Main to test an instance method)
In LinqPad, you can use the Dump() extension method to let it show your objects in the results window. this is handy to see what is actually happening.
So, if you want to know how to dynamically construct a type and invoke it, this should get you started:
EDIT: I totally forgot to mention, that you do need to add these 2 namespaces to the query. You do that by hitting F4->additional namespace imports and adding these 2:
System.CodeDom.Compiler
System.CodeDom
public static String TestMethod1(int a, ref int X, out string t)
{
a += X;
X = a * 2;
t = "...>" + (X + a);
return a.ToString() + "...";
}
public class TestClass
{
public int SomeMethod(int a, DateTime? xyz)
{
if(xyz != null)
a+= xyz.GetValueOrDefault().Day;
return 12 + a;
}
}
void Main()
{
var sb = new StringBuilder();
var methodInfo = typeof(UserQuery).GetMethod("TestMethod1");
dynamic instance = CreateWrapper(methodInfo, sb);
instance.a = 11;
instance.X = 2;
instance.CallMethod();
/*
var methodInfo = typeof(TestClass).GetMethod("SomeMethod");
dynamic instance = CreateWrapper(methodInfo, sb);
instance.a = 11;
instance.xyz = new DateTime(2010, 1, 2);
instance.CallMethod(new TestClass());
*/
((Object)instance).Dump();
sb.ToString().Dump();
}
static object CreateWrapper(MethodInfo methodInfo, StringBuilder sb)
{
// pick either C#, VB or another language that can handle generics
var codeDom = CodeDomProvider.CreateProvider("C#");
var unit = new CodeCompileUnit();
var codeNameSpace = new CodeNamespace();
codeNameSpace.Name = "YourNamespace";
var wrapperType = AddWrapperType(codeDom, codeNameSpace, methodInfo, "WrapperType", "MethodResultValue");
unit.Namespaces.Add(codeNameSpace);
// this is only needed so that LinqPad can dump the code
codeDom.GenerateCodeFromNamespace(codeNameSpace, new StringWriter(sb), new CodeGeneratorOptions());
// put the temp assembly in LinqPad's temp folder
var outputFileName = Path.Combine(Path.GetDirectoryName(new Uri(typeof(UserQuery).Assembly.CodeBase).AbsolutePath),
Guid.NewGuid() + ".dll");
var results = codeDom.CompileAssemblyFromDom(new CompilerParameters(new[]{new Uri(methodInfo.DeclaringType.Assembly.CodeBase).AbsolutePath,
new Uri(typeof(UserQuery).Assembly.CodeBase).AbsolutePath,
new Uri(typeof(UserQuery).BaseType.Assembly.CodeBase).AbsolutePath}.Distinct().ToArray(),
outputFileName),
unit);
results.Errors.Dump();
new Uri(results.CompiledAssembly.CodeBase).AbsolutePath.Dump();
if(results.Errors.Count == 0)
{
var compiledType = results.CompiledAssembly.GetType(codeNameSpace.Name + "." + wrapperType.Name);
return Activator.CreateInstance(compiledType);
}
return null;
}
static CodeTypeDeclaration AddWrapperType(CodeDomProvider codeDom,
CodeNamespace codeNameSpace,
MethodInfo methodInfo,
string typeName,
string resultPropertyName)
{
var parameters = (from parameter in methodInfo.GetParameters()
select parameter).ToList();
var returnValue = methodInfo.ReturnType;
if(!String.IsNullOrEmpty(methodInfo.DeclaringType.Namespace))
codeNameSpace.Imports.Add(new CodeNamespaceImport(methodInfo.DeclaringType.Namespace));
var wrapperType = new CodeTypeDeclaration(typeName);
var defaultAttributes = MemberAttributes.Public | MemberAttributes.Final;
var thisRef = new CodeThisReferenceExpression();
Func<Type, Type> getRealType = t => t.IsByRef || t.IsPointer ? t.GetElementType(): t;
Func<String, String> getFieldName = parameterName => "m_" + parameterName + "_Field";
Action<ParameterInfo> addProperty = p =>
{
var realType = getRealType(p.ParameterType);
var usedName = p.Position == -1 ? resultPropertyName : p.Name;
wrapperType.Members.Add(new CodeMemberField
{
Name = getFieldName(usedName),
Type = new CodeTypeReference(realType),
Attributes= MemberAttributes.Private
});
var property = new CodeMemberProperty
{
Name = usedName,
Type = new CodeTypeReference(realType),
Attributes= defaultAttributes
};
property.GetStatements.Add(new CodeMethodReturnStatement(new CodeFieldReferenceExpression(thisRef,
getFieldName(usedName))));
property.SetStatements.Add(new CodeAssignStatement(new CodeFieldReferenceExpression(thisRef, getFieldName(usedName)),
new CodeArgumentReferenceExpression("value")));
wrapperType.Members.Add(property);
};
parameters.ForEach(addProperty);
if(methodInfo.ReturnParameter != null)
{
addProperty(methodInfo.ReturnParameter);
}
var callMethod = new CodeMemberMethod
{
Name="CallMethod",
Attributes=defaultAttributes
};
CodeMethodInvokeExpression invokeExpr;
if(!methodInfo.IsStatic)
{
callMethod.Parameters.Add(new CodeParameterDeclarationExpression(methodInfo.DeclaringType,
"instance"));
invokeExpr = new CodeMethodInvokeExpression(new CodeArgumentReferenceExpression("instance"),
methodInfo.Name);
}
else
invokeExpr = new CodeMethodInvokeExpression(new CodeTypeReferenceExpression(methodInfo.DeclaringType), methodInfo.Name);
foreach(var parameter in parameters)
{
CodeExpression fieldExpression = new CodeFieldReferenceExpression(thisRef,
getFieldName(parameter.Name));
if(parameter.ParameterType.IsByRef && !parameter.IsOut)
fieldExpression = new CodeDirectionExpression(FieldDirection.Ref, fieldExpression);
else if(parameter.IsOut)
fieldExpression = new CodeDirectionExpression(FieldDirection.Out, fieldExpression);
else if(parameter.IsIn)
fieldExpression = new CodeDirectionExpression(FieldDirection.In, fieldExpression);
invokeExpr.Parameters.Add(fieldExpression);
}
wrapperType.Members.Add(callMethod);
if(returnValue != typeof(void))
callMethod.Statements.Add(new CodeAssignStatement(new CodeFieldReferenceExpression(thisRef,
getFieldName(resultPropertyName)),
invokeExpr));
else
callMethod.Statements.Add(invokeExpr);
codeNameSpace.Types.Add(wrapperType);
return wrapperType;
}
I think you could add a new class to your project that implement the ICustomTypeDescriptor interface. And use the instance of this class as the wrapper of your method parameters.
Here is an article shows how to custom property grid display by implementing ICustomTypeDescriptor.

Using Reflection.Emit to copy a custom attribute to another method

I am trying to generate a new set of wcf interfaces based on existing interfaces.
I am using the Reflection.Emit namespace to accomplish this. My problem is how to copy the old custom attributes from one method to the new method. Every example I have seen of SetCustomAttributes() requires knowing the attribute type beforehand. I need to discover the attribute type at runtime. Any thoughts?
The answer you (frjames) posted is close, but doesn't account for property initializers like...
[ServiceBehavior(Name="ServiceName")]
However, the idea of converting CustomAttributeData to a CustomAttributeBuilder for use in Reflection.Emit is right on.
I ended up having to do this for an open source project (Autofac) and came up with this extension method:
public static CustomAttributeBuilder ToAttributeBuilder(this CustomAttributeData data)
{
if (data == null)
{
throw new ArgumentNullException("data");
}
var constructorArguments = new List<object>();
foreach (var ctorArg in data.ConstructorArguments)
{
constructorArguments.Add(ctorArg.Value);
}
var propertyArguments = new List<PropertyInfo>();
var propertyArgumentValues = new List<object>();
var fieldArguments = new List<FieldInfo>();
var fieldArgumentValues = new List<object>();
foreach (var namedArg in data.NamedArguments)
{
var fi = namedArg.MemberInfo as FieldInfo;
var pi = namedArg.MemberInfo as PropertyInfo;
if (fi != null)
{
fieldArguments.Add(fi);
fieldArgumentValues.Add(namedArg.TypedValue.Value);
}
else if (pi != null)
{
propertyArguments.Add(pi);
propertyArgumentValues.Add(namedArg.TypedValue.Value);
}
}
return new CustomAttributeBuilder(
data.Constructor,
constructorArguments.ToArray(),
propertyArguments.ToArray(),
propertyArgumentValues.ToArray(),
fieldArguments.ToArray(),
fieldArgumentValues.ToArray());
}
That one accounts for all the ways to initialize the attribute.
Here is the answer I came up with after some more research.
CustomAttributeBuilder ct = AddAttributesToMemberInfo(methodInfo);
if (ct != null)
{
methodBuilder.SetCustomAttribute(ct);
}
CustomAttributeBuilder AddAttributesToMemberInfo(MemberInfo oldMember)
{
CustomAttributeBuilder ct = null;
IList<CustomAttributeData> customMethodAttributes = CustomAttributeData.GetCustomAttributes(oldMember);
foreach (CustomAttributeData att in customMethodAttributes)
{
List<object> namedFieldValues = new List<object>();
List<FieldInfo> fields = new List<FieldInfo>();
List<object> constructorArguments = new List<object>();
foreach (CustomAttributeTypedArgument cata in att.ConstructorArguments)
{
constructorArguments.Add(cata.Value);
}
if (att.NamedArguments.Count > 0)
{
FieldInfo[] possibleFields = att.GetType().GetFields();
foreach (CustomAttributeNamedArgument cana in att.NamedArguments)
{
for (int x = 0; x < possibleFields.Length; x++)
{
if (possibleFields[x].Name.CompareTo(cana.MemberInfo.Name) == 0)
{
fields.Add(possibleFields[x]);
namedFieldValues.Add(cana.TypedValue.Value);
}
}
}
}
if (namedFieldValues.Count > 0)
{
ct = new CustomAttributeBuilder(att.Constructor, constructorArguments.ToArray(), fields.ToArray(), namedFieldValues.ToArray());
}
else
{
ct = new CustomAttributeBuilder(att.Constructor, constructorArguments.ToArray());
}
}
return ct;
}
The code from Travis Illig needs amendment as below to work with .Net Core:
foreach (var namedArg in data.NamedArguments)
{
string argName = namedArg.MemberName;
var fi = data.AttributeType.GetField(argName);
var pi = data.AttributeType.GetProperty(argName);
try this:
MethodInfo mi;
//...
object[] custAttribs = mi.GetCustomAttributes(false);
foreach (object attrib in custAttribs)
attrib.GetType();
i assume you have MethodInfo for your methods

Categories