Here is the Context :
I try to code a mapper for converting my DomainModel Objects to ViewModel Ojects dynamically. The problem I get, it's when I try to invoke a method of generic class by reflection I get this error :
System.InvalidOperationException : Late bound operations cannot be performed on types or methods for which ContainsGenericParameters is true.
Can someone help-me to figure out where is the fault ? It would be greatly appreciated
Here is the Code (I tried to simplified it) :
public class MapClass<SourceType, DestinationType>
{
public string Test()
{
return test
}
public void MapClassReflection(SourceType source, ref DestinationType destination)
{
Type sourceType = source.GetType();
Type destinationType = destination.GetType();
foreach (PropertyInfo sourceProperty in sourceType.GetProperties())
{
string destinationPropertyName = LookupForPropertyInDestinationType(sourceProperty.Name, destinationType);
if (destinationPropertyName != null)
{
PropertyInfo destinationProperty = destinationType.GetProperty(destinationPropertyName);
if (destinationProperty.PropertyType == sourceProperty.PropertyType)
{
destinationProperty.SetValue(destination, sourceProperty.GetValue(source, null), null);
}
else
{
Type d1 = typeof(MapClass<,>);
Type[] typeArgs = { destinationProperty.GetType(), sourceType.GetType() };
Type constructed = d1.MakeGenericType(typeArgs);
object o = Activator.CreateInstance(constructed, null);
MethodInfo theMethod = d1.GetMethod("Test");
string toto = (string)theMethod.Invoke(o,null);
}
}
}
}
private string LookupForPropertyInDestinationType(string sourcePropertyName, Type destinationType)
{
foreach (PropertyInfo property in destinationType.GetProperties())
{
if (property.Name == sourcePropertyName)
{
return sourcePropertyName;
}
}
return null;
}
}
You need to call GetMethod on the constructed type constructed, not on the type definition d1.
// ...
Type d1 = typeof(MapClass<,>);
Type[] typeArgs = { destinationProperty.GetType(), sourceType.GetType() };
Type constructed = d1.MakeGenericType(typeArgs);
object o = Activator.CreateInstance(constructed, null);
MethodInfo theMethod = constructed.GetMethod("Test");
string toto = (string)theMethod.Invoke(o, null);
// ...
Related
I'm trying to convert a string delimited by a comma to a list of some type.
private static void SetPropertyValue(this object obj, PropertyInfo propInfo, object value)
{
if (propInfo.PropertyType != typeof(string) && typeof(IEnumerable).IsAssignableFrom(propInfo.PropertyType))
{
var listType = propInfo.PropertyType.GetCollectionType();
var listValue = value.ToString().Split(',').Select(x => Convert.ChangeType(x, listType, null)).ToList();
propInfo.SetValue(obj, listValue, null);
}
//....
}
public static Type GetCollectionType(this Type type)
{
foreach (Type interfaceType in type.GetInterfaces())
{
if (interfaceType.IsGenericType &&
interfaceType.GetGenericTypeDefinition()
== typeof(IList<>))
{
return type.GetGenericArguments()[0];
}
}
throw new ArgumentException("Message is irrelevant");
}
Splitting the string and converting each value with Convert.ChangeType works well, but creates a List<object>. I need some way of generating a List<PropType> in order for the SetValue call to work correctly.
If you were to run this code with a List<string> property, you would receive an ArgumentException as the List<object> cannot be converted to a List<string>.
I've resolved this by making my SetPropertyValue method accept a generic type T and creating a List<T>.
public static void SetPropertyValue<T>(object obj, PropertyInfo propInfo, object value)
{
if (propInfo.PropertyType != typeof(string) && typeof(IEnumerable).IsAssignableFrom(propInfo.PropertyType))
{
List<T> listValue = value.ToString()
.Split(',')
.Select(x => (T)Convert.ChangeType(x, typeof(T), null))
.ToList();
propInfo.SetValue(obj, listValue, null);
}
// ...
}
I can then call the method using reflection to pass a Type variable as T.
var type = property.PropertyType.GetCollectionType();
var method = typeof(TypeExtensions).GetMethod(nameof(TypeExtensions.SetPropertyValue));
var generic = method.MakeGenericMethod(type);
generic.Invoke(obj, new object[] { obj, property, value });
I'm trying to retrieve a value from the following struct.
public struct MyType
{
public string Key { get; set; }
public string Value { get; set; }
}
using the following code.
var mem = new MemberAccessor(typeof(MyType), "Value");
var r = new MyType {Key = "One", Value = "Two"};
object s = mem.Get(r);
Console.WriteLine(s); //Should be Two but is One
And it works, but it gives me the value of "Key" not "Value". If I try to get the value of "Key" then it gives me a "Cannot read from memory" error.
The following is the creation of the Get delegate used in the above code. This only appears to be a problem with a struct.
public class MemberAccessor
{
private readonly Type _targetType;
private readonly Type _memberType;
private readonly MemberInfo _member;
private static readonly Hashtable _mTypeHash = new Hashtable
{
[typeof(sbyte)] = OpCodes.Ldind_I1,
[typeof(byte)] = OpCodes.Ldind_U1,
[typeof(char)] = OpCodes.Ldind_U2,
[typeof(short)] = OpCodes.Ldind_I2,
[typeof(ushort)] = OpCodes.Ldind_U2,
[typeof(int)] = OpCodes.Ldind_I4,
[typeof(uint)] = OpCodes.Ldind_U4,
[typeof(long)] = OpCodes.Ldind_I8,
[typeof(ulong)] = OpCodes.Ldind_I8,
[typeof(bool)] = OpCodes.Ldind_I1,
[typeof(double)] = OpCodes.Ldind_R8,
[typeof(float)] = OpCodes.Ldind_R4
};
public static Type GetMemberInfoType(MemberInfo member)
{
Type type;
if (member is FieldInfo)
type = ((FieldInfo)member).FieldType;
else if (member is PropertyInfo)
type = ((PropertyInfo)member).PropertyType;
else if (member == null)
type = typeof(object);
else
throw new NotSupportedException();
return type;
}
/// <summary>
/// Creates a new property accessor.
/// </summary>
/// <param name="targetType">Target object type.</param>
/// <param name="memberName">Property name.</param>
public MemberAccessor(Type targetType, string memberName)
{
_targetType = targetType;
MemberInfo memberInfo = (targetType).GetProperties().First(x => x.Name == memberName);
if (memberInfo == null)
{
throw new Exception(string.Format("Property \"{0}\" does not exist for type " + "{1}.", memberName, targetType));
}
var canRead = IsField(memberInfo) || ((PropertyInfo)memberInfo).CanRead;
var canWrite = IsField(memberInfo) || ((PropertyInfo)memberInfo).CanWrite;
// roslyn automatically implemented properties, in particular for get-only properties: <{Name}>k__BackingField;
if (!canWrite)
{
var backingFieldName = $"<{memberName}>k__BackingField";
var backingFieldMemberInfo = targetType.GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance).FirstOrDefault(x => x.Name == backingFieldName);
if (backingFieldMemberInfo != null)
{
memberInfo = backingFieldMemberInfo;
canWrite = true;
}
}
_memberType = GetMemberInfoType(memberInfo);
_member = memberInfo;
if (canWrite)
{
SetDelegate = GetSetDelegate();
}
if (canRead)
{
GetDelegate = GetGetDelegate();
}
}
private Func<object, object> GetDelegate = null;
private Action<object, object> SetDelegate = null;
/// <summary>
/// Sets the property for the specified target.
/// </summary>
/// <param name="target">Target object.</param>
/// <param name="value">Value to set.</param>
public void Set(object target, object value)
{
SetDelegate?.Invoke(target, value);
}
public object Get(object target)
{
return GetDelegate?.Invoke(target);
}
private Action<object, object> GetSetDelegate()
{
Type[] setParamTypes = new Type[] { typeof(object), typeof(object) };
Type setReturnType = null;
var owner = _targetType.GetTypeInfo().IsAbstract || _targetType.GetTypeInfo().IsInterface ? null : _targetType;
var setMethod = owner != null
? new DynamicMethod(Guid.NewGuid().ToString(), setReturnType, setParamTypes, owner, true)
: new DynamicMethod(Guid.NewGuid().ToString(), setReturnType, setParamTypes, true);
// From the method, get an ILGenerator. This is used to
// emit the IL that we want.
//
ILGenerator setIL = setMethod.GetILGenerator();
//
// Emit the IL.
//
Type paramType = _memberType;
setIL.Emit(OpCodes.Ldarg_0); //Load the first argument
//(target object)
//Cast to the source type
setIL.Emit(OpCodes.Castclass, this._targetType);
setIL.Emit(OpCodes.Ldarg_1); //Load the second argument
//(value object)
if (paramType.GetTypeInfo().IsValueType)
{
setIL.Emit(OpCodes.Unbox, paramType); //Unbox it
if (_mTypeHash[paramType] != null) //and load
{
OpCode load = (OpCode)_mTypeHash[paramType];
setIL.Emit(load);
}
else
{
setIL.Emit(OpCodes.Ldobj, paramType);
}
}
else
{
setIL.Emit(OpCodes.Castclass, paramType); //Cast class
}
if (IsField(_member))
{
setIL.Emit(OpCodes.Stfld, (FieldInfo)_member);
}
else
{
MethodInfo targetSetMethod = GetSetMethodOnDeclaringType(((PropertyInfo)this._member));
if (targetSetMethod != null)
{
setIL.Emit(OpCodes.Callvirt, targetSetMethod);
}
else
{
setIL.ThrowException(typeof(MissingMethodException));
}
}
setIL.Emit(OpCodes.Ret);
var del = setMethod.CreateDelegate(Expression.GetActionType(setParamTypes));
return del as Action<object, object>;
}
public static bool IsField(MemberInfo member)
{
return member is FieldInfo;
}
public static MethodInfo GetSetMethodOnDeclaringType(PropertyInfo propertyInfo)
{
var methodInfo = propertyInfo.GetSetMethod(true);
return methodInfo ?? propertyInfo
.DeclaringType
.GetProperty(propertyInfo.Name)
.GetSetMethod(true);
}
private Func<object, object> GetGetDelegate()
{
Type[] setParamTypes = new[] { typeof(object) };
Type setReturnType = typeof(object);
Type owner = _targetType.GetTypeInfo().IsAbstract || _targetType.GetTypeInfo().IsInterface ? null : _targetType;
var getMethod = owner != null
? new DynamicMethod(Guid.NewGuid().ToString(), setReturnType, setParamTypes, owner, true)
: new DynamicMethod(Guid.NewGuid().ToString(), setReturnType, setParamTypes, true);
// From the method, get an ILGenerator. This is used to
// emit the IL that we want.
ILGenerator getIL = getMethod.GetILGenerator();
getIL.DeclareLocal(typeof(object));
getIL.Emit(OpCodes.Ldarg_0); //Load the first argument
//(target object)
//Cast to the source type
getIL.Emit(OpCodes.Castclass, this._targetType);
//Get the property value
if (IsField(_member))
{
getIL.Emit(OpCodes.Ldfld, (FieldInfo)_member);
if (_memberType.GetTypeInfo().IsValueType)
{
getIL.Emit(OpCodes.Box, _memberType);
}
}
else
{
var targetGetMethod = ((PropertyInfo)_member).GetGetMethod();
getIL.Emit(OpCodes.Callvirt, targetGetMethod);
if (targetGetMethod.ReturnType.GetTypeInfo().IsValueType)
{
getIL.Emit(OpCodes.Box, targetGetMethod.ReturnType);
}
}
getIL.Emit(OpCodes.Ret);
var del = getMethod.CreateDelegate(Expression.GetFuncType(setParamTypes.Concat(new[] { setReturnType }).ToArray()));
return del as Func<object, object>;
}
}
Problem in the IL code that you generate for GetDelegate. You should unbox struct from object type parameter before calling GetMethod of it's property, like this:
if (_targetType.IsValueType)
getIL.Emit(OpCodes.Unbox, _targetType);
else
getIL.Emit(OpCodes.Castclass, this._targetType);
Small side notes about GetGetDelegate method:
For struct types you can use Call instead Callvirt because it's faster and struct can't have virtual properties.
I was't able to understand why do you need declare local variable with this instruction getIL.DeclareLocal(typeof(object)) and I believe you can safely remove this line.
Just in case here version of GetGetDelegate method that worked for me:
private Func<object, object> GetGetDelegate()
{
Type setParamType = typeof(object);
Type[] setParamTypes = { setParamType };
Type setReturnType = typeof(object);
Type owner = _targetType.GetTypeInfo().IsAbstract || _targetType.GetTypeInfo().IsInterface ? null : _targetType;
var getMethod = owner != null
? new DynamicMethod(Guid.NewGuid().ToString(), setReturnType, setParamTypes, owner, true)
: new DynamicMethod(Guid.NewGuid().ToString(), setReturnType, setParamTypes, true);
// From the method, get an ILGenerator. This is used to
// emit the IL that we want.
ILGenerator getIL = getMethod.GetILGenerator();
getIL.Emit(OpCodes.Ldarg_0); //Load the first argument (target object)
if (_targetType.IsValueType)
getIL.Emit(OpCodes.Unbox, _targetType); //unbox struct
else
getIL.Emit(OpCodes.Castclass, this._targetType); //Cast to the source type
Type returnType = null;
if (IsField(_member))
{
getIL.Emit(OpCodes.Ldfld, (FieldInfo)_member);
returnType = _memberType;
}
else
{
var targetGetMethod = ((PropertyInfo)_member).GetGetMethod();
var opCode = _targetType.IsValueType ? OpCodes.Call : OpCodes.Callvirt;
getIL.Emit(opCode, targetGetMethod);
returnType = targetGetMethod.ReturnType;
}
if (returnType.IsValueType)
{
getIL.Emit(OpCodes.Box, returnType);
}
getIL.Emit(OpCodes.Ret);
var del = getMethod.CreateDelegate(Expression.GetFuncType(setParamType, setReturnType));
return del as Func<object, object>;
}
I have a ConvertMethods class that can be dynamically Invoked.
public class ConvertMethods
{
public ConvertMethods()
{
Type type = typeof(ConvertMethods);
methodInfos = type.GetMethods(BindingFlags.Public | BindingFlags.Static | BindingFlags.DeclaredOnly);
}
public Type GetParameterType(string methodName)
{
foreach (var method in methodInfos) {
if (method.Name == methodName) {
return method.GetParameters()[0].GetType();
}
}
throw new MissingMethodException("ConvertMethods", methodName);
}
public Type GetReturnType(string methodName)
{
foreach (var method in methodInfos) {
if (method.Name == methodName) {
return method.ReturnType;
}
}
throw new MissingMethodException("ConvertMethods", methodName);
}
public object InvokeMethod(string methodName, object parameter)
{
foreach (var method in methodInfos) {
if (method.Name == methodName) {
return InvokeInternal(method, parameter);
}
}
throw new MissingMethodException("ConvertMethods", methodName);
}
public static TimeSpan SecondsToTimeSpan(long seconds)
{
return TimeSpan.FromSeconds(seconds);
}
private object InvokeInternal(MethodInfo method, object parameter)
{
return method.Invoke(null, new[] { parameter });
}
private MethodInfo[] methodInfos;
}
Potentially, every value that needs to be converted comes from the database as a string. I want to dynamically cast/convert it it to whatever the Parameter Type is of the Invoked method. Here is what I have:
class Program
{
static void Main(string[] args)
{
string methodName = "SecondsToTimeSpan";
string value = "10";
ConvertMethods methods = new ConvertMethods();
Type returnType = methods.GetReturnType(methodName);
Type paramType = methods.GetParameterType(methodName);
object convertedParameter = (paramType)value; // error on this line
var result = methods.InvokeMethod(methodName, convertedParameter);
Console.WriteLine(result.ToString());
}
}
How can I property convert or convert the String value to whatever type paramType contains?
object convertedParameter = TypeDescriptor.GetConverter(paramType).ConvertFromString(value);
will do what you want.
I can't understand why my fieldType is not qualified as a generic list. I tried to use the solution proposed below to get the generic type of a list. But when I reach fieldType.IsGenericType == false, I was quite surprised and I don't understand what is happening.
My goal is to be able to use my method CreateTable() for each field in my context. My context should only have List fields.
This is the information i found in my object:
FieldType = {Name = "List`1" FullName =
"System.Collections.Generic.List`1[[WebApplication1.Models.Movie, WebApplication1,
Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]]"}
Reference : C# generic list <T> how to get the type of T?
My code example:
public class MediaContext
{
public List<Movie> Movies { get; set; }
public List<Subtitle> Subtitles { get; set; }
public MediaContext()
{
this.Movies = new List<Movie>();
this.Subtitles = new List<Subtitle>();
}
}
public void CreateDB(object context)
{
Type type = context.GetType();
FieldInfo[] fields = type.GetFields(BindingFlags.Instance
| BindingFlags.NonPublic);
foreach (FieldInfo field in fields)
{
Type genericType = this.GetGenericType(field);
MethodInfo method = this.GetType().GetMethod("CreateTable");
MethodInfo generic = method.MakeGenericMethod(genericType);
generic.Invoke(this, null);
}
foreach (FieldInfo field in fields)
{
Type genericType = this.GetGenericType(field);
MethodInfo method = this.GetType().GetMethod("AddKeys");
MethodInfo generic = method.MakeGenericMethod(genericType);
generic.Invoke(this, null);
}
}
private Type GetGenericType(FieldInfo field)
{
Type fieldType = field.GetType();
Type genericType = null;
// Where I believe is should be generic.
if (fieldType.IsGenericType &&
fieldType.GetGenericTypeDefinition() == typeof(List<>))
{
genericType = fieldType.GetGenericArguments()[0];
}
else
{
throw new Exception("An array is needed");
}
return genericType;
}
public void CreateTable<T>()
{
StringBuilder query = new StringBuilder();
Type type = typeof(T);
query.Append(String.Format("CREATE TABLE {0} (", NamingGeneration.PlurializeName(type.Name)));
query.Append(this.AddFields(typeof(T).GetProperties()));
query.Append(")");
SqlCommand command = new SqlCommand();
command.CommandText = query.ToString();
SqlExecuteRequest.Instance.ExecuteNonQuery(command);
}
private void AddKeys<T>()
{
Type type = typeof(T);
PropertyInfo[] properties = type.GetProperties();
IEnumerable<PropertyInfo> keyedProperties = properties.Where(x => x.Name.Contains("Id"));
foreach (PropertyInfo property in keyedProperties)
{
if (property.Name == "Id")
{
this.AddPrimaryKey(type.Name, property.Name);
}
else if (property.Name.EndsWith("Id"))
{
this.AddForeignKey(type.Name, property.Name);
}
}
}
To fix the code as you have it, instead of
Type fieldType = field.GetType();
You want
Type fieldType = field.FieldType;
And it will work. FieldInfo is what you pass through. When you call GetType(), the type you're getting is the FieldInfo type information. FieldInfo contains information about a Field, and the Type information is held in FieldInfo.FieldType.
If you step through this code, you would see this behavior. Debugging is useful.
I have a static method that has two parameters, one that is source object that contains all info and a destination object that I want to transfer all values to.
The problem is that the class has nested classes and I don't know how to add it to the dest object.
I appreciate all help that I can get.
To call the method looks like this:
Automapper(annonsDto, new Annons(){
Model = new Model()
{Company = new Company()},
User = new Users()});
private static Object Automapper(Object source, Object dest)
{
PropertyInfo[] sourcePropertyInfos = source.GetType().GetProperties();
Array.Sort(sourcePropertyInfos, delegate(PropertyInfo propertyInfo1, PropertyInfo propertyInfo2)
{
return propertyInfo1.Name.CompareTo(propertyInfo2.Name);
});
foreach (PropertyInfo sourcePropertyInfo in sourcePropertyInfos)
{
if (sourcePropertyInfo.PropertyType.IsClass)
{
if (sourcePropertyInfo.PropertyType.FullName.ToLower().Contains("xXxXx"))
{
var nestedClass = sourcePropertyInfo.GetValue(source, null);
Automapper(nestedClass, dest);
continue;
}
}
dest = TransferPropValue(sourcePropertyInfo.Name, source, dest);
}
return dest;
}
private static Object TransferPropValue(String targetName, Object sourceObj, Object destObj)
{
if (sourceObj == null)
{
return null;
}
Type destType = destObj.GetType();
PropertyInfo destInfo = destType.GetProperty(targetName);
Type sourceType = sourceObj.GetType();
PropertyInfo sourceInfo = sourceType.GetProperty(targetName);
if (sourceInfo == null) { return null; }
try
{
var sourceValue = sourceInfo.GetValue(sourceObj, null);
destInfo.SetValue(destObj, sourceValue, null);
}
catch (Exception) { }
return destObj;
}