Using Reflection.Emit to copy a custom attribute to another method - c#

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

Related

Create Class Dynamically During Runtime

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";

Get Connected Elements in Autodesk Inventor via API

We have Autodesk Inventor and created a C# Addon.
In this we loop through all Objects like this:
foreach (CurrencyAPIv2.IAssetInstance s in objects)
{
var c = s.NativeObject as ComponentOccurrence;
}
How can we get the connected Object of an Object (the one which is snapped to it). And also the Info to which connector it is connected (snapped)
Michael Navara:
Can you be more specific? CurrencyAPIv2.IAssetInstance is not a member of Inventor API
using CurrencyAPIv2 = Autodesk.Factory.PublicAPI.Currency.v2;
We solved it with the XML-files which can also be watched in AttributeHelper:
void LoadConnectors(Document document, List<ConnectionElement> connections, List<ConnectedComponent> components)
{
var am = document.AttributeManager;
var d = new Dictionary<string, string>();
var entities = am.FindObjects("*", "*");
foreach (var e in entities)
{
try
{
dynamic o = e;
foreach (AttributeSet attrS in o.AttributeSets)
{
foreach (Inventor.Attribute a in attrS)
{
d.Add(o.Name, a.Value as string);
}
}
}
catch(Exception)
{ }
}
foreach (var element in d)
{
XmlDocument xmlDoc = new XmlDocument();
xmlDoc.LoadXml(element.Value);
var connectionNodes = xmlDoc.SelectNodes("//ObjectData[#Type='42']");
var componentNodes = xmlDoc.SelectNodes("//ObjectData[#Type='38']");
// Verbindungen
if (connectionNodes.Count > 0)
{
var link1Node = xmlDoc.SelectSingleNode("//ObjectData/Link1") as XmlElement;
var link2Node = xmlDoc.SelectSingleNode("//ObjectData/Link2") as XmlElement;
var connection = new ConnectionElement();
connection.Name = element.Key;
connection.Link1 = link1Node.GetAttribute("VAL");
connection.Link2 = link2Node.GetAttribute("VAL");
connections.Add(connection);
}
// Komponenten
else if (componentNodes.Count > 0)
{
var componentConnectorNodes = xmlDoc.SelectNodes("//ObjectData/Connector");
var componentConnectors = new List<ComponentConnector>();
foreach (XmlNode cc in componentConnectorNodes)
{
var idNode = cc.SelectSingleNode("Id") as XmlElement;
var displayNameNode = cc.SelectSingleNode("DisplayName") as XmlElement;
var connector = new ComponentConnector();
connector.DisplayName = displayNameNode.GetAttribute("VAL");
connector.Id = idNode.GetAttribute("VAL");
componentConnectors.Add(connector);
}
if (components.FirstOrDefault(x => x.Name == element.Key) != null)
{
components.FirstOrDefault(x => x.Name == element.Key).Connectors.AddRange(componentConnectors);
}
else
{
var component = new ConnectedComponent();
component.Name = element.Key;
component.Connectors = new List<ComponentConnector>();
component.Connectors.AddRange(componentConnectors);
components.Add(component);
}
}
}
}

Convert Hierarchical Dictionary to List<KeyValuePair<Document,File>>()

I have a Dictionary which is like Dictionary<Document, File[ n ]>
i want to convert that Dictionary to List<KeyValuePair<Document, File>>
which is flat system
currently i'm using foreach to convert that list is it possible with Linq?
var documentFileList = RepositoryFactory.FileRepository.GetFiles(_case);
var retVal = new List<KeyValuePair<Document, File>>();
if (documentFileList != null)
{
foreach (var docFileKeyPair in documentFileList)
{
if (docFileKeyPair.Value != null)
{
foreach (var fileEntity in docFileKeyPair.Value)
{
var document = docFileKeyPair.Key as Document;
if (document != null)
{
retVal.Add(new KeyValuePair<Document, File>(document, fileEntity));
}
}
}
}
}
Thanks in advance
using Linq, you could do this.
var flattenList = documentFileList.SelectMany(x=>x.Value.Where(v=> v != null).Select(s=> new KeyValuePair<Document, File>(x.Key, s)))
.ToList();
Check this example code

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.

AWS API: how to cast dynamo result as class

