I want to select the right generic method via reflection and then call it.
Usually this is quite easy. For example
var method = typeof(MyType).GetMethod("TheMethod");
var typedMethod = method.MakeGenericMethod(theTypeToInstantiate);
However the issue start when there are different generic overloads of the method. For example the static-methods in the System.Linq.Queryable-class. There are two definitions of the 'Where'-method
static IQueryable<T> Where(this IQueryable<T> source, Expression<Func<T,bool>> predicate)
static IQueryable<T> Where(this IQueryable<T> source, Expression<Func<T,int,bool>> predicate)
This meand that GetMethod doesn't work, because it cannot destiguish the two. Therefore I want to select the right one.
So far I often just took the first or second method, depending on my need. Like this:
var method = typeof (Queryable).GetMethods().First(m => m.Name == "Where");
var typedMethod = method.MakeGenericMethod(theTypeToInstantiate);
However I'm not happy with this, because I make a huge assumption that the first method is the right one. I rather want to find the right method by the argument type. But I couldn't figure out how.
I tried it with passing the 'types', but it didn't work.
var method = typeof (Queryable).GetMethod(
"Where", BindingFlags.Static,
null,
new Type[] {typeof (IQueryable<T>), typeof (Expression<Func<T, bool>>)},
null);
So has anyone an idea how I can find the 'right' generic method via reflection. For example the right version of the 'Where'-method on the Queryable-class?
You can somewhat elegantly select a specific generic overload of a method at compile-time, without passing any strings to run-time searches like the other answers here do.
Static Methods
Suppose you have multiple static methods of the same name like:
public static void DoSomething<TModel>(TModel model)
public static void DoSomething<TViewModel, TModel>(TViewModel viewModel, TModel model)
// etc
If you create an Action or Func that matches the generic count and parameter count of the overload you're looking for, you can select it at compile-time with relatively few acrobatics.
Example: Select the first method - returns void, so use an Action, takes one generic. We use object to avoid specifying type just yet:
var method = new Action<object>(MyClass.DoSomething<object>);
Example: Select the second method - returns void, so Action, 2 generic types so use type object twice, once for each of the 2 generic parameters:
var method = new Action<object, object>(MyClass.DoSomething<object, object>);
You just got the method you wanted without doing any crazy plumbing, and no run-time searching or usage of risky strings.
MethodInfo
Typically in Reflection you want the MethodInfo object, which you can also get in a compile-safe way. This is when you pass the actual generic types you want to use in your method. Assuming you wanted the second method above:
var methodInfo = method.Method.MakeGenericMethod(type1, type2);
There's your generic method without any of the reflection searching or calls to GetMethod(), or flimsy strings.
Static Extension Methods
The specific example you cite with Queryable.Where overloads forces you to get a little fancy in the Func definition, but generally follows the same pattern. The signature for the most commonly used Where() extension method is:
public static IQueryable<TModel> Where<TModel>(this IQueryable<TModel>, Expression<Func<TModel, bool>>)
Obviously this will be slightly more complicated - here it is:
var method = new Func<IQueryable<object>,
Expression<Func<object, bool>>,
IQueryable<object>>(Queryable.Where<object>);
var methodInfo = method.Method.MakeGenericMethod(modelType);
Instance Methods
Incorporating Valerie's comment - to get an instance method, you'll need to do something very similar. Suppose you had this instance method in your class:
public void MyMethod<T1>(T1 thing)
First select the method the same way as for statics:
var method = new Action<object>(MyMethod<object>);
Then call GetGenericMethodDefinition() to get to the generic MethodInfo, and finally pass your type(s) with MakeGenericMethod():
var methodInfo = method.Method.GetGenericMethodDefinition().MakeGenericMethod(type1);
Decoupling MethodInfo and Parameter Types
This wasn't requested in the question, but once you do the above you may find yourself selecting the method in one place, and deciding what types to pass it in another. You can decouple those 2 steps.
If you're uncertain of the generic type parameters you're going to pass in, you can always acquire the MethodInfo object without them.
Static:
var methodInfo = method.Method;
Instance:
var methodInfo = method.Method.GetGenericMethodDefinition();
And pass that on to some other method that knows the types it wants to instantiate and call the method with - for example:
processCollection(methodInfo, type2);
...
protected void processCollection(MethodInfo method, Type type2)
{
var type1 = typeof(MyDataClass);
object output = method.MakeGenericMethod(type1, type2).Invoke(null, new object[] { collection });
}
One thing this especially helps with is selecting a specific instance method of a class, from inside the class, then exposing that to outside callers who need it with various types later on.
Addendum
A number of comments below say they cannot get this to work. It might not be surprising that I don't often have to select a generic method like this, but I happen to be doing so today, in well-tested code used behind the scenes all the time, so I thought I'd provide that real-world example - and perhaps it will help those who struggle to get this to work.
C# lacks a Clone method, so we have our own. It can take a number of arguments, including those that explain how to recursively copy IEnumerable properties inside the source object.
The method that copies an IEnumerable is named CopyList, and looks like this:
public static IEnumerable<TTo> CopyList<TTo>(
IEnumerable<object> from,
Func<PropertyInfo, bool> whereProps,
Dictionary<Type, Type> typeMap
)
where TTo : new()
{
To complicate things (and flex the muscles of this approach), it has several overloads, like this one:
public static IEnumerable<TTo> CopyList<TTo>(
IEnumerable<object> from,
Dictionary<Type, Type> typeMap
)
where TTo : new()
{
So, we've got several (I'm only showing you 2, but there are more in the code) method signatures. They have the same number of Generic arguments, but a different number of method arguments. The names are identical. How are we possibly going to call the right method? Begin the C# ninjaing!
var listTo = ReflectionHelper.GetIEnumerableType(
fromValue.GetType());
var fn = new Func<
IEnumerable<object>,
Func<PropertyInfo, bool>,
Dictionary<Type, Type>,
IEnumerable<object>>(
ModelTransform.CopyList<object>);
var copyListMethod = fn.GetMethodInfo()
.GetGenericMethodDefinition()
.MakeGenericMethod(listTo);
copyListMethod.Invoke(null,
new object[] { fromValue, whereProps, typeMap });
The first line uses a helper method we'll come back to, but all it's doing is getting the generic type of the IEnumerable list in this property, and assigning it to listTo. The next line is where we really begin performing this trick, where we lay out a Func with adequate parameters to match up with the specific CopyList() overload we intend to grab. Specifically, the CopyList() we want has 3 arguments, and returns IEnumerable<TTo>. Remember that Func takes its return type as its last generic arg, and that we're substituting object wherever there's a generic in the method we intend to grab. But, as you can see in this example, we do not need to substitute object anywhere else. For example, we know we want to pass a where clause that accepts a PropertyInfo and returns true/false (bool), and we just say those types right in the Func.
As the constructor arg to the Func, we pass CopyList() - but remember that the name CopyList is vague because of the method overloads. What's really cool is that C# is doing the hard work for you right now, by looking at the Func args, and identifying the right one. In fact, if you get the types or number of args wrong, Visual Studio will actually mark the line with an error:
No overload for 'CopyList' matches delegate 'Func...'
It's not smart enough to tell you what exactly you need to fix, but if you see that error you're close - you need to carefully double-check the args and return type and match them up exactly, replacing Generic args with object.
On the third line, we call the C# built-in .GetMethodInfo() and then .MakeGeneric(listTo). We have only one Generic to set for this, so we pass that in as listTo. If we had 2, we'd pass 2 args here. These Type args are replacing the object substitutions we made earlier.
And that's it - we can call copyListMethod(), with no strings, fully compile-safe. The final line makes the call, first passing null because it's a static method, then an object[] array with the 3 args. Done.
I said I'd come back to the ReflectionHelper method. Here it is:
public static Type GetIEnumerableType(Type type)
{
var ienumerable = type.GetInterface(typeof(System.Collections.Generic.IEnumerable<>).FullName);
var generics = ienumerable.GetGenericArguments();
return generics[0];
}
It can be done, but it's not pretty!
For example, to get the first overload of Where mentioned in your question you could do this:
var where1 = typeof(Queryable).GetMethods()
.Where(x => x.Name == "Where")
.Select(x => new { M = x, P = x.GetParameters() })
.Where(x => x.P.Length == 2
&& x.P[0].ParameterType.IsGenericType
&& x.P[0].ParameterType.GetGenericTypeDefinition() == typeof(IQueryable<>)
&& x.P[1].ParameterType.IsGenericType
&& x.P[1].ParameterType.GetGenericTypeDefinition() == typeof(Expression<>))
.Select(x => new { x.M, A = x.P[1].ParameterType.GetGenericArguments() })
.Where(x => x.A[0].IsGenericType
&& x.A[0].GetGenericTypeDefinition() == typeof(Func<,>))
.Select(x => new { x.M, A = x.A[0].GetGenericArguments() })
.Where(x => x.A[0].IsGenericParameter
&& x.A[1] == typeof(bool))
.Select(x => x.M)
.SingleOrDefault();
Or if you wanted the second overload:
var where2 = typeof(Queryable).GetMethods()
.Where(x => x.Name == "Where")
.Select(x => new { M = x, P = x.GetParameters() })
.Where(x => x.P.Length == 2
&& x.P[0].ParameterType.IsGenericType
&& x.P[0].ParameterType.GetGenericTypeDefinition() == typeof(IQueryable<>)
&& x.P[1].ParameterType.IsGenericType
&& x.P[1].ParameterType.GetGenericTypeDefinition() == typeof(Expression<>))
.Select(x => new { x.M, A = x.P[1].ParameterType.GetGenericArguments() })
.Where(x => x.A[0].IsGenericType
&& x.A[0].GetGenericTypeDefinition() == typeof(Func<,,>))
.Select(x => new { x.M, A = x.A[0].GetGenericArguments() })
.Where(x => x.A[0].IsGenericParameter
&& x.A[1] == typeof(int)
&& x.A[2] == typeof(bool))
.Select(x => x.M)
.SingleOrDefault();
This question is about 2 years old, but I came up with (what I think is) an elegant solution, and thought I'd share it with the fine folks here at StackOverflow. Hopefully it will help those who arrive here via various search queries.
The problem, as the poster stated, is to get the correct generic method. For example, a LINQ extension method may have tons of overloads, with type arguments nested inside other generic types, all used as parameters. I wanted to do something like this:
var where = typeof(Enumerable).GetMethod(
"Where",
typeof(IQueryable<Refl.T1>),
typeof(Expression<Func<Refl.T1, bool>>
);
var group = typeof(Enumerable).GetMethod(
"GroupBy",
typeof(IQueryable<Refl.T1>),
typeof(Expression<Func<Refl.T1, Refl.T2>>
);
As you can see, I've created some stub types "T1" and "T2", nested classes within a class "Refl" (a static class which contains all my various Reflection utility extension functions, etc. They serve as placeholders for where the type parameters would have normally went. The examples above correspond to getting the following LINQ methods, respectively:
Enumerable.Where(IQueryable<TSource> source, Func<TSource, bool> predicate);
Enumerable.GroupBy(IQueryable<Source> source, Func<TSource, TKey> selector);
So it should be clear that Refl.T1 goes where TSource would gone, in both of those calls; and the Refl.T2 represents the TKey parameter.The TX classes are declared as such:
static class Refl {
public sealed class T1 { }
public sealed class T2 { }
public sealed class T3 { }
// ... more, if you so desire.
}
With three TX classes, your code can identify methods containing up to three generic type parameters.
The next bit of magic is to implement the function that does the search via GetMethods():
public static MethodInfo GetMethod(this Type t, string name, params Type[] parameters)
{
foreach (var method in t.GetMethods())
{
// easiest case: the name doesn't match!
if (method.Name != name)
continue;
// set a flag here, which will eventually be false if the method isn't a match.
var correct = true;
if (method.IsGenericMethodDefinition)
{
// map the "private" Type objects which are the type parameters to
// my public "Tx" classes...
var d = new Dictionary<Type, Type>();
var args = method.GetGenericArguments();
if (args.Length >= 1)
d[typeof(T1)] = args[0];
if (args.Length >= 2)
d[typeof(T2)] = args[1];
if (args.Length >= 3)
d[typeof (T3)] = args[2];
if (args.Length > 3)
throw new NotSupportedException("Too many type parameters.");
var p = method.GetParameters();
for (var i = 0; i < p.Length; i++)
{
// Find the Refl.TX classes and replace them with the
// actual type parameters.
var pt = Substitute(parameters[i], d);
// Then it's a simple equality check on two Type instances.
if (pt != p[i].ParameterType)
{
correct = false;
break;
}
}
if (correct)
return method;
}
else
{
var p = method.GetParameters();
for (var i = 0; i < p.Length; i++)
{
var pt = parameters[i];
if (pt != p[i].ParameterType)
{
correct = false;
break;
}
}
if (correct)
return method;
}
}
return null;
}
The code above does the bulk of the work -- it iterates through all the Methods in a particular type, and compares them to the given parameter types to search for. But wait! What about that "substitute" function? That's a nice little recursive function that will search through the entire parameter type tree -- after all, a parameter type can itself be a generic type, which may contain Refl.TX types, which have to be swapped for the "real" type parameters which are hidden from us.
private static Type Substitute(Type t, IDictionary<Type, Type> env )
{
// We only really do something if the type
// passed in is a (constructed) generic type.
if (t.IsGenericType)
{
var targs = t.GetGenericArguments();
for(int i = 0; i < targs.Length; i++)
targs[i] = Substitute(targs[i], env); // recursive call
t = t.GetGenericTypeDefinition();
t = t.MakeGenericType(targs);
}
// see if the type is in the environment and sub if it is.
return env.ContainsKey(t) ? env[t] : t;
}
Another solution that you might find useful - it is possible to get a MethodInfo based on Expression.Call that already has a logic for overload resolution.
For example, in case you need to get some specific Enumerable.Where method that could be accomplished using the following code:
var mi = Expression.Call(typeof (Enumerable), "Where", new Type[] {typeof (int)},
Expression.Default(typeof (IEnumerable<int>)), Expression.Default(typeof (Func<int, int, bool>))).Method;
Third argument in the example - describes types of generic arguments, and all other arguments - types of parameters.
In the same way it is possible to get even non static object generic methods.You need to change only first argument from typeof (YourClass) to Expression.Default(typeof (YourClass)).
Actually, I have used that approach in my plugin for .NET Reflection API. You may check how it works here
Chris Moschini's answer is good when you know the method name in compile time. Antamir's answer works if we get method name in runtime, but is quite an overkill.
I am using another way, for which I got inspiration using reflector from .NET function Expression.Call, which selects correct generic method from a string.
public static MethodInfo GetGenericMethod(Type declaringType, string methodName, Type[] typeArgs, params Type[] argTypes) {
foreach (var m in from m in declaringType.GetMethods()
where m.Name == methodName
&& typeArgs.Length == m.GetGenericArguments().Length
&& argTypes.Length == m.GetParameters().Length
select m.MakeGenericMethod(typeArgs)) {
if (m.GetParameters().Select((p, i) => p.ParameterType == argTypes[i]).All(x => x == true))
return m;
}
return null;
}
Usage:
var m = ReflectionUtils.GetGenericMethod(typeof(Queryable), "Where", new[] { typeof(Person) }, typeof(IQueryable<Person>), typeof(Expression<Func<Person, bool>>));
If you need only generic method definition or simply do not know the type T at the time, you can use some bogus types and then strip the generic's information:
var m = ReflectionUtils.GetGenericMethod(typeof(Queryable), "Where", new[] { typeof(object) }, typeof(IQueryable<object>), typeof(Expression<Func<object, bool>>));
m = m.GetGenericMethodDefinition();
Let the compiler do it for you:
var fakeExp = (Expression<Func<IQueryable<int>, IQueryable<int>>>)(q => q.Where((x, idx) => x> 2));
var mi = ((MethodCallExpression)fakeExp.Body).Method.GetGenericMethodDefinition();
for the Where with index, or simply leave out the second parameter in the Where expression for the one without
In additional to #MBoros's answer.
You can avoid writing complex generic arguments using this helper method:
public static MethodInfo GetMethodByExpression<Tin, Tout>(Expression<Func<IQueryable<Tin>, IQueryable<Tout>>> expr)
{
return (expr.Body as MethodCallExpression).Method;
}
Usage:
var where = GetMethodByExpression<int, int>(q => q.Where((x, idx) => x > 2));
Or
var select = GetMethodByExpression<Person, string>(q => q.Select(x => x.Name));
Use DynamicMethods.GenericMethodInvokerMethod, GetMethod is not enough to use with generics
I made a little helper func:
Func<Type, string, Type[], Type[], MethodInfo> getMethod = (t, n, genargs, args) =>
{
var methods =
from m in t.GetMethods()
where m.Name == n && m.GetGenericArguments().Length == genargs.Length
let mg = m.IsGenericMethodDefinition ? m.MakeGenericMethod(genargs) : m
where mg.GetParameters().Select(p => p.ParameterType).SequenceEqual(args)
select mg
;
return methods.Single();
};
Works for simple non-generics:
var m_movenext = getMethod(typeof(IEnumerator), nameof(IEnumerator.MoveNext), Type.EmptyTypes, Type.EmptyTypes);
Like for complicated generics:
var t_source = typeof(fillin1);
var t_target = typeof(fillin2);
var m_SelectMany = getMethod(
typeof(Enumerable),
nameof(Enumerable.SelectMany),
new[] {
t_source,
t_target
},
new[] {
typeof(IEnumerable<>).MakeGenericType(t_source),
typeof(Func<,>).MakeGenericType(t_source, typeof(IEnumerable<>).MakeGenericType(t_target))
});
I have a similar issue and I thought I would post my solution here. I'm trying to call several functions:
p.Foo<Klass1>(true)
p.Foo<Klass2>(true)
p.Foo<Klass3>(true)
bool k1 = p.Bar<Klass1>()
bool k2 = p.Bar<Klass2>()
bool k3 = p.Bar<Klass3>()
My solution:
public static TAction RemapGenericMember<TAction>(object parent, Type target, TAction func) where TAction : Delegate {
var genericMethod = func?.Method?.GetGenericMethodDefinition()?.MakeGenericMethod(target);
if (genericMethod.IsNull()) {
throw new Exception($"Failed to build generic call for '{func.Method.Name}' with generic type '{target.Name}' for parent '{parent.GetType()}'");
}
return (TAction)genericMethod.CreateDelegate(typeof(TAction), parent);
}
And now I can call:
foreach(var type in supportedTypes) {
InvokeGenericMember<Action<bool>>(p, type, Foo<object>)(true);
bool x = InvokeGenericMember<Function<bool>>(p, type, Bar<object>)();
}
Antamir's answer was very useful for me, but it has a bug in that it doesn't validate that the number of parameters on the method found matches the number of types passed in when you provide a mix of generic and concrete types.
For example, if you ran:
type.GetMethod("MyMethod",typeof(Refl.T1),typeof(bool))
it can't differentiate between two methods:
MyMethod<T>(T arg1)
MyMethod<T>(T arg1, bool arg2)
The two calls to:
var p = method.GetParameters();
should be changed to:
var p = method.GetParameters();
if (p.Length != parameters.Length)
{
correct = false;
continue;
}
Also, both of the existing 'break' lines should be 'continue'.
I found out the easiest way to use iQuerable expressions while calling method using reflection. Please see below code:
You can use the IQuerable expression as per requirement.
var attributeName = "CarName";
var attributeValue = "Honda Accord";
carList.FirstOrDefault(e => e.GetType().GetProperty(attributeName).GetValue(e, null) as string== attributeValue);
var firstGenericParam = Type.MakeGenericMethodParameter(0);
var firstParam = typeof(IQueryable<>).MakeGenericType(firstGenericParam);
var funcType = typeof(Func<,>).MakeGenericType(firstGenericParam, typeof(bool));
//var funcType = typeof(Func<,,>).MakeGenericType(firstGenericParam, typeof(int), typeof(bool)); //for second version
var secondParam = typeof(Expression<>).MakeGenericType(funcType);
var method = typeof(Queryable).GetMethod(nameof(Queryable.Where), new Type[] { firstParam, secondParam });
Related
The ultimate objective is to have a piece of code that you can feed definitions of simple queries to and it generate data.
so given i can create this query
var query =
from cha in ds.Channels
select new object[]
{
cha.ChanLanguagestreamlocking,
cha.ChanMinimumtrailerduration,
from link in cha.Channelaudiolanguagelinks
select new object[]
{
link.ObjDatecreated
}
};
var data = query.ToArray();
how would i create it dynamically?
well i can get this far using a class stolen from another stack overflow question
public class SelectList<TSource>
{
private List<LambdaExpression> members = new List<LambdaExpression>();
public SelectList<TSource> Add<TValue>(Expression<Func<TSource, TValue>> selector)
{
members.Add(selector);
return this;
}
public Expression<Func<TSource, object[]>> ToDynamicColumns()
{
var parameter = Expression.Parameter(typeof(TSource), "e");
return Expression.Lambda<Func<TSource, object[]>>(
Expression.NewArrayInit(
typeof(object),
members.Select(m =>
Expression.Convert(Expression.Invoke(m, parameter), typeof(object))
)
),
parameter);
}
}
Imagine a case where the definition of which 'columns' to return is passed as a tree at runtime, this can then be mapped to predefined typessafe lambdas that define which columns to return and then the expression is created at runtime. So a hard coded version of this technique is this:
var channelColumns = new SelectList<Channel>();
channelColumns.Add(c => c.ChanLanguagestreamlocking);
channelColumns.Add(c => c.ChanMinimumtrailerduration);
var channelQuery =
ds.Channels.Select(channelColumns.ToDynamicColumns());
var bar = query.ToArray();
i.e. i can generate dynamic queries from the 'root' concept, but how do i generate the nested data.
If i do the obvious i.e. this
var audioColumns = new SelectList<Channelaudiolanguagelink>();
audioColumns.Add(a => a.ObjDatecreated);
var channelColumns = new SelectList<Channel>();
channelColumns.Add(c => c.ChanLanguagestreamlocking);
channelColumns.Add(c => c.ChanMinimumtrailerduration);
// next line causes an error
// Error CS1929 'ICollection<Channelaudiolanguagelink>' does not contain a definition for
// 'Select' and the best extension method overload
// 'Queryable.Select<Channel, object[]>(IQueryable<Channel>, Expression<Func<Channel, object[]>>)' requires a receiver of type 'IQueryable<Channel>' CSharpDb2Raw C:\Users\mark.nicholls\source\repos\scaffold2\CSharpDb2Raw\Program.cs 57 Active
channelColumns.Add(c => c.Channelaudiolanguagelinks.Select(channelColumns.ToDynamicColumns()));
var channelQuery =
ds.Channels.Select(channelColumns.ToDynamicColumns());
var bar = query.ToArray();
the error makes perfect sense.
c.Channelaudiolanguagelinks is an ICollection and so the select is looking for a Func<T,U> and I've given it an Expression<Func<T,U>>
(I don't really understand Expressions!)
Problem that you have defined method for IQueryable version of Select, so basic solution is simple - transform IEnumerable to IQueryable.
channelColumns.Add(c =>
c.Channelaudiolanguagelinks.AsQueryable().Select(audioColumns.ToDynamicColumns())
);
I have an IQueryable<T> from my DbSet in Entity Framework. I am provided a "Fuzzy Search String", named searchText, like so:
public List<T> Search<T>(string searchText)
{
using (var context = ...)
{
var baseQuery = context.Set<T>().AsQueryable();
baseQuery = baseQuery.Where(x =>
DbFunctions.Like(x.PropertyName, searchText)
|| DbFunctions.Like(x.PropertyTwo, searchText)
|| DbFunctions.Like(x.PropertyThree, searchText)
|| DbFunctio..... etc
);
return baseQuery.ToList();
}
}
But given the generic nature, I don't know what properties there are on the type. I can provide an abstract method to somebody implementing this which allows them to give me a List of Properties (or even PropertyInfo or whatever else, I can figure that out). But I don't know how to dynamically create the expression. This is what I have so far:
var baseQuery = context.Set<T>().AsQueryable();
var expression = baseQuery.Expression;
var colName = "colName"; // Or names, I can iterate.
var parameter = Expression.Parameter(typeof(T), "x");
var selector = Expression.PropertyOrField(parameter, colName);
expression = Expression.Call(typeof(DbFunctions), nameof(DbFunctions.Like),
new Type[] { baseQuery.ElementType, selector.Type },
expression, Expression.Quote(Expression.Lambda(selector, parameter)));
The problem here is... well, it doesn't work to begin with. But mainly that I'm not using the searchText anywhere in it, and don't know how to plug it in. I THINK I'm close... but have spent an inordinate amount of time on it.
Hopefully I'm getting your query logic right: if you want to build a set of LIKE conditions based on known type and list of column names, you could try something like this:
static private MethodInfo dbLikeMethod = typeof(DbFunctions).GetMethod(nameof(DbFunctions.Like), BindingFlags.Public | BindingFlags.Static, null, new Type[] {typeof(string), typeof(string)}, null); // I am targeting DbFunctions.Like(string, string). You might want another overload (or even mix them up depending on your inputs)
public List<T> Search<T>(string searchText) where T: class
{
using (var context = new ...)
{
var baseQuery = context.Set<T>().AsQueryable().Where(CreateExpression<T>(searchText));// you could probably find a more elegant way of plugging it into your query
return baseQuery.ToList();
}
}
Expression<Func<T, bool>> CreateExpression<T>(string searchText) where T : class
{
var cols = new List<string> {
"PropertyName",
"PropertyTwo" // i understand you've got a way to figure out which strings you need here
};
var parameter = Expression.Parameter(typeof(T), "x");
var dbLikeCalls = cols.Select(colName => Expression.Call(dbLikeMethod, Expression.PropertyOrField(parameter, colName), Expression.Constant(searchText))); // for convenience, generate list of DbFunctions.Like(x.<Property>, searchText) expressions here
var aggregatedCalls = dbLikeCalls.Skip(1).Aggregate((Expression)dbLikeCalls.First(), (accumulate, call) => Expression.OrElse(accumulate, call)); // aggregate the list using || operators: use first item as a seed and keep adding onto it
return Expression.Lambda<Func<T, bool>>(aggregatedCalls, parameter);
}
Let's say I have a generic method (retrieved from parent type with reflection):
void MyType::MyMethod<T>(T[], int, int)
I want to know if I can invoke that method on some type list:
byte[], int32, int32
Obviously the answer is yes.
However I'd like to find a generic way to find the best overload (among several generics) matching a parameter list.
I can easily get all methods:
MyType.GetMethods().Where(m => m.Name == "MyMethod")
then filter to match parameter count:
.Where(m => m.GetParameters().Count() == 3)
but then I'm blocked.
[EDIT AS REQUESTED IN COMMENTS]
var methods = typeof(Array).GetMethods();
var sortMethods = methods.Where(m => m.Name == "Sort");
var candidates = sortMethods.Where(m => m.GetParameters().Count() == 3);
var parameterTypes = new Type[] { typeof(byte).MakeArrayType(), typeof(Int32), typeof(Int32) };
foreach(var m in candidates)
{
// find the best match
}
I get 4 methods :
And I don't know how to programmatically find the best match.
I recently had an ugly bug whereby my collection unexpectedly had a reverse order.
public class WidgetContainer : IWidgetContainer
{
// ...
public void Add(IWidget widget, int? index = null)
{
if (!index.HasValue)
_collection.Add(widget);
else
_collection.Insert(index.Value, widget);
}
}
The calling code was not specifying the index. So, for all intents and purposes, this code should have been working, inserting elements sequentially.
But it didn't.
Then I looked at the interface:
public interface IWidgetContainer
{
void Add(IWidget widget, int? index = 0);
}
Boom.
The calling code resolved the instance by interface, so 0 was used instead of null.
No compiler errors, no warnings - nothing. Can I enable them somewhere?
If not, can I automatically detect and prevent such issues, possibly with a solution test? Mono.Cecil, Reflection are all acceptable.
Applying to an assembly:
Assembly
.GetExecutingAssembly()
.GetTypes()
.Where(t => t.IsClass)
.Select(GetDefaultParameterValuesMismatch)
.Where(m => m.Count() > 0);
IEnumerable<(string Interface, string Class, string Method, string Parameter, object InterfaceParameterValue, object ClassParameterValue)>
GetDefaultParameterValuesMismatch(Type type)
{
var interfaceParameterValues = type
.GetTypeInfo()
.ImplementedInterfaces
.SelectMany(i => i.GetMethods().Select(m => new { Type = i.Name, m }))
.SelectMany(t => t.m.GetParameters().Select(p => new
{
Type = t.Type,
Method = t.m.Name,
Parameter = p.Name,
p.DefaultValue
}));
var classParameterValues = type
.GetTypeInfo()
.GetMethods()
.SelectMany(m => m.GetParameters().Select(p => new
{
Type = type.Name,
Method = m.Name,
Parameter = p.Name,
p.DefaultValue
}));
return interfaceParameterValues
.Zip(classParameterValues, (t1, t2) => new { t1, t2 })
.Where(typePair => !object.Equals(typePair.t1.DefaultValue, (typePair.t2.DefaultValue)))
.Select(typePair => (Interface: typePair.t1.Type,
Class: typePair.t2.Type,
Method: typePair.t1.Method,
Parameter: typePair.t1.Parameter,
InterfaceParameterValue: typePair.t1.DefaultValue,
ClassParameterValue: typePair.t2.DefaultValue));
}
The calling code resolved the instance by interface, so 0 was used instead of null.
Well, yes. If the call is someIWidgetContainerTypedReference.Add(widget); then the call will obviously be resolved to IWidgetContainerTypedReference.Add.
Now, the way default arguments work is that the compiler essential converts someIWidgetContainerTypedReference.Add(widget) to someIWidgetContainerTypedReference.Add(widget, 0) at the callsite. Thats the key point here, optional arguments aren't "processed" inside the called method, they are directly baked into the call itself, so the optional argument specificed in WidgetContainer.Add is ignored altogether.
You have an extremely bad situation in your hands because your WidgetContainer class is essentially "breaking" the interface contract.
The best solution? Make your class implement exactly the interface, including optional argument default values. A good question is why the compiler doesn't enforce this (IMHO it should).
If you want to only check if the default parameters are different you can use this code:
var parameter = typeof(WidgetContainer).GetMethod("Add").GetParameters().FirstOrDefault(p => p.Name == "index");
var iparameter = typeof(IWidgetContainer).GetMethod("Add").GetParameters().FirstOrDefault(p => p.Name == "index");
if(!object.Equals(parameter.DefaultValue, iparameter.DefaultValue))
{
// they are different
}
This can be used within unit tests and production code as well, but I would recommend using it inside of some test classes.
This could be done in a more generic way which could be more usefull for your case :
void PerformCheck(Type classType, Type interfaceType)
{
var methods = interfaceType.GetMethods().Where(m => m.GetParameters().Any(p => p.IsOptional));
foreach(var method in methods)
{
var optionalParameters = method.GetParameters().Where(p => p.IsOptional);
foreach(var parameter in optionalParameters)
{
var classParam = classType.GetMethod(method.Name).GetParameters().FirstOrDefault(p => p.Name == parameter.Name);
if(!object.Equals(classParam.DefaultValue, parameter.DefaultValue))
{
Console.WriteLine("method " + method.Name + " has different defaul values on parameter " + parameter.Name);
}
}
}
}
Then you can use it in some test application and just call the above code like this :
var types = typeof(WidgetContainer).Assembly.GetTypes().Where(t => t.GetInterfaces().Any());
foreach(var type in types)
{
var interfaces = type.GetInterfaces();
foreach(var i in interfaces)
{
performCheck(type, i);
}
}
Try this online
You could also use an SonarLint extension for Visual Studio. In such case a warning with the code RSPEC-1006 "Method overrides should not change parameter defaults" would be generated.
I have some code to sync Entity Framework POCOs between different database servers (source server is SQL Server, destination is MySQL). It is written to work generically using the [Key] attribute and the fact that the POCOs representing the data being synced know how to compare themselves.
The code is currently as follows:
var srcdbset = setprop.GetValue(src, null); var dstdbset = setprop.GetValue(dst, null);
var tabletype = srcdbset.GetType().GetGenericArguments().First();
var keys = tabletype.GetProperties().Where(p => p.GetCustomAttributesData().FirstOrDefault(a => a.AttributeType.Name == "KeyAttribute") != null).ToList();
var param = Expression.Parameter(tabletype); // TODO - support compound keys
var dt = typeof(Func<,>).MakeGenericType(tabletype, keys[0].PropertyType);
var df = Expression.Lambda(dt, Expression.Property(param, keys[0].Name), param).Compile();
var qtodict = typeof(Enumerable).GetMethods().Where(m => m.Name == "ToDictionary").First();
var gtodict = qtodict.MakeGenericMethod(new[] { tabletype, keys[0].PropertyType });
var srcdict = ((IDictionary)gtodict.Invoke(null, new object[] { srcdbset, df }));
var dstdict = ((IDictionary)gtodict.Invoke(null, new object[] { dstdbset, df }));
var qexcept = typeof(Enumerable).GetMethods().Where(m => m.Name == "Except").First();
var gexcept = qexcept.MakeGenericMethod(new[] { keys[0].PropertyType });
dynamic snotd = gexcept.Invoke(null, new object[] { srcdict.Keys, dstdict.Keys });
dynamic dnots = gexcept.Invoke(null, new object[] { dstdict.Keys, srcdict.Keys });
A little help - src is the source DbContext and dst is the destination DbContext and setprop is the PropertyInfo object of the DbSet being synced.
This question really isn't so much about Entity Framework, but linq and reflection. As you can see the TODO comment says - "support compound keys" - the code above works fine for POCOs with just one key, but to support composite keys, the lambda expression needs to change from something like:
dbcontext.Accounts.ToDictionary(a => a.AccountID);
to something like:
dbcontext.OntLocations.ToDictionary(l => Tuple.Create(l.OntID, l.LocationID, l.Index);
The two linq expressions immediately above are obviously written in a non generic way to make things simpler to explain - my question is - how do I write a generic lambda that does the tuple create? If anyone could point me in the right direction on that, I think the rest of the code would work as is.
Also, you are probably thinking - why aren't they using transactional replication - long story short - unable to find a product that works reliably - if anyone knows of one that works well from SQL Server to MySQL and doesn't require much/any downtime to the SQL Server to install I'm all ears.
The answer by #Dave M is fine, but the whole thing can be greatly simplified by emitting a call to one of the Tuple.Create method overloads using the following handy Expression.Call method overload
public static MethodCallExpression Call(
Type type,
string methodName,
Type[] typeArguments,
params Expression[] arguments
)
which will do the most of the work for you:
var source = Expression.Parameter(tabletype, "e");
var key = Expression.Call(typeof(Tuple), "Create",
keys.Select(pi => pi.PropertyType).ToArray(), // generic type arguments
keys.Select(pi => Expression.MakeMemberAccess(source, pi)).ToArray() // arguments
);
var keySelector = Expression.Lambda(key, source).Compile();
Later on, key.Type property can be used to get the tuple type.
First you will need to get the type of tuple you need to create, this has to be hard-coded per number of keys since the Tuple class is actually a different class depending on the number of items:
Type tupleType = null;
if(keys.Count == 1) tupleType = typeof(Tuple<>);
else if(keys.Count == 2) tupleType = typeof(Tuple<,>);
else if(keys.Count == 3) tupleType = typeof(Tuple<,,>);
else if(keys.Count == 4) tupleType = typeof(Tuple<,,,>);
//and so on
tupleType = tupleType.MakeGenericType(keys.Select(t=>t.PropertyType).ToArray());
Now you can make the Func as you had it above:
var lambdaFuncType = typeof(Func<,>).MakeGenericType(tabletype, tupleType);
Now build the expression to create the tuple. We'll need the tuple constructor and a property accessor expression for each key.
var parameterExpression = Expression.Parameter(tabletype);
var constructorExpression = Expression.New(tupleType.GetConstructors()[0],
keys.Select(t=>Expression.Property(parameterExpression, t.Name)).ToArray());
Now make the full lambda and compile it:
var compiledExpression = Expression.Lambda(lambdaFuncType, constructorExpression, parameterExpression).Compile();