Generate POCO from dynamic in C# - 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.

Related

Is there a built-in method to convert .NET Configuration to JSON?

It's easy to convert JSON into configuration, e.g. with
using var stream = new MemoryStream(Encoding.UTF8.GetBytes(json));
var configuration = new ConfigurationBuilder().AddJsonStream(stream).Build();
which gives you an IConfigurationRoot.
Is there a method (preferably in one of the Microsoft.Extensions.Configuration packages) that does the reverse?
Context: I'm downloading a bunch of Azure App Configuration settings and want to export them as JSON. Similar functionality is available in the Azure Portal but I want to resolve key vault references as well.
I can probably do something like this:
// Convert to JSON
var jRoot = new JObject();
foreach (var setting in settings) {
Add(jRoot, setting.Key, setting.Value);
}
with the Add method defined as
private void Add(JObject jObject, string key, string value) {
var index = key.IndexOf(':');
if (index == -1) {
jObject[key] = value;
return;
}
var prefix = key[..index];
if (!jObject.ContainsKey(prefix)) {
jObject[prefix] = new JObject();
}
Add((JObject)jObject[prefix], key[(index + 1)..], value);
}
which I'd probably need to extend to support arrays, but I was hoping I'd not have to reinvent the wheel.
For now, I've expanded the Add method to support arrays:
private void Add(JToken jToken, string key, string value) {
var components = key.Split(":", 3);
if (components.Length == 1) {
// Leaf node
if (jToken is JArray jArray_) {
jArray_.Add(value);
} else {
jToken[components[0]] = value;
}
return;
}
// Next level
JToken nextToken;
var nextTokenIsAnArray = int.TryParse(components[1], out _);
if (jToken is JArray jArray) {
var index = int.Parse(components[0]);
if (jArray.Count == index) {
nextToken = nextTokenIsAnArray ? new JArray() : (JToken)new JObject();
jArray.Add(nextToken);
} else {
nextToken = jArray[index];
}
} else {
nextToken = jToken[components[0]];
if (nextToken == null) {
nextToken = jToken[components[0]] = nextTokenIsAnArray ? new JArray() : (JToken)new JObject();
}
}
Add(nextToken, key[(components[0].Length + 1)..], value);
}
which works for me, assuming arrays appear in the right order, e.g.
key "Foo:0", value "Bar"
key "Foo:1", value "Baz"
which gets serialized as
{ "Foo": ["Bar", "Baz"] }

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

Entity Framework 6. Automatically truncate string fields that exceed DB Field Length

Firstly, why do this? In production system if data comes in that is too big for the DB, you may want to keep running and accept the data loss if the fields are not critical, and there will be bigger problems if the record is not inserted.
I have this working with the following code but it is ugly as hell having to reflect over private .net framework properties.
Surely there is a better way to do this?
I saw following code on another posting to load the metadata but it does not work. The Types loaded this way do not have the database field lengths populated. We generate model from DB. That way we do not ever have to make manual adjustments to entity model ourselves (the DB first method).
var metaDataWorkspace = ((IObjectContextAdapter)db).ObjectContext.MetadataWorkspace;
var edmTypes = metaDataWorkspace.GetItems<EdmType>(DataSpace.OSpace);
The Method with the logic is in AutoTruncateStringToMaxLength() below.
has following usings:
using System.Data.Entity;
using System.Data.Entity.Validation;
using System.Data.Entity.Core.EntityClient;
using System.Data.Entity.Core.Metadata.Edm;
code which sits in partial entity class (e.g. public partial class MyEntities):
public override int SaveChanges()
{
try
{
this.AutoTruncateStringToMaxLength();
return base.SaveChanges();
}
catch (DbEntityValidationException ex)
{
List<string> errorMessages = new List<string>();
foreach (DbEntityValidationResult validationResult in ex.EntityValidationErrors)
{
string entityName = validationResult.Entry.Entity.GetType().Name;
foreach (DbValidationError error in validationResult.ValidationErrors)
{
errorMessages.Add(entityName + "." + error.PropertyName + ": " + error.ErrorMessage);
}
}
// Join the list to a single string.
var fullErrorMessage = string.Join("; ", errorMessages);
// Combine the original exception message with the new one.
var exceptionMessage = string.Concat(ex.Message, " The validation errors are: ", fullErrorMessage);
// Throw a new DbEntityValidationException with the improved exception message.
throw new DbEntityValidationException(exceptionMessage, ex.EntityValidationErrors);
}
}
private void AutoTruncateStringToMaxLength()
{
var entries = this?.ChangeTracker?.Entries();
if (entries == null)
{
return;
}
//********** EDM type from here does not work. MaxLength properties are not set ***************** //
//var metaDataWorkspace = ((IObjectContextAdapter)db).ObjectContext.MetadataWorkspace;
//var edmTypes = metaDataWorkspace.GetItems<EdmType>(DataSpace.OSpace);
ReadOnlyMetadataCollection<EdmMember> memberMetaDataProperties = null;
string currentloadedEdmType = null;
foreach (var entry in entries)
{
var internalEntry = entry.GetType().GetProperty("InternalEntry", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance).GetValue(entry);
var edmType = (EdmType)internalEntry.GetType().GetProperty("EdmEntityType", System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Instance).GetValue(internalEntry);
if (edmType != null)
{
if (currentloadedEdmType == null || edmType.Name != currentloadedEdmType)
{
currentloadedEdmType = edmType.Name;
//seems slow to load (in debug) so cache just in case there is performance issue
memberMetaDataProperties = (ReadOnlyMetadataCollection<EdmMember>)edmType.MetadataProperties.FirstOrDefault(x => x.Name == "Members").Value;
}
entry.Entity.GetType().GetProperties().ToList().ForEach(p =>
{
var matchingMemberMetaData = memberMetaDataProperties.FirstOrDefault(x => x.Name == p.Name);
if (matchingMemberMetaData != null && matchingMemberMetaData.BuiltInTypeKind.ToString() == "EdmProperty")
{
var edmProperty = (EdmProperty)matchingMemberMetaData;
if (edmProperty.MaxLength.HasValue && edmProperty.TypeName == "String")
{
string value = (p.GetValue(entry.Entity) ?? "").ToString();
if (value.Length > edmProperty.MaxLength.Value)
{
// oops. Its too Long, so truncate it.
p.SetValue(entry.Entity, value.Substring(value.Length - edmProperty.MaxLength.Value));
}
}
}
});
}
}
}
EF Core has a better metadata API. EG
var q = from e in this.Model.GetEntityTypes()
from p in e.GetProperties()
where p.ClrType == typeof(string)
select new { EntityName = e.Name, PropertyName = p.Name, MaxLength = p.GetMaxLength() };

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

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