Lambdas in Custom Attributes

I want to pursue the concept of embedding lamdbas in custom attributes. It is not supported today to my knowledge but I believe could be very valuable for DLINQ and more. 

public class Customer
{
  [Association((c, o) => c.CustomerID == o.CustomerID && o.Status == OrderStatus.Incomplete)]
  public Order[] IncompleteOrders;
}


I believe that a syntax such as this would generalize the join conditions that are supported by AssociationAttribute and allow for additional conditions. In that way, queries need not repeat those conditions. 

Note that regardless of whether DLINQ adopts this, there is a deeper issue of supporting attribute properties of delegate type (in this case Func<P, F, bool>).  This might touch the runtime and is thus off the table I presume.



Answer this question

Lambdas in Custom Attributes

  • JavierPrg

    A significant benefit of this syntax is the elimination of the OtherKey property on [Association].  It is a weakly-typed reference to a property name.  Clearly, a lambda can provide the same reference in a strongly-typed manner, and contributes to the main objective of LINQ to reduce weak typing.


  • Mikhail Podolski

    The suggestion implies that the join conditions must be folded into the query.  Today, DLINQ generates queries using only the WHERE clause (that is, pre-SQL92 syntax) for specifying join conditions.  The modern JOIN..ON syntax could be used to precisely manifest the conditions specified on the [Association] attribute. 

    My point is that this separation of concerns might ease the generation work.


  • Saptagiri

    1st, What's Phoenix

    I was having some back and forth conversations with Sonja Keserovic, one of the CLR PMs about this a few months ago, here's the product feedback suggestion:

    http://lab.msdn.microsoft.com/productfeedback/viewfeedback.aspx feedbackid=9c4af9f4-5717-4755-b715-53d7581ecf2b

    Partially this was for AOPish style injection (preconditions, logging, instrumentation), however there are a large number of applications.

    The current Expression model in LINQ is immutable, once an expression tree has been created you cannot modify it other than creating a new tree.

    Structs cannot be const, only the intrinsic types and strings support const:
    http://msdn.microsoft.com/library/en-us/csspec/html/vclrfcsharpspec_10_3.asp (10.3 Const declaration)
    http://msdn.microsoft.com/library/default.asp url=/library/en-us/csspec/html/vclrfcsharpspec_4_1.asp (4.1 Value Types)

    Static readonlys also cannot be passed to Attributes, e.g. you must use "" and not string.Empty.

    A Token to a method or methodhandle could be passed to an attribute however this information is only known at compile time by the compiler, therefore having the compiler interpret a lambda to a static readonly expression tree and some hidden internal retrieval method referenced by a token is entirely plausible.

  • MarkGrant

  • Roberto Santos

    ... or a delegate to a method that returned an expression tree.

    But I agree, less-than-optimal syntax.

  • Co&amp;#235;ndou

    > public class Customer
    > {
    >   [Association((c, o) => c.CustomerID == o.CustomerID && o.Status > == OrderStatus.Incomplete)]
    >   public Order[] IncompleteOrders;
    > }

    Where do the parameters c and o come from How does compiler knows, that c is of type Customer and o of type Order

  • bertcord

    Right.. So I think the question is one of making some read-only trees.  That should allow them to be safely used for this (for example, you wouldn't be able to access the tree at runtime to make modifications to it).  I'm not certain off-hand how to write a struct in such a way that it's considered const-able.  Actually, I don't think I've even seen such a thing mentioned.

    Compiler enhancements: Sounds like what I imagine Phoenix would do.  What did Abrams have to say


  • infrandom

    This doesn't entail a change to the runtime, since it's not a Func<> delegate, but the other thing (which I can't recall its name right now, the ASTstuff).
    This shouldn't be a problem, and it's looking much better than the strings.  

  • Ahmed Amin El-Morali

    I like the idea of specifying join conditions as expression trees.< xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office:office" />

     

    I’ve created a test copy of the project, we create at my firm, and start to replace our ORM classes and database access through them with DLinq.

     

    I’m already stuck with the problem, that I can’t add some extra condition to association condition (like “o.Status == OrderStatus.Incomplete” in the example above).

     

    So I have my proposal. If we can’t use expression trees in attributes, then why not create the expressions as static read-only variables, and in the attribute specify the name of the variable

     

    Something like this:

     

      class AssociationCondition<ThisType, OtherType>

        where ThisType : class

        where OtherType : class

      {

        public AssociationCondition(

          Expression<Func<ThisType, OtherType, bool>> innerJoinCondition)

        {

        }

     

        public AssociationCondition(

          Expression<Func<ThisType, OtherType, bool>> innerJoinCondition,

          Expression<Func<OtherType, bool>> whereCondition)

        {

        }

      }

     

      class Customer

      {

        [Column]

        public string CustomerID;

     

        private static readonly AssociationCondition<Customer, Order>
          AllOrdersCondition =
    new AssociationCondition<Customer, Order>(

            (c, o) => c.CustomerID == o.CustomerID

          );

     

        private static readonly AssociationCondition<Customer, Order>
          IncompleteOrdersCondition =
    new AssociationCondition<Customer, Order>(

            (c, o) => c.CustomerID == o.CustomerID,

            o => o.OrderStatus == OrderStatus.Incomplete

          );

     

        [Association(Condition = "AllOrdersCondition") /* and other properties*/]

        EntitySet<Order> _AllOrders;

     

        [Association(Condition = "IncompleteOrdersCondition")]

        EntitySet<Order> _IncompleteOrders;

      }

     

      class Order

      {

        [Column]

        public string CustomerID;

        [Column]

        public OrderStatus OrderStatus;

      }

     

      enum OrderStatus

      {

        Incomplete

      }


  • bdee1

    The main issue with CLR here is that Attributes can only take constants as arguments.  This could "possibly" be done though using something like a RuntimeMethodHandle.

    When you declare an attribute that takes in a Type argument and use typeof() the compiler injects the RuntimeTypeHandle for the type as if it were a constant, making that a legal case.

    An expression tree is not a constant, and therefore could not be part of an attribute argument.  The RuntimeMethodHandle to the generated delegate for a lambda could be passed, but might defeat the need to place an expression tree.

    The custom compiler could inject a static method that returns Expression for the expression tree and pass in the RuntimeMethodHandle for the static method to the argument and that is a possiblity, but as to whether that is a legal implementation I am unsure.

  • Andy_Webber_

    This should be completely feasible, given that attributes are themselves classes, and an attribute use leads to an instantiation.

    Other uses I can see:  validation, pre- and post- conditions, etc.



  • Federico_Star

    I think it could be relatively clean.  The last part of my previous comment was exactly what you're mentioning here.  Gen a static readonly field that holds the expression and a runtimemethodhandle to a delegate that returns the generated expression.  If its all hidden and behind the scenes it might be ok.  The drawback is that Attributes in .NET are expected to be pretty static, single-init with the class, etc.  having an attribute that drives off a method is somewhat dangerous territory.

    I was talking with Abrams at PDC about a CLR or compiler enhancement for 3.0 for a Compile-Time attribute that could inject code before, inside and after any signature at compile time that could possibly perform the same type of thing

  • Brian Merz

    How about:



    public class Customer
    {
       [Association<Customer, Order>((c, o) => c.CustomerID == o.CustomerID && o.Status >= OrderStatus.Incomplete)]
       public Order[] IncompleteOrders;
    }

     


  • snymanr

    Phoenix:  see the last article in the current MSDN.  There's a (relatively secret) project at MSR, but they have some bits viewable: http://research.microsoft.com/act/#projects

    Re static readonly:  static readonly != immutable, which may be a source of that problem.

    Re const:  I remember now.. Const are inlined.  Can't do that with something that may contain a ref field and expect the ref to be maintained.

    Of course, I don't know the issues deep enough to know why one couldn't load a tree into an attribute.


  • Lambdas in Custom Attributes