Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
226 views
in Technique[技术] by (71.8m points)

c# - Attribute Constructor With Lambda

It is possible to do this:

public static void SomeMethod<TFunc>(Expression<TFunc> expr)
{
    //LambdaExpression happily excepts any Expession<TFunc>
    LambdaExpression lamb = expr;
}

and call it elsewhere passing a lambda for the parameter:

SomeMethod<Func<IQueryable<Person>,Person>>( p=>p.FirstOrDefault());

I would instead like to pass an expression as a parameter to an attribute constructor. Is it possible to do the below?

class ExpandableQueryAttribute: Attribute {
    private LambdaExpression someLambda;
    //ctor
    public ExpandableQueryMethodAttribute(LambdaExpression expression) 
    {
        someLambda = expression
    } 
}

//usage:
static LambdaExpression exp = 
      (Expression<Func<IQueryable<Person>, Person>>)
        (p => p.FirstOrDefault());

[ExpandableQueryAttribute(exp)]   //error here
// "An attribute argument must be a constant expression, typeof expression
// or array creation expression of an attribute parameter type"

My goal is to specify a method or lambda in the constructor of the attribute(even if I have to declare a full named method and pass the name of the method somehow, that'd be fine to).

  1. Parameter types can change, but it is important that the attribute constructor can take that parameter and in some way be able to assign it to a field of type LambdaExpression

  2. I want the declaration of the lambda/method to be just above the call to the attribute constructor, or inline, so that you don't have to go far to see what is being passed.

So these alternatives would be fine, but no luck getting them to work:

public static ... FuncName(...){...}

[ExpandableQueryAttribute(FuncName)]   
// ...

or

//lambdas aren't allowed inline for an attribute, as far as I know
[ExpandableQueryAttribute(q => q.FirstOrDefault())]   
// ...

The existing work around is to pass a number ID to the constructor(satisfying the "argument must be a constant" requirement), which is used by the constructor to do a lookup in a dictionary where expressions have been added previously. Was hoping to improve/simplify this, but I have a feeling it doesn't get any better due to limitations on attribute constructors.

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

how about this:

    class ExpandableQueryAttribute : Attribute
    {

        private LambdaExpression someLambda;
        //ctor
        public ExpandableQueryAttribute(Type hostingType, string filterMethod)
        {
            someLambda = (LambdaExpression)hostingType.GetField(filterMethod).GetValue(null); 
            // could also use a static method
        }
    }

this should let you assign your lambda to a field and then suck it in at runtime, although in general I would prefer to use something like PostSharp to do this at compile time.

simple usage example

    public class LambdaExpressionAttribute : Attribute
    {
        public LambdaExpression MyLambda { get; private set; }
        //ctor
        public LambdaExpressionAttribute(Type hostingType, string filterMethod)
        {
            MyLambda = (LambdaExpression)hostingType.GetField(filterMethod).GetValue(null);
        }
    }

    public class User
    {
        public bool IsAdministrator { get; set; }
    }

    public static class securityExpresions
    {
        public static readonly LambdaExpression IsAdministrator = (Expression<Predicate<User>>)(x => x.IsAdministrator);
        public static readonly LambdaExpression IsValid = (Expression<Predicate<User>>)(x => x != null);

        public static void CheckAccess(User user)
        {
            // only for this POC... never do this in shipping code
            System.Diagnostics.StackTrace stackTrace = new System.Diagnostics.StackTrace();
            var method = stackTrace.GetFrame(1).GetMethod();

            var filters = method.GetCustomAttributes(typeof(LambdaExpressionAttribute), true).OfType<LambdaExpressionAttribute>();
            foreach (var filter in filters)
            {
                if ((bool)filter.MyLambda.Compile().DynamicInvoke(user) == false)
                {
                    throw new UnauthorizedAccessException("user does not have access to: " + method.Name);
                }
            }

        }
    }

    public static class TheClass
    {
        [LambdaExpression(typeof(securityExpresions), "IsValid")]
        public static void ReadSomething(User user, object theThing)
        {
            securityExpresions.CheckAccess(user);
            Console.WriteLine("read something");
        }

        [LambdaExpression(typeof(securityExpresions), "IsAdministrator")]
        public static void WriteSomething(User user, object theThing)
        {
            securityExpresions.CheckAccess(user);
            Console.WriteLine("wrote something");
        }

    }


    static void Main(string[] args)
    {

        User u = new User();
        try
        {
            TheClass.ReadSomething(u, new object());
            TheClass.WriteSomething(u, new object());
        }
        catch(Exception e) 
        {
            Console.WriteLine(e);
        }
    }

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

1.4m articles

1.4m replys

5 comments

57.0k users

...