locked
NotSupportedException for Lambda Function RRS feed

  • Question

  • Hi,

    I'm trying to simply my code, & eliminate repetition & generalise it a bit, so I decided to create this extension method, that I could hopefully use on any EntitySet.

    So, for example,

    dim result = Me.DataWorkspace.ApplicationData.Customers.ValueFound("Test") would return true if a Customer with a Name property that had a value of "Test" existed, & false if none existed.

    Here's the extension method's code (I know callin the parameter "this" is not required, I could call it anything I liked, I just made it match C#'s "this"):

        <Extension()>
        Public Function ValueFound(Of T As IEntityObject)(this As EntitySet(Of T) _
            , value As String _
            , Optional propertyName As String = "Name" _
            ) As Boolean
            Dim result As Boolean = Nothing
     
            result = (this.Where(Function(x As T) x.Details.Properties(propertyName).ToString = value).FirstOrDefault IsNot Nothing)
     
            Return result
        End Function

    Can anyone tell me why this is resulting in a NotSupportedException for the expression?

    This is the full error description:

    "The expression not supported.   Expression: value(LightSwitchApplication.CentralData).Genders.Where(x => (CompareString(x.Details.Properties.get_Item(value(EntitySetExtensions+_Closure$__1[LightSwitchApplication.Gender]).$VB$Local_propertyName).ToString(), value(EntitySetExtensions+_Closure$__1[LightSwitchApplication.Gender]).$VB$Local_value, False) == 0)).Take(1)"

    Any ideas on how to make this work, if it's possible, would be appreciated.

    TIA,

    Yann

    Thursday, April 21, 2011 6:05 AM

Answers

  • Hi Yann,
    Your code throws a NotSupportedException because it ends up trying to pass x.Details.Properties(propertyName) to the data provider to execute.  The data providers do know about the LightSwitch constructs.  I think what you are trying to do is possible with two different approaches. 
    The first approach would be to evaluate your expression in memory versus passing it to the data provider.  I believe this can be done by replacing this.Where(... with this.GetQuery().Execute().Where(...  This obviously has a performance cost to it because it will force the EntitySet to be loaded into memory.
    An alternative solution would be to build an Expression dynamically that the data providers would understand.  In otherwords you can write code that will dynamically build up the expression you want such as Customers.Where(c => c.Name == "Test")
    public static bool ValueFound<TEntity>(this EntitySet<TEntity> entitySet, string propertyName, object value) 
      where TEntity : IEntityObject
    {
      ParameterExpression param = Expression.Parameter(typeof(TEntity), "e");
      Expression<Func<TEntity, bool>> expression = 
        Expression.Lambda<Func<TEntity, bool>>(
          Expression.Equal(
            Expression.Property(param, propertyName),
            Expression.Constant(value)),
          param);
      return entitySet.Where(expression).FirstOrDefault() != null;
    }
    
    or
     
     
        <Extension()> _
        Public Function ValueFound(Of TEntity As IEntityObject)(ByVal entitySet As EntitySet(Of TEntity), ByVal propertyName As String, ByVal value As Object) As Boolean
          Dim param As ParameterExpression = Expression.Parameter(GetType(TEntity), "e")
          Dim expr As Expression(Of Func(Of TEntity, Boolean)) = Expression.Lambda(Of Func(Of TEntity, Boolean))( _
           Expression.Equal( _
            Expression.Property(param, propertyName), _
            Expression.Constant(value)), _
           param)
          Return (Not entitySet.Where(expr).FirstOrDefault() Is Nothing)
        End Function
    
    HTH
     
    Thursday, April 21, 2011 2:38 PM

All replies

  • Hi Yann,
    Your code throws a NotSupportedException because it ends up trying to pass x.Details.Properties(propertyName) to the data provider to execute.  The data providers do know about the LightSwitch constructs.  I think what you are trying to do is possible with two different approaches. 
    The first approach would be to evaluate your expression in memory versus passing it to the data provider.  I believe this can be done by replacing this.Where(... with this.GetQuery().Execute().Where(...  This obviously has a performance cost to it because it will force the EntitySet to be loaded into memory.
    An alternative solution would be to build an Expression dynamically that the data providers would understand.  In otherwords you can write code that will dynamically build up the expression you want such as Customers.Where(c => c.Name == "Test")
    public static bool ValueFound<TEntity>(this EntitySet<TEntity> entitySet, string propertyName, object value) 
      where TEntity : IEntityObject
    {
      ParameterExpression param = Expression.Parameter(typeof(TEntity), "e");
      Expression<Func<TEntity, bool>> expression = 
        Expression.Lambda<Func<TEntity, bool>>(
          Expression.Equal(
            Expression.Property(param, propertyName),
            Expression.Constant(value)),
          param);
      return entitySet.Where(expression).FirstOrDefault() != null;
    }
    
    or
     
     
        <Extension()> _
        Public Function ValueFound(Of TEntity As IEntityObject)(ByVal entitySet As EntitySet(Of TEntity), ByVal propertyName As String, ByVal value As Object) As Boolean
          Dim param As ParameterExpression = Expression.Parameter(GetType(TEntity), "e")
          Dim expr As Expression(Of Func(Of TEntity, Boolean)) = Expression.Lambda(Of Func(Of TEntity, Boolean))( _
           Expression.Equal( _
            Expression.Property(param, propertyName), _
            Expression.Constant(value)), _
           param)
          Return (Not entitySet.Where(expr).FirstOrDefault() Is Nothing)
        End Function
    
    HTH
     
    Thursday, April 21, 2011 2:38 PM
  • Hi snomis,

    Yes, it's so obvious, once it's pointed out, lol. I've lost count of how many posts I've made, telling people, "you can't pass anything to the data provider (in this case EF) that it doesn't understand", & then I go & do it myself.

    Using "lambda expressions" like you have in your code just hasn't "gelled" with me yet. I hope I get used to it one day. :-)

    Thanks! Your code worked brilliantly!

    I now have a very handy little extension method that I can call on any EntitySet.

    Yann


    Tuesday, April 26, 2011 10:13 AM