In the documentation for DynamicObject, there is an example of a DynamicDictionary that allows you to work with a dictionary as if it's a class with properties.
Here is the class (modified slightly for brevity):
public class DynamicDictionary : DynamicObject
{
Dictionary<string, object> _dictionary = new Dictionary<string, object>();
public int Count
{
get { return _dictionary.Count; }
}
public override bool TryGetMember(GetMemberBinder binder, out object result)
{
string name = binder.Name.ToLower();
return _dictionary.TryGetValue(name, out result);
}
public override bool TrySetMember(SetMemberBinder binder, object value)
{
_dictionary[binder.Name.ToLower()] = value;
return true;
}
}
What I'd like to do is modify the class, so that I can do the following:
public class Test
{
public Test()
{
var result = Enumerable.Range(1, 5).Select(i => new DynamicDictionary
{
Id = i,
Foo = "Foo",
Bar = 2
});
}
}
Questions
Is this possible?
If yes, how?
DynamicObject provides TryCreateInstance(), which is meant for situations like this, but it's not usable from C#.
I see some ways around this:
Create a dynamic factory class. When you call its Create() method with named argumets, it passes it to the dictionary:
class DynamicDictionaryFactory : DynamicObject
{
public override bool TryInvokeMember(
InvokeMemberBinder binder, object[] args, out object result)
{
if (binder.Name == "Create")
{
// use binder.CallInfo.ArgumentNames and args
// to create the dynamic dictionary
result = …;
return true;
}
return base.TryInvokeMember(binder, args, out result);
}
}
…
dynamic factory = new DynamicDictionaryFactory();
dynamic dict = factory.Create(Id: 42);
Use non-dynamic collection initializer. This means having the property names as strings in the code:
// has to implement IEnumerable, so that collection initializer works
class DynamicDictionary
: DynamicObject, IEnumerable<KeyValuePair<string, object>>
{
public void Add(string name, object value)
{
m_dictionary.Add(name, value);
}
// IEnumerable implmentation and actual DynamicDictionary code here
}
…
dynamic dict = new DynamicDictionary { { "Id", 42 } };
Probably the closest to what you asked for would be to use nested object initializer. That is, the class will have a dynamic property (say, Values), whose properties can be set using object initializer:
class DynamicDictionary : DynamicObject
{
private readonly IDictionary<string, object> m_expandoObject =
new ExpandoObject();
public dynamic Values
{
get { return m_expandoObject; }
}
// DynamicDictionary implementation that uses m_expandoObject here
}
…
dynamic dict = new DynamicDictionary { Values = { Id = 42 } };
Using the open source ImpromptuInterface (via nuget) it has a Builder Syntax. that lets you do something close to the initialization syntax. Specifically after including ImpromptuInterface.Dynamic you could do
var result = Enumerable.Range(1, 5).Select(i => Build<DynamicDictionary>.NewObject
(
Id: i,
Foo: "Foo",
Bar: 2
));
There are other options too listed on that syntax page if you drop the <DynamicDictionary> it will use an ImpromptuDictionary which is essentially the same thing. And you can look at the source for the build syntax too.
It turns out, I was able to solve my problem by using Linq's built-in ToDictionary() method.
Example:
public Test()
{
var data = Enumerable.Range(1, 5).Select(i => new
{
Id = i,
Foo = "Foo",
Bar = 2
});
var result = data
.Select(d => d.GetType().GetProperties()
.Select(p => new { Name = p.Name, Value = p.GetValue(pd, null) })
.ToDictionary(
pair => pair.Name,
pair => pair.Value == null ? string.Empty : pair.Value.ToString()));
}
When I see a pattern of embedded language like this, I tend to use extension methods, so I could write the following code to achieve my goal:
public Test()
{
var data = Enumerable.Range(1, 5).Select(i => new
{
Id = i,
Foo = "Foo",
Bar = 2
}.AsDynamicDictionary());
}
Then I would define the extension method with the code to convert any object (including anonymously typed ones) to DynamicDictionary:
public static class DynamicDictionaryExtensions
{
public static DynamicDictionary AsDynamicDictionary(this object data)
{
if (data == null) throw new ArgumentNullException("data");
return new DynamicDictionary(
data.GetType().GetProperties()
.Where(p => p. && p.CanRead)
.Select(p => new {Name: p.Name, Value: p.GetValue(data, null)})
.ToDictionary(p => p.Name, p => p.Value)
);
}
}
You would have to implement a constructor in DynamicDictionary to receive a IDictionary<string, object>, but that's a piece of cake.
Related
I'm working on a factory to create concrete instances based in two criteria:
1) The class must inherit from an specific abstract class
2) The class must have an specific value in an overridden property
My code looks like this:
public abstract class CommandBase
{
public abstract string Prefix { get; }
}
public class PaintCommand : CommandBase
{
public override string Prefix { get; } = "P";
}
public class WalkCommand : CommandBase
{
public override string Prefix { get; } = "W";
}
class Program
{
static void Main(string[] args)
{
var paintCommand = GetInstance("P");
var walkCommand = GetInstance("W");
Console.ReadKey();
}
static CommandBase GetInstance(string prefix)
{
try
{
var currentAssembly = Assembly.GetExecutingAssembly();
var concreteType = currentAssembly.GetTypes().Where(t => t.IsSubclassOf(typeof(CommandBase)) &&
!t.IsAbstract &&
t.GetProperty("Prefix").GetValue(t).ToString() == prefix).SingleOrDefault();
if (concreteType == null)
throw new InvalidCastException($"No concrete type found for command: {prefix}");
return (CommandBase)Activator.CreateInstance(concreteType);
}
catch (Exception ex)
{
return default(CommandBase);
}
}
}
I'm getting the error:
{System.Reflection.TargetException: Object does not match target type. at System.Reflection.RuntimeMethodInfo.CheckConsistency(Object target) at System.Reflection.RuntimeMethodInfo.InvokeArgumentsCheck(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture) at System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture)
A cleaner way is to define your own attribute to store the prefix.
[AttributeUsage(AttributeTargets.Class)]
public class CommandAttribute : Attribute
{
public String Prefix { get; set; }
public CommandAttribute(string commandPrefix)
{
Prefix = commandPrefix;
}
}
Then use them like so:
[CommandAttribute("P")]
public class PaintCommand : CommandBase
{}
[CommandAttribute("W")]
public class WalkCommand : CommandBase
{}
In reflection:
static CommandBase GetInstance(string prefix)
{
var currentAssembly = Assembly.GetExecutingAssembly();
var concreteType = currentAssembly.GetTypes().Where(commandClass => commandClass.IsDefined(typeof(CommandAttribute), false) && commandClass.GetCustomAttribute<CommandAttribute>().Prefix == prefix).FirstOrDefault();
if (concreteType == null)
throw new InvalidCastException($"No concrete type found for command: {prefix}");
return (CommandBase)Activator.CreateInstance(concreteType);
}
As spender alluded to in his comment, the reason you are getting this specific error is this line:
t.GetProperty("Prefix").GetValue(t)
Here, t is a Type variable containing a class, such as WalkCommand. You're getting the PropertyInfo object for the Prefix property on that class, then trying to use GetValue() to read the value of that property from an instance of a WalkCommand object.
The problem is that you aren't passing GetValue() an instance of the WalkCommand class, you're passing it a Type so Reflection is then throwing this exception.
There are a few of ways to deal with this:
1) Create an instance of each type on the fly, just to read its prefix (I really wouldn't recommend doing this):
var instance = currentAssembly.GetTypes()
.Where(t => t.IsSubclassOf(typeof(CommandBase)) && !t.IsAbstract)
.Select(t => new { t, i = (CommandBase)Activator.CreateInstance(t) })
.Where(x => x.t.GetProperty("Prefix").GetValue(x.i).ToString() == prefix)
.Select(x => x.i)
.SingleOrDefault();
return instance;
2) Change the whole thing over to use Attributes, such as in SwiftingDuster's answer
3) Use a static constructor to create a Dictionary that maps the string prefices to the concrete types. Reflection is expensive, and the classes aren't going to change (unless you're dynamically loading assemblies), so do it once.
We could do this by (ab)using my earlier "create an instance to throw it away" code, so we're still creating an instance of each class just to read the property, but at least we only do it once:
static Dictionary<string, Type> prefixMapping;
static Program()
{
prefixMapping = currentAssembly.GetTypes()
.Where(t => t.IsSubclassOf(typeof(CommandBase)) && !t.IsAbstract)
.Select(t => new { t, c = (CommandBase)Activator.CreateInstance(t) })
.ToDictionary(x => x.t.GetProperty("Prefix").GetValue(x.c).ToString(), x => x.t);
}
static CommandBase GetInstance(string prefix)
{
Type concreteType;
if ( prefixMapping.TryGetValue(prefix, out concreteType) )
{
return (CommandBase)Activator.CreateInstance(concreteType);
}
return default(CommandBase);
}
Note that this will throw a really horrible exception if you have multiple classes with the same prefix, and as it would be an exception in a static constructor, it'll likely explode your application. Either catch the exception (which will leave prefixMapping as null), or modify the Linq expression to only return one type for each prefix (as below).
4) Use both the Attribute method from SwiftingDuster and also the precomputation of the dictionary. This would be my preferred solution:
static Dictionary<string, Type> prefixMapping;
static Program()
{
prefixMapping = currentAssembly.GetTypes()
.Where(t => t.IsSubclassOf(typeof(CommandBase)) && t.IsDefined(typeof(CommandAttribute), false) && !t.IsAbstract)
.Select(t => new { t, p = t.GetCustomAttribute<CommandAttribute>().Prefix })
.GroupBy(x => x.p)
.ToDictionary(g => g.Key, g => g.First().t);
}
static CommandBase GetInstance(string prefix)
{
Type concreteType;
if ( prefixMapping.TryGetValue(prefix, out concreteType) )
{
return (CommandBase)Activator.CreateInstance(concreteType);
}
return default(CommandBase);
}
Hope this helps
I have a class, lets call it Book with a property called Name. With that property, I have an attribute associated with it.
public class Book
{
[Author("AuthorName")]
public string Name
{
get; private set;
}
}
In my main method, I'm using reflection and wish to get key value pair of each attribute for each property. So in this example, I'd expect to see "Author" for attribute name and "AuthorName" for the attribute value.
Question: How do I get the attribute name and value on my properties using Reflection?
Use typeof(Book).GetProperties() to get an array of PropertyInfo instances. Then use GetCustomAttributes() on each PropertyInfo to see if any of them have the Author Attribute type. If they do, you can get the name of the property from the property info and the attribute values from the attribute.
Something along these lines to scan a type for properties that have a specific attribute type and to return data in a dictionary (note that this can be made more dynamic by passing types into the routine):
public static Dictionary<string, string> GetAuthors()
{
Dictionary<string, string> _dict = new Dictionary<string, string>();
PropertyInfo[] props = typeof(Book).GetProperties();
foreach (PropertyInfo prop in props)
{
object[] attrs = prop.GetCustomAttributes(true);
foreach (object attr in attrs)
{
AuthorAttribute authAttr = attr as AuthorAttribute;
if (authAttr != null)
{
string propName = prop.Name;
string auth = authAttr.Name;
_dict.Add(propName, auth);
}
}
}
return _dict;
}
To get all attributes of a property in a dictionary use this:
typeof(Book)
.GetProperty("Name")
.GetCustomAttributes(false)
.ToDictionary(a => a.GetType().Name, a => a);
remember to change from false to true if you want to include inheritted attributes as well.
If you just want one specific Attribute value For instance Display Attribute you can use the following code:
var pInfo = typeof(Book).GetProperty("Name")
.GetCustomAttribute<DisplayAttribute>();
var name = pInfo.Name;
I have solved similar problems by writing a Generic Extension Property Attribute Helper:
using System;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
public static class AttributeHelper
{
public static TValue GetPropertyAttributeValue<T, TOut, TAttribute, TValue>(
Expression<Func<T, TOut>> propertyExpression,
Func<TAttribute, TValue> valueSelector)
where TAttribute : Attribute
{
var expression = (MemberExpression) propertyExpression.Body;
var propertyInfo = (PropertyInfo) expression.Member;
var attr = propertyInfo.GetCustomAttributes(typeof(TAttribute), true).FirstOrDefault() as TAttribute;
return attr != null ? valueSelector(attr) : default(TValue);
}
}
Usage:
var author = AttributeHelper.GetPropertyAttributeValue<Book, string, AuthorAttribute, string>(prop => prop.Name, attr => attr.Author);
// author = "AuthorName"
You can use GetCustomAttributesData() and GetCustomAttributes():
var attributeData = typeof(Book).GetProperty("Name").GetCustomAttributesData();
var attributes = typeof(Book).GetProperty("Name").GetCustomAttributes(false);
If you mean "for attributes that take one parameter, list the attribute-names and the parameter-value", then this is easier in .NET 4.5 via the CustomAttributeData API:
using System.Collections.Generic;
using System.ComponentModel;
using System.Reflection;
public static class Program
{
static void Main()
{
PropertyInfo prop = typeof(Foo).GetProperty("Bar");
var vals = GetPropertyAttributes(prop);
// has: DisplayName = "abc", Browsable = false
}
public static Dictionary<string, object> GetPropertyAttributes(PropertyInfo property)
{
Dictionary<string, object> attribs = new Dictionary<string, object>();
// look for attributes that takes one constructor argument
foreach (CustomAttributeData attribData in property.GetCustomAttributesData())
{
if(attribData.ConstructorArguments.Count == 1)
{
string typeName = attribData.Constructor.DeclaringType.Name;
if (typeName.EndsWith("Attribute")) typeName = typeName.Substring(0, typeName.Length - 9);
attribs[typeName] = attribData.ConstructorArguments[0].Value;
}
}
return attribs;
}
}
class Foo
{
[DisplayName("abc")]
[Browsable(false)]
public string Bar { get; set; }
}
private static Dictionary<string, string> GetAuthors()
{
return typeof(Book).GetProperties()
.SelectMany(prop => prop.GetCustomAttributes())
.OfType<AuthorAttribute>()
.ToDictionary(a => a.GetType().Name.Replace("Attribute", ""), a => a.Name);
}
Example using generics (target framework 4.5)
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
private static Dictionary<string, string> GetAttribute<TAttribute, TType>(
Func<TAttribute, string> valueFunc)
where TAttribute : Attribute
{
return typeof(TType).GetProperties()
.SelectMany(p => p.GetCustomAttributes())
.OfType<TAttribute>()
.ToDictionary(a => a.GetType().Name.Replace("Attribute", ""), valueFunc);
}
Usage
var dictionary = GetAttribute<AuthorAttribute, Book>(a => a.Name);
public static class PropertyInfoExtensions
{
public static TValue GetAttributValue<TAttribute, TValue>(this PropertyInfo prop, Func<TAttribute, TValue> value) where TAttribute : Attribute
{
var att = prop.GetCustomAttributes(
typeof(TAttribute), true
).FirstOrDefault() as TAttribute;
if (att != null)
{
return value(att);
}
return default(TValue);
}
}
Usage:
//get class properties with attribute [AuthorAttribute]
var props = typeof(Book).GetProperties().Where(prop => Attribute.IsDefined(prop, typeof(AuthorAttribute)));
foreach (var prop in props)
{
string value = prop.GetAttributValue((AuthorAttribute a) => a.Name);
}
or:
//get class properties with attribute [AuthorAttribute]
var props = typeof(Book).GetProperties().Where(prop => Attribute.IsDefined(prop, typeof(AuthorAttribute)));
IList<string> values = props.Select(prop => prop.GetAttributValue((AuthorAttribute a) => a.Name)).Where(attr => attr != null).ToList();
While the above most upvoted answers definitely work, I'd suggest using a slightly different approach in some cases.
If your class has multiple properties with always the same attribute and you want to get those attributes sorted into a dictionary, here is how:
var dict = typeof(Book).GetProperties().ToDictionary(p => p.Name, p => p.GetCustomAttributes(typeof(AuthorName), false).Select(a => (AuthorName)a).FirstOrDefault());
This still uses cast but ensures that the cast will always work as you will only get the custom attributes of the type "AuthorName".
If you had multiple Attributes above answers would get a cast exception.
Here are some static methods you can use to get the MaxLength, or any other attribute.
using System;
using System.Linq;
using System.Reflection;
using System.ComponentModel.DataAnnotations;
using System.Linq.Expressions;
public static class AttributeHelpers {
public static Int32 GetMaxLength<T>(Expression<Func<T,string>> propertyExpression) {
return GetPropertyAttributeValue<T,string,MaxLengthAttribute,Int32>(propertyExpression,attr => attr.Length);
}
//Optional Extension method
public static Int32 GetMaxLength<T>(this T instance,Expression<Func<T,string>> propertyExpression) {
return GetMaxLength<T>(propertyExpression);
}
//Required generic method to get any property attribute from any class
public static TValue GetPropertyAttributeValue<T, TOut, TAttribute, TValue>(Expression<Func<T,TOut>> propertyExpression,Func<TAttribute,TValue> valueSelector) where TAttribute : Attribute {
var expression = (MemberExpression)propertyExpression.Body;
var propertyInfo = (PropertyInfo)expression.Member;
var attr = propertyInfo.GetCustomAttributes(typeof(TAttribute),true).FirstOrDefault() as TAttribute;
if (attr==null) {
throw new MissingMemberException(typeof(T).Name+"."+propertyInfo.Name,typeof(TAttribute).Name);
}
return valueSelector(attr);
}
}
Using the static method...
var length = AttributeHelpers.GetMaxLength<Player>(x => x.PlayerName);
Or using the optional extension method on an instance...
var player = new Player();
var length = player.GetMaxLength(x => x.PlayerName);
Or using the full static method for any other attribute (StringLength for example)...
var length = AttributeHelpers.GetPropertyAttributeValue<Player,string,StringLengthAttribute,Int32>(prop => prop.PlayerName,attr => attr.MaximumLength);
Inspired by the Mikael Engver's answer.
I wrote this into a dynamic method since I use lots of attributes throughout my application. Method:
public static dynamic GetAttribute(Type objectType, string propertyName, Type attrType)
{
//get the property
var property = objectType.GetProperty(propertyName);
//check for object relation
return property.GetCustomAttributes().FirstOrDefault(x => x.GetType() == attrType);
}
Usage:
var objectRelAttr = GetAttribute(typeof(Person), "Country", typeof(ObjectRelationAttribute));
var displayNameAttr = GetAttribute(typeof(Product), "Category", typeof(DisplayNameAttribute));
Hope this helps anyone
Necromancing.
For those that still have to maintain .NET 2.0, or those that want to do it without LINQ:
public static object GetAttribute(System.Reflection.MemberInfo mi, System.Type t)
{
object[] objs = mi.GetCustomAttributes(t, true);
if (objs == null || objs.Length < 1)
return null;
return objs[0];
}
public static T GetAttribute<T>(System.Reflection.MemberInfo mi)
{
return (T)GetAttribute(mi, typeof(T));
}
public delegate TResult GetValue_t<in T, out TResult>(T arg1);
public static TValue GetAttributValue<TAttribute, TValue>(System.Reflection.MemberInfo mi, GetValue_t<TAttribute, TValue> value) where TAttribute : System.Attribute
{
TAttribute[] objAtts = (TAttribute[])mi.GetCustomAttributes(typeof(TAttribute), true);
TAttribute att = (objAtts == null || objAtts.Length < 1) ? default(TAttribute) : objAtts[0];
// TAttribute att = (TAttribute)GetAttribute(mi, typeof(TAttribute));
if (att != null)
{
return value(att);
}
return default(TValue);
}
Example usage:
System.Reflection.FieldInfo fi = t.GetField("PrintBackground");
wkHtmlOptionNameAttribute att = GetAttribute<wkHtmlOptionNameAttribute>(fi);
string name = GetAttributValue<wkHtmlOptionNameAttribute, string>(fi, delegate(wkHtmlOptionNameAttribute a){ return a.Name;});
or simply
string aname = GetAttributValue<wkHtmlOptionNameAttribute, string>(fi, a => a.Name );
Just looking for the right place to put this piece of code.
let's say you have the following property:
[Display(Name = "Solar Radiation (Average)", ShortName = "SolarRadiationAvg")]
public int SolarRadiationAvgSensorId { get; set; }
And you want to get the ShortName value. You can do:
((DisplayAttribute)(typeof(SensorsModel).GetProperty(SolarRadiationAvgSensorId).GetCustomAttribute(typeof(DisplayAttribute)))).ShortName;
Or to make it general:
internal static string GetPropertyAttributeShortName(string propertyName)
{
return ((DisplayAttribute)(typeof(SensorsModel).GetProperty(propertyName).GetCustomAttribute(typeof(DisplayAttribute)))).ShortName;
}
foreach (var p in model.GetType().GetProperties())
{
var valueOfDisplay =
p.GetCustomAttributesData()
.Any(a => a.AttributeType.Name == "DisplayNameAttribute") ?
p.GetCustomAttribute<DisplayNameAttribute>().DisplayName :
p.Name;
}
In this example I used DisplayName instead of Author because it has a field named 'DisplayName' to be shown with a value.
to get attribute from enum, i'm using :
public enum ExceptionCodes
{
[ExceptionCode(1000)]
InternalError,
}
public static (int code, string message) Translate(ExceptionCodes code)
{
return code.GetType()
.GetField(Enum.GetName(typeof(ExceptionCodes), code))
.GetCustomAttributes(false).Where((attr) =>
{
return (attr is ExceptionCodeAttribute);
}).Select(customAttr =>
{
var attr = (customAttr as ExceptionCodeAttribute);
return (attr.Code, attr.FriendlyMessage);
}).FirstOrDefault();
}
// Using
var _message = Translate(code);
If you want get property having the custom Attribute then please try the following:
IEnumerable propertyInfos = properties.GetType().GetProperties();
PropertyInfo p = propertyInfos.Where(x => x.GetCustomAttribute() != null);
I need to convert one class to another using automapper and my objects looks like this:
public class Foo
{
public List<object> Objects { get; set; }
}
public class Bar
{
public List<object> Objects { get; set; }
}
public class FooItem
{
public string Name { get; set; }
}
public class BarItem
{
public string Name { get; set; }
}
There could be more types for items, not just FooItem and BarItem but I just use these two for simplicity and I need to have this design.
I've tried several things like type converters and so on but still no luck.
Here the current basic conversion code
Mapper.Initialize(cfg =>
{
cfg.CreateMap<FooItem, BarItem>();
cfg.CreateMap<Foo, Bar>();
});
var foo = new Foo()
{
Objects = new List<object>() { new FooItem() { Name = "name" } }
};
var map = Mapper.Map<Foo, Bar>(foo);
The goal is that the Bar object contains a list of BarItems at runtime, but so far I only have only managed to get a list of FooItem at runtime.
Any ideas?
Are you satisfied with only a list of BarItems? In this case you can do it like this:
var map = Mapper.Map<IEnumerable<FooItem>, List<BarItem>>(foo.Objects.Cast<FooItem>());
Update: You can do it like this.
class Program
{
static void Main(string[] args)
{
Mapper.Initialize(cfg =>
{
cfg.CreateMap<FooItem, BarItem>();
cfg.CreateMap<Foo, Bar>()
.ForMember(dest => dest.Objects, opt => opt.ResolveUsing<CustomResolver>());
});
Mapper.AssertConfigurationIsValid();
var foo = new Foo()
{
Objects = new List<object>() { new FooItem() { Name = "name" } }
};
//var map = Mapper.Map<IEnumerable<FooItem>, List<BarItem>>(foo.Objects.Cast<FooItem>());
var map = Mapper.Map<Foo, Bar>(foo);
}
}
public class CustomResolver : IValueResolver<Foo, Bar, List<object>>
{
public List<object> Resolve(Foo source, Bar destination, List<object> member, ResolutionContext context)
{
var map = Mapper.Map<IEnumerable<FooItem>, List<BarItem>>(source.Objects.Cast<FooItem>());
return map.Cast<object>().ToList();
}
}
You can do type checks inside a ConstructUsing clause:
cfg.CreateMap<object, object>()
.ConstructUsing(src => {
if (src is FooItem) {
return Mapper.Map<BarItem>(src);
}
// ...
throw new InvalidOperationException($"Can not map source item of type '{src.GetType().FullName}'.");
});
But you probably need to introduce an interface for the items in the Objects collection, because the map object -> object overrides all other maps, but we want to use the map FooItem -> BarItem.
Inspired by #Ogglas I manage to found out an interesting solution where it is not needed to check for all the types.
Basically I wrote my own ITypeConverter
public class CustomResolver : ITypeConverter<List<object>, List<object>>
{
public List<object> Convert(List<object> source, List<object> destination, ResolutionContext context)
{
var objects = new List<object>();
foreach (var obj in source)
{
var destinationType = context.ConfigurationProvider.GetAllTypeMaps().First(x => x.SourceType == obj.GetType()).DestinationType;
var target = context.Mapper.Map(obj, obj.GetType(), destinationType);
objects.Add(target);
}
return objects;
}
}
And get from the context the proper mapping for each type. Currently is not aware of nulls and so on...
Then, when configuring automapper
Mapper.Initialize(cfg =>
{
cfg.CreateMap<FooItem, BarItem>();
cfg.CreateMap<List<object>, List<object>>().ConvertUsing<CustomResolver>();
cfg.CreateMap<Foo, Bar>();
});
I use a template engine that renders templates from c# objects (nested). I would like to reflect and figure out which properties / objects are used in each template string.
An ideal way would be to build a "dummy" object representing the right shape and render this in the template. I would then inspect this object afterwards to find out which properties were accessed. This would allow me to keep this logic independant of the template library.
Any idea how i might implement this? The expando object is built dynamically like this:
var dynamicObject = new ExpandoObject() as IDictionary<string, Object>;
foreach (var property in properties) {
dynamicObject.Add(property.Key,property.Value);
}
Had some ideas along these lines:
public class DummyObject {
public DummyObject() {
Accessed = new Dictionary<string, bool>();
}
public Dictionary<string, bool> Accessed;
object MyProp {
get {
Accessed["MyProp"] = true;
return "";
}
}
}
But this custom property obviously doesn't work with the dictionary / expando object. Any ideas of a route forward here?
You can override the TryGetMember method on DynamicObject:
public sealed class LoggedPropertyAccess : DynamicObject {
public readonly HashSet<string> accessedPropertyNames = new HashSet<string>();
public override bool TryGetMember(GetMemberBinder binder, out object result) {
accessedPropertyNames.Add(binder.Name);
result = "";
return true;
}
}
and then the following will output the accessed property names
dynamic testObject = new LoggedPropertyAccess();
string firstname = testObject.FirstName;
string lastname = testObject.LastName;
foreach (var propertyName in testObject.accessedPropertyNames) {
Console.WriteLine(propertyName);
}
Console.ReadKey();
N.B. There is still an issue here -- this works only as long as the template library expects only strings from the properties. The following code will fail, because every property will return a string:
DateTime dob = testObject.DOB;
In order to resolve this, and also allow for nested objects, have TryGetMember return a new instance of LoggedPropertyAccess. Then, you can override the TryConvert method as well; where you can return different values based on the conversion to different types (complete code):
using System;
using System.Collections.Generic;
using System.Dynamic;
namespace DynamicObjectGetterOverride {
public sealed class LoggedPropertyAccess : DynamicObject {
public readonly Dictionary<string, object> __Properties = new Dictionary<string, object>();
public readonly HashSet<string> __AccessedProperties = new HashSet<string>();
public override bool TryGetMember(GetMemberBinder binder, out object result) {
if (!__Properties.TryGetValue(binder.Name, out result)) {
var ret = new LoggedPropertyAccess();
__Properties[binder.Name] = ret;
result = ret;
}
__AccessedProperties.Add(binder.Name);
return true;
}
//this allows for setting values which aren't instances of LoggedPropertyAccess
public override bool TrySetMember(SetMemberBinder binder, object value) {
__Properties[binder.Name] = value;
return true;
}
private static Dictionary<Type, Func<object>> typeActions = new Dictionary<Type, Func<object>>() {
{typeof(string), () => "dummy string" },
{typeof(int), () => 42 },
{typeof(DateTime), () => DateTime.Today }
};
public override bool TryConvert(ConvertBinder binder, out object result) {
if (typeActions.TryGetValue(binder.Type, out var action)) {
result = action();
return true;
}
return base.TryConvert(binder, out result);
}
}
}
and use as follows:
using System;
using static System.Console;
namespace DynamicObjectGetterOverride {
class Program {
static void Main(string[] args) {
dynamic testObject = new LoggedPropertyAccess();
DateTime dob = testObject.DOB;
string firstname = testObject.FirstName;
string lastname = testObject.LastName;
dynamic address = testObject.Address;
address.House = "123";
address.Street = "AnyStreet";
address.City = "Anytown";
address.State = "ST";
address.Country = "USA";
WriteLine("----- Writes the returned values from reading the properties");
WriteLine(new { firstname, lastname, dob });
WriteLine();
WriteLine("----- Writes the actual values of each property");
foreach (var kvp in testObject.__Properties) {
WriteLine($"{kvp.Key} = {kvp.Value}");
}
WriteLine();
WriteLine("----- Writes the actual values of a nested object");
foreach (var kvp in testObject.Address.__Properties) {
WriteLine($"{kvp.Key} = {kvp.Value}");
}
WriteLine();
WriteLine("----- Writes the names of the accessed properties");
foreach (var propertyName in testObject.__AccessedProperties) {
WriteLine(propertyName);
}
ReadKey();
}
}
}
Having a model of type Dictionary<string,dynamic> and would like to convert it to Dictionary<string, MyType1> or Dictionary<string, MyOtherType>!
I've tried
var converted = (Dictionary<string,MyType1>)model
without success tried
IConvertible iConv = model; var converted = iConv.ToType(typeof(MyOtherType), null);
too but it doesn't work
Exception: Cannot convert system.object to type x
How do I convert from runtime type (dynamic) to a well known Type?
There is no built-in conversion from one dictionary type to another dictionary type. However, using Enumerable.ToDictionary, you can easily create a new dictionary from any other data structure.
In your particular example, you can use it as
var converted = model.ToDictionary(kv => kv.Key, kv => (MyType1) kv.Value);
Of course this will throw an exception if your values aren't actually of type MyType1. If they aren't, then instead of (MyType1) kv.Value, call some custom conversion function at that point.
The following little demo works for simple types:
MapDynamicToDictionary test shows turning the dynamic to a dictionary.
MapDictionaryToType shows converting the dictionary to a type T.
You could improve on this by doing checks for types or using as etc.
public class Test
{
[Fact]
public void MapDynamicToDictionary()
{
dynamic d = new { Nr = 1, Name = "Devon" };
var dictionary = TurnObjectIntoDictionary(d);
Assert.Equal(2, dictionary.Keys.Count);
}
[Fact]
public void MapDictionaryToType()
{
dynamic d = new { Nr = 1, Name = "Devon" };
var dictionary = TurnObjectIntoDictionary(d);
var instance = new MyType();
Map(dictionary, instance);
Assert.Equal(instance.Nr, 1);
Assert.Equal(instance.Name, "Devon");
}
public static void Map<T>(IDictionary<string, object> dictionary, T instance)
{
var attr = BindingFlags.Public | BindingFlags.Instance;
foreach (var prop in instance.GetType().GetProperties(attr))
{
if (prop.CanWrite)
{
if(dictionary.ContainsKey(prop.Name))
{
var v = Convert.ChangeType(dictionary[prop.Name], prop.PropertyType);
prop.SetValue(instance, v); }
}
}
}
public static IDictionary<string, object> TurnObjectIntoDictionary(object data)
{
var attr = BindingFlags.Public | BindingFlags.Instance;
var dict = new Dictionary<string, object>();
foreach (var prop in data.GetType().GetProperties(attr))
{
if (prop.CanRead)
{
dict.Add(prop.Name, prop.GetValue(data, null));
}
}
return dict;
}
}
class MyType
{
public int Nr { get; set; }
public string Name { get; set; }
}
Could use TypeConverter to handle more complex examples. Nice example here: http://putridparrot.com/blog/type-conversions-in-c/
I would put a static constructor on your well known type, which accepts dynamic, and build the well known type from that. e.g.
public class SomeType
{
public static SomeType FromDynamic(dynamic arg)
{
return new SomeType
{
SomeProperty = arg.SomeProp
}
}
public int SomeProperty {get; set; }
}
Then you'd just have to iterate over your Dictionary<string,dynamic> and build up the new object like:
var dictionary = new Dictionary<string, SomeType>();
foreach(var item in model)
{
dictionary.Add(item.Key, SomeType.FromDynamic(item.Value));
}
Or borrowing from #hvd:
var converted = model.ToDictionary(kv => kv.Key, kv => SomeType.FromDynamic(kv.Value));