Updating Expression Parameter - c#

Here's my code:
public static Response<TResult> Create<TResult>(Expression<Func<Response<TResult>>> method)
{
var objectMember = Expression.Convert(((MethodCallExpression)method.Body).Arguments[0], typeof(TResult));
var getterLambda = Expression.Lambda<Func<TResult>>(objectMember);
var getter = getterLambda.Compile();
TResult myObject = getter();
// Do something to 'myObject'
// Call original method with updated 'myObject' as the parameter
}
As you can see from the code snippet above, I have a generic method that accepts a method as a parameter.
Orginally, I was calling method.Compile()() which worked fine. However, I now need to update the parameter ("myObject") first before calling the method. So, I've added the lines above to get the parameter.
I then "Do something to 'myObject'". Finally, I want to call the original passed method, but replace the original parameter with the new, updated parameter.
What am I missing?
UPDATE
So, I've, technically, accomplished my need by the following code:
Response<TResult> result = (Response<TResult>)((MethodCallExpression)method.Body).Method.Invoke(parentType, new object[] { myObject });
But this is using a lot of reflection and casting. Is there a better way to do this?
Thanks,
Joshua

Related

System.Reflection.AmbiguousMatchException: 'Ambiguous match found.'

I am trying to get the MethodInfo from a method TableExists<T> so I can call it with a type.
The method is declared inside OrmLiteSchemaApi class. There are 2 overloads:
public static bool TableExists<T>(this IDbConnection dbConn)
{
// code omitted
}
public static bool TableExists(this IDbConnection dbConn, string tableName, string schema = null)
{
// code omitted
}
I am trying to get the MethodInfo like this:
var tableMethod = typeof(OrmLiteSchemaApi).GetMethod("TableExists");
But it generates exception:
System.Reflection.AmbiguousMatchException: 'Ambiguous match found.'
I could only find an old question related to this that suggested to pass an empty object array as parameter but this doesn't seem to work for .net core.
I guess I need to specify the specific overload but I am not sure exactly how.
How do I get the MethodInfo?
You can use GetMethods (plural!) to get an array of all matching methods, and then look for the one which has IsGenericMethod:
var tm = typeof(OrmLiteSchemaApi)
.GetMethods()
.Where(x => x.Name == "TableExists")
.FirstOrDefault(x => x.IsGenericMethod);
I recommend this over using parameter specifiers, since it'll give you an object you can step through at debug time if there are ever any problems.
Passing an empty object array would only work if you're looking for a function with no parameters. Instead, you need to use a different overload of GetMethod that specifies the types of parameters as a type array. That way you can tell it which reference to get by specifying which types of parameters it should look for.

Getting correct return value on a method call using reflection

I have the following generic method inside my class that works as a repository pattern:
public DbSet<T> GetAll<T>() where T : class
{
return dbContext.Set<T>();
}
Now, i would like to get a list of all entities in the database that belong to an entity class that implements a specific interface (IChangeTrackingEntity). So currently there are around 10 specific tables/classes that conform to this, but i don't want to add 10 hardcoded calls to these tables, so I would like to do it using reflection instead (it might also be that the classes that implement this interface change in the future and I don't want to have to remember to change here as well and make the code dependant on each other).
Example of code that works, but that i don't want:
var result = new List<IChangeTrackingEntity>();
using ( var repository = new DbRepository())
{
result.AddRange( repository.GetAll<FirstTypeThatImplementsInterface>() );
result.AddRange( repository.GetAll<SecondTypeThatImplementsInterface>() );
result.AddRange( repository.GetAll<ThirdTypeThatImplementsInterface>() );
result.AddRange( repository.GetAll<FourthTypeThatImplementsInterface>() );
result.AddRange( repository.GetAll<FifthTypeThatImplementsInterface>() );
}
return result;
I am quite close, but I can't get the last part to work of casting the result of the Invoke back to the correct type. Waht i got currently is this:
var result = new List<IChangeTrackingEntity>();
var method = typeof (DbRepository).GetMethod("GetAll");
using ( var repository = new DbRepository())
{
foreach (var p in typeof(AnchorDbContext).GetProperties())
{
if (p.PropertyType.IsGenericType && p.PropertyType.GetGenericTypeDefinition() == typeof(DbSet<>))
{
var pType = p.PropertyType.GetGenericArguments()[0];
if (pType.GetInterface("IChangeTrackingEntity") != null)
{
var genericMethod = method.MakeGenericMethod(new[] {pType});
result.AddRange(genericMethod.Invoke(repository, null) as DbSet<IChangeTrackingEntity>);
}
}
}
return result;
}
The problem above it that the Invoke call return a object and I need to cast it to basically DbSet<pType>.
In my current code genericMethod.Invoke(repository, null) as DbSet<IChangeTrackingEntity> returns null indicating that I can't cast the return value as I want, so I need the specific return value type and not the interface I think.
Any idea of how to accomplish this?
Try casting to IEnumerable<IChangeTrackingEntity>. This should work due to co/contravariance.
I don't know much about this specific issue, but you seem to be casting from DbSet<T> where T : IChangeTrackingEntity to DbSet<IChangeTrackingEntity>. This is called covariance or contravariance (I always get confused between them...) and it only works if DbSet<> is an interface. So, casting won't work here. Use an equivalent interface if you can, or make a generic method that accepts DbSet where T: IChangeTrackingEntity and returns DbSet<IChangeTrackingEntity> somehow. I'll try to work out how to do that, and post an answer, if no one has answered before me (unlikely on this site :P)
I'm thinking you need to see this question:
MethodInfo method = typeof(Sample).GetMethod("GenericMethod");
MethodInfo generic = method.MakeGenericMethod(myType);
generic.Invoke(this, null);

