I am having an intermittent exception from my code. I have a method with a dynamic parameter. My code is essentially looping through a lot of types and registering mappings. The dynamic parameter represents an expression mapping the type to a specific property of the type.
Every few runs one call (could be any call to this method, there is no pattern) to AddIndexAttributes will fail with an 'Unable to cast' exception trying to cast the type of the dynamic expression to another expression type completely unreferenced by this code. Presumably, this is a type which has previously been mapped using this method during this run.
System.InvalidCastException: Unable to cast object of type 'System.Linq.Expressions.Expression`1[System.Func`2[Type1,System.Byte[]]]' to type 'System.Linq.Expressions.Expression`1[System.Func`2[Type2,System.Nullable`1[System.DateTimeOffset]]]'.
at CallSite.Target(Closure , CallSite , Type , PropertyInfo , Object , StructuralTypeConfiguration`1 , Dictionary`2 , Dictionary`2 )
at System.Dynamic.UpdateDelegates.UpdateAndExecuteVoid6[T0,T1,T2,T3,T4,T5](CallSite site, T0 arg0, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5)
at CallSite.Target(Closure , CallSite , Type , PropertyInfo , Object , StructuralTypeConfiguration`1 , Dictionary`2 , Dictionary`2 )
at TypeConfigurationHelper.Configure[T](StructuralTypeConfiguration`1 type) in C:\\project\TypeConfigurationHelper.cs:line 127
Where Type1 is T (see code below) and Type2 is another type in my database which is not being used or referenced anywhere in this call. Byte[] is the type of the current parameter in the loop, DateTimeOffset is, I presume, the type of a property of Type2.
The exception itself is coming from System.Core and I have been able to trace the error using VS's framework debugging. I can see it get as far as UpdateDelegates.UpdateAndExecuteVoid6 and it runs its first rule and crashes.
This code is called for all types in my EF database in one thread, one after the other in a loop.
Simplified Code:
public static void Configure<T>(StructuralTypeConfiguration<T> type) where T : class
{
var properties = typeof(T).GetProperties();
//These dictionaries keep track of whether our composite indexes are valid or not.
var compositeIndexMaximumOrder = new Dictionary<string, int>();
var compositeIndexCounts = new Dictionary<string, int>();
foreach (var property in properties)
{
//Get the property for the item on the type
PropertyInfo pi = typeof(T).GetProperty(property.Name);
//Get an expression for the type => property
//To support inherited objects we use ReflectedType instead of DeclaringType
dynamic expression = pi.ReflectedType.TypeMapExpression(pi.Name);
//Place to configure anything that is not tied to a field definition.
AddIndexAttributes<T>(pi, expression, type, compositeIndexMaximumOrder, compositeIndexCounts);
}
}
private static void AddIndexAttributes<T>(PropertyInfo property, dynamic expression, StructuralTypeConfiguration<T> type, Dictionary<string, int> compositeIndexMaximumOrder,
Dictionary<string, int> compositeIndexCounts) where T : class
{
List<IndexAttribute> indexAttributes = convertSymIndexAttributesToDotNetIndexAttributes(property, compositeIndexMaximumOrder, compositeIndexCounts);
if (indexAttributes.Count() > 0)
{
type.Property(expression).HasColumnAnnotation("Index", new IndexAnnotation(indexAttributes));
}
}
Why is this crashing? Why would it be trying to cast a dynamic value to a completely unreferenced type? I've tried to read up about what CallSite etc is actually doing, but I can't see why it would be trying to make a cast at that point.
My project is using .Net 4.5.
TypeMapExpression:
public static dynamic TypeMapExpression(this Type type, string parameterName)
{
//Get a parameter expression for the type
var parameter = System.Linq.Expressions.Expression.Parameter(type);
//Get a lambda expression for the type
return System.Linq.Expressions.Expression.Lambda(System.Linq.Expressions.Expression.Property(parameter, parameterName), parameter);
}
Related
I have a stored procedure call interface that I'm using to handle results from stored procedures with entity (using Translate method to translate the results of our stored procedure into entities that can be tracked and used in EF as normal)
Here's the basic code...
List<object> current = new List<object>();
object item = ((Type)currenttype.Current).GetConstructor(System.Type.EmptyTypes).Invoke(new object[0]);
ObjectContext actualContext = ((IObjectContextAdapter)context).ObjectContext;
string className = "";
EntityContainer container = null;
string setName = "";
className = ((Type)currenttype.Current).ToString();
container = actualContext.MetadataWorkspace.GetEntityContainer(((IObjectContextAdapter)context).ObjectContext.DefaultContainerName, DataSpace.CSpace);
setName = (from meta in container.BaseEntitySets
where meta.ElementType.FullName == className
select meta.Name).FirstOrDefault();
var t = typeof(ObjectContext).GetMethod("Translate", new Type[] { typeof(DbDataReader), typeof(string), typeof(MergeOption) }).MakeGenericMethod(item.GetType()).Invoke(actualContext, new object[] { reader, setName, MergeOption.AppendOnly });
The issue is that I can't do anything with 't' that I want, it's type is listed as
object {System.Data.Entity.Core.Objects.ObjectResult<POCOClass>}. I can't call any of the normal methods that I can normally on the ObjectResult type such as ToArray or ToList.
I need a way to convert it into System.Data.Entity.Core.Objects.ObjectResult<POCOClass>. The difference being that 't' is listed as type object first.
I cannot use any strongly typed casts because the types will change depending on the stored procedure. I've tried using the dynamic keyword instead of var for t and I've also tried using Convert.ChangeType. It never changes from the object base type. dynamic t returns this the following error:
'System.Data.Entity.Core.Objects.ObjectResult<POCOClass>' does not contain a definition for 'ToList'
Thought I know for a fact it does...
To clear up confusion, here's a watch screenshot. The first line is what's being returned, I want it to be like the second (see Type column).
Edit: might be getting closer... I added this:
var listedT = typeof(Enumerable).GetMethod("ToList").MakeGenericMethod(item.GetType()).Invoke(null, new object[] { t });
current.AddRange(listedT); // Error here...
listedT becomes a object {System.Collections.Generic.List<ReportCatalog.Models.Catalog_Reports>} and I get the error, cannot convert from object to System.Collections.Generic.IEnumerable<object>.
Since ObjectResult<T> implements also the non-generic IEnumerable interface, cast it to this type and enumerate it.
var e = (IEnumerable)t;
foreach (object o in e) {
//TODO: use o
}
I have this method, using Expressions to create fields getters:
public static Func<object, T> CreateFieldValueGetter<T>(this Type declaringType, FieldInfo fieldToGet) {
var paramExp = Expression.Parameter(typeof(object));
// ArgumentException if declaringType describes generic-type:
var cast = Expression.Convert(paramExp, declaringType);
var body = Expression.Field(cast, fieldToGet);
return Expression.Lambda<Func<object, T>>(body, paramExp).Compile();
}
It works great until I give it a generic type like:
class DataErrorNotifyingViewModelBase<TErr> : ViewModelBase, INotifyDataErrorInfo
where TErr : struct, IConvertible, IComparable, IFormattable
{
// ...
}
This way:
var vm = new DataErrorNotifyingViewModelBase<MyErrorsTypeEnum> ();
var type = vm.GetType();
// ArgumentException:
var getter = type.CreateFieldValueGetter<PropertyChangedEventHandler>(type.GetField("PropertyChanged"));
This is the exception I get:
Exception thrown: 'System.ArgumentException' in System.Core.dll
Additional information: Type GuiHelpers.DataErrorNotifyingViewModelBase`1[TErr] is a generic type definition
although simple casting works:
var vm = new DataErrorNotifyingViewModelBase<PrintDialogError>();
var obj = (object) vm;
So how can I feed it with generic types? Am I limited to non-generic-types only?
Edit - solution:
Kaveh Hadjari caught it:
Passing t = typeof (Dictionary<T, int>) will raise ArgumentException, as t.GetGenericArguments()[0].IsGenericParameter is true (albeit t.GetGenericArguments()[1].IsGenericParameter is false!)
Passing the type t = typeof (Dictionary<int, int>) works fine, becuse no element of the t.GetGenericArguments() array has IsGenericParameter == true
A generic type is a template for many different specialized types, and at runtime theres a difference between the generic type and the "instanced" types. A possible reason the call to Expression.Convert might be failing could be you're providing it with the type of the generic version and not with a specialized version with type variables set.
Update: I imagine there's a good reason this method would never work with generic types. Consider the case if the type variable is used as type for a field in the generic class. Since the type size (reference, Boolean, short, int, long, etc) could be variable it would mean that it could offset the memory address of other fields in different specializations of the generic class in a variable way. How would you know in advance which field length thus address offset would be the case if all the variables where not set? You couldn't and therefor we can't determine the the address of the field we might want to create a getter for. The only solution would be to actually create a getter that would rely on using reflection on each object you call the getter with, which would incur higher costs than imagined and if you are happy with that solution you might be as well have a single method that gets the value of the field using reflection without actually creating these getters in the first place.
"Am I limited to non-generic-types only?"
No, of course not. The error message is clear, though not consistent with the code example you provided. You seem to be passing for the type the original generic type definition (i.e. with an unspecified value for the type parameter), not the constructed generic type (i.e. with a specified value for the type parameter).
Unfortunately, without a good, minimal, complete code example that reliably reproduces the problem, it's impossible to know precisely what you've done wrong. All I can say is that you did do something wrong. If you want more specific advice than that, please edit your post so that it includes a good code example.
For what it's worth, here's a complete code example that demonstrates your method working fine with a generic type:
class A<T>
{
public int field1;
public T field2;
public event EventHandler Event1;
}
class Program
{
static void Main(string[] args)
{
A<bool> a = new A<bool>();
Func<object, int> field1Getter =
CreateFieldValueGetter<int>(a.GetType(), a.GetType().GetField("field1"));
Func<object, bool> field2Getter =
CreateFieldValueGetter<bool>(a.GetType(), a.GetType().GetField("field2"));
Func<object, EventHandler> event1Getter =
CreateFieldValueGetter<EventHandler>(a.GetType(), a.GetType()
.GetField("Event1", BindingFlags.NonPublic | BindingFlags.Instance));
}
static Func<object, T> CreateFieldValueGetter<T>(Type declaringType, FieldInfo fieldToGet)
{
var paramExp = Expression.Parameter(typeof(object));
// ArgumentException if declaringType describes generic-type:
var cast = Expression.Convert(paramExp, declaringType);
var body = Expression.Field(cast, fieldToGet);
return Expression.Lambda<Func<object, T>>(body, paramExp).Compile();
}
}
The only wrinkle here is that to obtain the field for the event, you have to specify the BindingFlags appropriate to that field (in particular, it's non-public so the default search of GetField() won't find it). The code you showed does this incorrectly, but it doesn't explain the exception you're getting.
Type valueType = Type.GetType("int");
object value = new List<valueType>();
The first line compiles fine, But the 2nd does not.
How can I create a generic list (or call a generic method)
object value = foo<valueType>();
By only having a string representation of the type?
My end goal is actually to take two string "int" and "5 (as an example) and assign the value of 5 to the object [and eventually to the userSettings]. But I have a method that will convert "5" to the actual value if I can tell the generic method it is of type int based on the string representation.
T StringToValue<T>(string s)
{
return (T)Convert.ChangeType(s, typeof(T));
}
Update: I was thinking that creating a generic object and calling a generic method would use the same methodology, but I guess I was wrong. How can I call the generic method?
Type.GetType("int") returns null. This is invalid because int is just a keyword in the C# language, which is equivalent to the type System.Int32. It has no special meaning to the .NET CLR, so it's not usable in reflection. You might have meant typeof(int) or Type.GetType("System.Int32") (or it doesn't really matter, because that was just an example).
Anyway, once you have the right Type, this is how you can get your list. The key is MakeGenericType.
Type valueType = typeof(int);
object val = Activator.CreateInstance(typeof(List<>).MakeGenericType(valueType));
Console.WriteLine(val.GetType() == typeof(List<int>)); // "True" - it worked!
I will share an example from Jeffrey Richter's book CLR Via C# about constructing generic types, this is not specific to the question but will help guide you to finding the appropriate way of doing what you want:
public static class Program {
public static void Main() {
// Get a reference to the generic type's type object
Type openType = typeof(Dictionary<,>);
// Close the generic type by using TKey=String, TValue=Int32
Type closedType = openType.MakeGenericType(typeof(String), typeof(Int32));
// Construct an instance of the closed type
Object o = Activator.CreateInstance(closedType);
// Prove it worked
Console.WriteLine(o.GetType());
}
}
Will display: Dictionary`2[System.String,System.Int32]
try this:
Type valueType = Type.GetType("System.Int32");
Type listType = typeof(List<>).MakeGenericType(valueType);
IList list = (IList) Activator.CreateInstance(listType);
// now use Reflection to find the Parse() method on the valueType. This will not be possible for all types
string valueToAdd = "5";
MethodInfo parse = valueType.GetMethod("Parse", BindingFlags.Public | BindingFlags.Static);
object value = parse.Invoke(null, new object[] { valueToAdd });
list.Add(value);
EDIT: the class/method that i'm trying to run this inside is static and therefore i'm unable to pass this into the generic.Invoke
I have a static Data Access Class that i use to automatically parse data from various sources.
i was starting to re-factor it when i ran into a problem.
Im tring to pass a Type to a Generic method via reflection,
(the method then parses the type and returns the Type with a value)
my code currently looks like
Type type1 = typeof( T );
var item = (T)Activator.CreateInstance( typeof( T ), new object[] { } );
foreach (PropertyInfo info in type1.GetProperties())
{
Type dataType = info.PropertyType;
Type dataType = info.PropertyType;
MethodInfo method = typeof( DataReader ).GetMethod( "Read" );
MethodInfo generic = method.MakeGenericMethod( dataType );
//The next line is causing and error as it expects a 'this' to be passed to it
//but i cannot as i'm inside a static class
generic.Invoke( this, info.Name, reader );
info.SetValue(item,DataReader.Read<dataType>(info.Name, reader ) , null);
}
I guess DataReader.Read is the static method, right?
Therefore, change the error line like below, since you are calling the static method. There is not object, so you just pass null into Invoke method:
var value = generic.Invoke( null, new object[] {info.Name, reader} );
The type parameter to a generic method isn't an instance of Type; you can't use your variable in this way. However, you can use reflection to create the closed-generic MethodInfo you require (that is, with the type parameter specified), which would look something like this:
// this line may need adjusting depending on whether the method you're calling is static
MethodInfo readMethod = typeof(DataReader).GetMethod("Read");
foreach (PropertyInfo info in type1.GetProperties())
{
// get a "closed" instance of the generic method using the required type
MethodInfo genericReadMethod m.MakeGenericMethod(new Type[] { info.PropertyType });
// invoke the generic method
object value = genericReadMethod.Invoke(info.Name, reader);
info.SetValue(item, value, null);
}
Let's say I have such a generic class
public class XClass<T, U>
{
public void MethodA<V>(){}
}
How could I get the type of
XClass<int,>
not hard-coded, not limiting to MakeGenericType method as below.
------ detailed elaboration below using MakeGenericType ------
I can get the type of the unbound and open class "XClass<,>" and its open method:
var type = typeof(XClass<,>);
Console.WriteLine(String.Format("Type ZClass<,>: \t generic? {0} \t open? {1}"
, type.IsGenericType, type.IsGenericTypeDefinition));
var method = type.GetMethod("MethodA");
Console.WriteLine(String.Format("Method MethodA<>: \t generic? {0} \t open? {1}"
, method.IsGenericMethod, method.IsGenericMethodDefinition));
Also, I can get the type of full closed class
XClass <int, char>
and its close method:
var type = typeof(XClass<,>);
var method = type.GetMethod("MethodA");
var fullType = method.DeclaringType.MakeGenericType(new[]{typeof(int), typeof(char)});
Console.WriteLine(String.Format("Type ZClass<int,char>: \t generic? {0} \t open? {1}"
, fullType.IsGenericType, fullType.IsGenericTypeDefinition));
var fullTypeOpenMethod = fullType.GetMethod("MethodA");
var fullMethod = fullTypeOpenMethod.MakeGenericMethod(typeof(string));
Console.WriteLine(String.Format("Method MethodA<String>:\t generic? {0} \t open? {1}"
, fullMethod.IsGenericMethod, fullMethod.IsGenericMethodDefinition));
Now, How can I get the type of bound but open class
XClass<int, >
and its method?
var type = typeof(XClass<,>);
var method = type.GetMethod("MethodA");
Type [] types = new Type[2];
types[0] = typeof(int);
types[1] = null; // what shall i put here?
var halffullType = method.DeclaringType.MakeGenericType(types);
If I put types[1] as null, an ArgumentNullException exception will throw "Value cannot be null".
What should I do?
What you are proposing to do is impossible and also will not really help you.
Impossible because...
The documentation states (emphasis mine) that
Types constructed with MakeGenericType can be open, that is, some of
their type arguments can be type parameters of enclosing generic
methods or types.
This means that you cannot make a Type object representing XClass<int,>. What you can do is:
class Outer<TOuter>
{
class XClass<T, U> {}
}
In this situation, you can make a Type object representing Outer<TOuter>.XClass<int, TOuter>. But there needs to be an enclosing generic class.
Not useful because...
The documentation also states (referring to a similar example to the above) that:
A constructed type such as Base is useful when emitting code,
but you cannot call the MakeGenericType method on this type because it
is not a generic type definition. To create a closed constructed type
that can be instantiated, first call the GetGenericTypeDefinition
method to get a Type object representing the generic type definition
and then call MakeGenericType with the desired type arguments.
Which means that if you have
Type myType = ... // represents Outer<TOuter>.XClass<int, TOuter>
Then to get a Type for XClass<int, string> you would first need to call myType.GetGenericTypeDefinition() (thus losing the int information) and then call MakeGenericType to put it back in (along with the string type parameter). So it's like one step back and two steps forward.
Alternatives
You might want to consider storing the type parameter types for XClass in a separate data structure (e.g. a Type[]) for as long as not all type parameters are known to you, and then create the closed generic type in one go after you have collected all of them.
You can also package all this into a small helper class for convenience:
class GenericTypeDescription
{
private readonly Type openGenericType;
private readonly Type[] typeParameters;
public GenericTypeDescription(Type openGenericType)
{
// add checks for openGenericType actually being what it says here
this.openGenericType = openGenericType;
this.typeParameters = new Type[openGenericType.GetGenericArguments().Length];
}
public void SetTypeParameter(int index, Type type) {
// add error handling to taste
this.typeParameters[index] = type;
}
public Type ConstructGenericType() {
// add error handling to taste
return this.openGenericType.MakeGenericType(this.typeParameters);
}
}
No, this isn't possible.
See my similar question: Does .Net support curried generics?
This is possible, when you feed MakeGenericType with its own generic arguments.
var type = typeof(XClass<,>);
var method = type.GetMethod("MethodA");
Type[] types = new Type[2];
types[0] = typeof(int);
types[1] = type.GetGenericArguments()[1]; // Use the open parameter type
var openConstructedType = type.MakeGenericType(types);
This will populate openConstructedType with a Type of XClass<int,U>.
Note that the type will have ContainsGenericParameters, so it won't be constructable, and there's no way to populate the open parameters.
I don't think that this is possible without inheriting from the class.
What you seem to be trying is to basically do this via reflection:
typeof(XClass<int,>)
This would be half-closed... and only possible by inheritance AFAIK:
class XClassInt<U>: XClass<int, U> {}
This second code allows you to get typeof(XClassInt<>).BaseType which is kind of what you want. However, in that case the second type argument for XClass<,> is not null but actually U (the type argument coming from XClassInt<>).
See also this MSDN page.
Edit: Here's my testbed for this:
public class C1<A,B> {}
public class C2<B>: C1<int, B> {}
[...]
Type baseType = typeof(C2<>).BaseType;
WL(baseType);
WL(baseType.GetGenericArguments()[0]);
Type arg1 = baseType.GetGenericArguments()[1];
WL(arg1);
WL(arg1.DeclaringType);
WL(arg1.GenericParameterPosition);
WL(arg1.IsGenericParameter);
Running this yields:
C1`2[System.Int32,B]
System.Int32
B
C2`1[B]
0
True
However, as I said, I believe this is only possible because the base type is closed with the generic type argument of the open generic type C2.