So I have a model repository that utilizes the C# AWS SDK for Dynamo. Right now it's a bit ugly. What I'd like is to cast-out result items to my model. Going into Dynamo it's great. I just do some type reflection on my Poco classes and shove them in like so:
var doc = new Document();
foreach (PropertyInfo prop in model.GetType().GetProperties(BindingFlags.Instance | BindingFlags.Public))
{
var propName = (string)prop.Name;
// dont add if value is null
if (prop.GetValue(model, null) != null)
{
if (prop.PropertyType == typeof(string))
doc[propName] = (string)prop.GetValue(model, null);
if (prop.PropertyType == typeof(List<string>))
doc[propName] = (List<string>)prop.GetValue(model, null);
if (prop.PropertyType == typeof(float))
doc[propName] = (float)prop.GetValue(model, null);
}
}
But here in the repo, I'd like to not have to write this ugly manual cast when retrieving items. Is there a AWS helper to make this less manual? I guess I could write the inverse of the above loop and get the attribute property names then test for null on each N, S, SS type etc.
var request = new ScanRequest
{
TableName = TableName.User,
};
var response = client.Scan(request);
var collection = (from item in response.ScanResult.Items
from att in item
select new User(att.Value.S, att.Value.N, att.Value.S, att.Value.N, att.Value.S, att.Value.S, att.Value.S, att.Value.S, att.Value.S,
att.Value.S, att.Value.S, att.Value.S, att.Value.S, att.Value.SS, att.Value.SS)).ToList();
return collection.AsQueryable();
You can use the built-in FromDocument method to convert the Dictionary<string, AttributeValue> to your class of type T
List<MyClass> result = new List<MyClass>();
var response = await client.QueryAsync(request);
foreach (Dictionary<string, AttributeValue> item in response.Items)
{
var doc = Document.FromAttributeMap(item);
var typedDoc = context.FromDocument<MyClass>(doc);
result.Add(typedDoc);
}
You can use the Object Persistence Model feature of the .NET SDK. This allows you to annotate your .NET objects with attributes that then direct the SDK how that data should be stored in DynamoDB.
I ended up doing it the LOOOOOONG way. It was kind of fun to use type reflection to create my own casting function. Would have been more fun if it weren't at 2 AM.
The Pavel's answer is more official, but this still works like a charm.
public static T ResultItemToClass<T>(Dictionary<string, AttributeValue> resultItem) where T : new()
{
var resultDictionary = new Dictionary<string, object>();
Type type = typeof(T);
T ret = new T();
foreach (KeyValuePair<string, AttributeValue> p in resultItem)
if (p.Value != null)
foreach (PropertyInfo prop in p.Value.GetType().GetProperties(BindingFlags.Instance | BindingFlags.Public))
if (prop.GetValue(p.Value, null) != null)
{
if (prop.Name == "S")
type.GetProperty(p.Key).SetValue(ret, prop.GetValue(p.Value, null), null);
if (prop.Name == "SS")
type.GetProperty(p.Key).SetValue(ret, (List<string>)prop.GetValue(p.Value, null), null);
if (prop.Name == "N")
type.GetProperty(p.Key).SetValue(ret, Convert.ToInt32(prop.GetValue(p.Value, null)), null);
// TODO: add some other types. Too tired tonight
}
return ret;
}
From the aws Document result class, you can use the instance method: .ToJson(), then deserialise into your class.
Generic method convert Dynamo table to c# class as an extension function.
public static List<T> ToMap<T>(this List<Document> item)
{
List<T> model = (List<T>)Activator.CreateInstance(typeof(List<T>));
foreach (Document doc in item)
{
T m = (T)Activator.CreateInstance(typeof(T));
var propTypes = m.GetType();
foreach (var attribute in doc.GetAttributeNames())
{
var property = doc[attribute];
if (property is Primitive)
{
var properties = propTypes.GetProperty(attribute);
if (properties != null)
{
var value = (Primitive)property;
if (value.Type == DynamoDBEntryType.String)
{
properties.SetValue(m, Convert.ToString(value.AsPrimitive().Value));
}
else if (value.Type == DynamoDBEntryType.Numeric)
{
properties.SetValue(m, Convert.ToInt32(value.AsPrimitive().Value));
}
}
}
else if (property is DynamoDBBool)
{
var booleanProperty = propTypes.GetProperty(attribute);
if (booleanProperty != null)
booleanProperty.SetValue(m, property.AsBoolean());
}
}
model.Add(m);
}
return model;
}

Categories