17

I'm trying to get the name of a method on a type using a lambda expression. I'm using Windows Identity Foundation and need to define access policies with the type name with namespace as a resource and the method name as the action. Here is an example.

This is the type I would be getting the type name and method name from:

namespace My.OrderEntry { public class Order { public void AddItem(string itemNumber, int quantity) {} } } 

This is how I would like to define the access policy through a DSL:

ForResource<Order>().Performing(o => o.AddItem).AllowUsersHaving(new Claim()); 

From that statement, I would like to get "My.OrderEntry.Order" as the resource and "AddItem" as the action. Getting the type name with namespace is no problem, but I don't think I can use a lambda for a method like I'm trying to do.

public static IPermissionExp Performing<T>( this IActionExp<T> exp, Func<T, delegate???> action) {} //this is where I don't know what to define 

Is this sort of thing even possible to do? Is there another way to do this sort of thing without using magic strings?

6
  • What is AddItem supposed to add? Commented Feb 22, 2010 at 21:36
  • It doesn't really matter what AddItem does, I just need the name of the method without using magic strings. Commented Feb 22, 2010 at 21:38
  • It kind of does matter. I think where you're heading is an Action<delegate> but I don't think that's the best answer. Commented Feb 22, 2010 at 21:42
  • Is it possible to do Action<delegate>? I really just need to get the method name to define whether the user is allowed to call that method (action) on that type (resource). Commented Feb 22, 2010 at 21:47
  • Ok, I get what you're trying to do now. One more question - where are the values being passed to AddItem going to come from? Commented Feb 22, 2010 at 22:05

4 Answers 4

7

There are two ways to do this:

1: You could make overloads that take the various Func and Action delegates(eg Expression<Func<T, Func<TParam1,TParam2, TReturn>>. Note that your callers would need to specify the generic parameters explicitly, either in the method call or by creating the delegate. This would be used like this:

ForResource<Order>().Performing(o => new Action<string>(o.AddItem)).AllowUsersHaving(new Claim()); 

2: You could take an Expression<Action> that contains a method call, and parse out the MethodInfo being called from the expression tree. This would be used like this:

ForResource<Order>().Performing(o => { o.AddItem(null); }).AllowUsersHaving(new Claim()); 
Sign up to request clarification or add additional context in comments.

4 Comments

for 1) could I use "object" instead of "TParam1"? for 2) any way to do that without specifying the null argument?
+1. I'm unable to come up with a more general version than your second example really.
@Wili: 1) No - delegates are not covariant. 2) No.
Not as clean as I wanted, but I was pretty sure what I wanted wasn't possible. The second example is pretty close and I decided to go with that.
3

It looks like this is what you are looking for if you want the name of the action delegate method passed in to the Performing function.

public static IPermissionExp Performing<T>( this IActionExp<T> exp, Expression<Action<T, string, int>> action) { var expression = action.Body as MethodCallExpression; string actionMethodName = string.Empty; if (expression != null) { actionMethodName = expression.Method.Name; } // use actionMethodName ("AddItem" in the case below) here } 

This would allow you to call the method like this...

ForResource<Order>().Performing((o, a, b) => o.AddItem(a, b)).AllowUsersHaving(new Claim()); 

1 Comment

Of course though, this requires the arguments being known and stated by the caller. Obviously, not your #1 preference given you comments on other answers :-(
1

I recently did a thing at work where you defined the a method using a lambda, which the internal object then took the name of. You could use strings as well, or pass in a MethodInfo but the first one isn't really type safe (and typos are a big risk), and the latter is not very elegant.

Basically I had a method like this (this is not the exact method, it is a bit more advanced):

public void SetRequest(Request req, Expression<Func<Service, Func<long, IEnumerable<Stuff>>> methodSelector); 

The key here is the "Expression" thing, this lets you "select" a method like this:

SetRequest(req, service => service.SomeMethodTakingLongReturningStuffs); 

Method selector is made into a expression tree which you can then fetch different bits of data from. I don't recall exactly what the resulting tree looks like, it also depends on how your lambdas look.

5 Comments

I would have to know the method signature for that. If I have methods of different signatures, I would have to use different expressions for every signature I come across.
Yes, you could use Expression<Action> or Expression instead.
I have Permorming<T>(this IActionExp<T> exp, Func<T, Expression<Action>> action). That doesn't compile.
I was incorrect in my previous comment, Expression cannot be used. However Expression<Action> can. If you look at my example the signature of the delegate to be made into a an expression tree needs to be specified as the type argument of Expression<>. So you would need to have Performing<T>(this IActionExp<T> exp, Expression<Action> action)... SLaks answer adequately shows how you would go about calling it. I don't think it can be made more "general" and prettier than his way though if you don't want to specify the signature of the method.
Another "solution" would be to specify a ton of Performing-overloads with different type arguments that would cover a wide range of possible method signatures. It's not pretty implementation wise but it is an alternative.
0

You could pass it in as a Action instead, which doesn't force any return type. It is still a little messy though, because you have to pass some arguments to the method in order for it to compile.

1 Comment

Action would work, but I really don't care about the arguments. Could I do something like: Func<T, Action>, Func<T, Action<object>>, Func<T, Action<object, object>>

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.