Get delegate arguments inside delegate

Could someone please help me to understand how to get all parameters passed to delegate inside delegate itself?
I have class :
public class ShopManager : ShopEntities
{
public ShopManager getWhere(Func<Object, Object> dataList)
{
var x = dataList.???; // how to get arguments?
return this;
}
public Object getLike(Object dataValue)
{
return dataValue;
}
}
Then i call it as :
ShopManager shopManager = new ShopManager()
var demo = shopManager.getWhere(xxx => shopManager.getLike("DATA"));
The question is : how to get passed parameters "xxx" and "DATA" inside method getWhere()?
Thanks in advance.
You can't because it's the other way around. You can't get the arguments because the delegate does not hold them; the getWhere method will need to pass a value for the xxx parameter when invoking the delegate. The anonymous method that the delegate refers to will then receive this value as the xxx parameter, and in turn pass the string "DATA" as argument for the dataValue parameter when calling getLike. The argument values as such are not part of the delegate's state.
If you want to get information about the parameters as such (not their values), you can do that:
// get an array of ParameterInfo objects
var parameters = dataList.Method.GetParameters();
Console.WriteLine(parameters[0].Name); // prints "xxx"
If you use:
public ShopManager getWhere(Expression<Func<Object, Object>> dataList)
then you can divide the Expression into its subexpressions and parse them. But I'm not sure if using a delegate like you do is even the right thing.
You can't do it (easily). But I don't understand your idea. For what reason do you need to look into a dataList? This is just an anonymous method, you can call it and get results, you shouldn't need to examine or modify it at all.
What is your idea? Why not just call shopManager.getLike() ?
you can get the name of function by doing something like below.
var x = dataList.GetInvocationList().FirstOrDefault().Method.GetParameters();
sring name = x.FirstOrDefault().Name
this will print name as 'xxx'
Arguments are what you will provide while invoking the delegate via dataList(args), and not by the recipient of the invocation. If you want to provide additional information to getWhere() , you can try the following ....
public ShopManager getWhere(Func<Object, Object> dataList, params object[] additonalData)
{
// inspect the additionalData
}
Thanks for replies guys, i decided to use Expression> instead of common delegate. This allows to get both sides of expression - LHS and RHS.
For those who are interested in answer, this is it :
http://social.msdn.microsoft.com/Forums/en-US/csharpgeneral/thread/0f6ca823-dbe6-4eb6-9dd4-6ee895fd07b5?prof=required
Thanks for patience and attention.
public static List<object> GetMethodParameterValues(Delegate method)
{
var target = method.Target;
if (target == null) return null;
var fields = target.GetType().GetFields();
var valueList = fields.Select(field => field.GetValue(target)).ToList();
return valueList;
}

How do you dynamially invoke a property, but only if its argument is a certain type?

I am trying to use reflection to get all the (settable) properties in my POCO (which take a string argument, for now, but I plan to expand to other types), and set them to something arbitrary. (I need to make sure the .Equals method is implemented properly.)
I have some code in my unit tests that looks something like this (where t is the object under test, and u is a default version of that object):
foreach(var property in t.GetType().GetProperties())
{
var setMethod = property.GetSetMethod();
var type = setMethod.GetParameters()[0].GetType();
if(typeof(string).IsAssignableFrom(type))
{
setMethod.Invoke(t, new object[] {"a"});
Assert.IsFalse(t.Equals(u));
Assert.IsFalse(t.GetHashCode() == u.GetHashCode());
}
}
The place where this fails is where I say typeof(string).IsAssignableFrom(type). The code inside the if { ... } block never runs. How would I properly code up this part of the test?
You have confused ParameterInfo.GetType() with ParameterInfo.ParameterType. You should have:
var type = setMethod.GetParameters()[0].ParameterType;
.GetType() returns the Type of the current object, in this case ParameterInfo, which is obviously not what you want.

Pass method, created with reflection, as Func parameter

I've got a method (fyi, I'm using c#), accepting a parameter of type "Func", let's say it's defined as such:
MethodAcceptingFuncParam(Func<bool> thefunction);
I've defined the function to pass in as such:
public bool DoStuff()
{
return true;
}
I can easily call this as such:
MethodAcceptingFuncParam(() => { return DoStuff(); });
This works as it should, so far so good.
Now, instead of passing in the DoStuff() method, I would like to create this method through reflection, and pass this in:
Type containingType = Type.GetType("Namespace.ClassContainingDoStuff");
MethodInfo mi = containingType.GetMethod("DoStuff");
=> this works, I can get the methodinfo correctly.
But this is where I'm stuck: I would now like to do something like
MethodAcceptingFuncParam(() => { return mi.??? });
In other words, I'd like to pass in the method I just got through reflection as the value for the Func param of the MethodAcceptingFuncParam method. Any clues on how to achieve this?
You can use Delegate.CreateDelegate, if the types are appropriate.
For example:
var func = (Func<bool>) Delegate.CreateDelegate(typeof(Func<bool>), mi);
MethodAcceptingFuncParam(func);
Note that if the function is executed a lot in MethodAcceptingFuncParam, this will be much faster than calling mi.Invoke and casting the result.
Use Invoke:
MethodAcceptingFuncParam(() => { return (bool)mi.Invoke(null, null); })

Categories