Я пытаюсь получить название метода на типе с помощью лямбда-выражения. Я использую Windows Identity Foundation и потребность определить политики доступа с именем типа с пространством имен как ресурс и имя метода как действие. Вот пример.
Это - тип, от которого я получил бы имя типа и имя метода:
namespace My.OrderEntry {
public class Order {
public void AddItem(string itemNumber, int quantity) {}
}
}
Это - то, как я хотел бы определить политику доступа через DSL:
ForResource<Order>().Performing(o => o.AddItem).AllowUsersHaving(new Claim());
От того оператора я хотел бы получить "Мой. OrderEntry. Порядок" как ресурс и "AddItem" как действие. Получение имени типа с пространством имен не является никакой проблемой, но я не думаю, что могу использовать лямбду для метода как, я пытаюсь сделать.
public static IPermissionExp Performing<T>(
this IActionExp<T> exp,
Func<T, delegate???> action) {} //this is where I don't know what to define
Этот вид вещи даже возможно сделать? Там другой путь состоит в том, чтобы сделать этот вид вещи, не используя волшебные строки?
Это можно сделать двумя способами:
1: вы можете создавать перегрузки, которые принимают различные делегаты Func
и Action
(например, Expression
. Обратите внимание, что вызывающим абонентам потребуется явно указать общие параметры либо в вызове метода, либо путем создания делегата. Это будет использоваться следующим образом:
ForResource<Order>().Performing(o => new Action<string>(o.AddItem)).AllowUsersHaving(new Claim());
2: Вы можете взять Expression
, которое содержит вызов метода, и проанализировать вызываемую MethodInfo
из дерева выражений. Это будет использоваться следующим образом:
ForResource<Order>().Performing(o => { o.AddItem(null); }).AllowUsersHaving(new Claim());
Недавно я сделал кое-что на работе, где вы определили метод, используя лямбда, имя которой затем принял внутренний объект. Вы также можете использовать строки или передать MethodInfo, но первый тип не является безопасным по типу (и опечатки представляют большой риск), а последний не очень элегантен.
В основном у меня был такой метод (это не точный метод, он немного более продвинутый):
public void SetRequest(Request req, Expression<Func<Service, Func<long, IEnumerable<Stuff>>> methodSelector);
Ключевым моментом здесь является «Выражение», оно позволяет вам «выбрать» такой метод:
SetRequest(req, service => service.SomeMethodTakingLongReturningStuffs);
Селектор метода представляет собой дерево выражений, из которого затем можно извлекать различные биты данных. Я не помню точно, как выглядит получившееся дерево, это также зависит от того, как выглядят ваши лямбды.
Вместо этого вы можете передать его как действие, которое не приводит к принудительному возвращению типа. Однако это все еще немного запутано, потому что вам нужно передать некоторые аргументы методу, чтобы он скомпилировался.
Похоже, это то, что вам нужно, если вы хотите, чтобы имя метода делегата действия передавалось в функцию Performing.
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
}
Это позволит вам вызывать метод следующим образом...
ForResource<Order>().Performing((o, a, b) => o.AddItem(a, b)).AllowUsersHaving(new Claim());