none
accessing properties of an anonymous type created by a dynamic linq query RRS feed

  • Question

  • Hi

    I am having trouble accessing data retrieved by a Linq to SQL query as an anonymous type. I found the following information in an earlier thread:

    Creating an local entity class to hold the data.

    I prefer this method because if you need to access the data many times during the application process, it can save many performance costs comparing with the other two methods I am going to introduce. 

     

    The drawback is that such an entity class does not have the override methods like Equals, ToString, and GetHashCode.   However, generally the anonymous type classes is implemented by some compiler-generated internal sealed classes with names.   Their Equls, ToString, and GetHashCode methods have been overridden, which are based on the anonymous type’s property values.   E.g.  Equals method will return true only if all the properties of two anonymous type objects are equal, and ToString method returns string value like “{ id = …, name = …}”.   If you need to perform some comparison between the anonymous types, this method can meet the demand unless we also override the certain object methods for the local entity class.

    =========================================

            public class EntityClass

            {

                public int id { get; set; }

                public string name { get; set; }

    }

     

            var results = from t in db.TableAs

                  select new EntityClass { id = t.id, name = t.name };

    =========================================

    The above post was by Lingzhi Sun, MSDN Subscriber Support in Forum, on Monday, August 03, 2009 7:26 AM, to give credit where it is due. 

    This would work very nicely for me but I am having trouble writing the syntax for the query. This is made more complex, for me at least, because I am wrting the query using dynamic linq query.

    Here is my code, in VB.Net 4.0:

    these two method calls, set the specific fields used in the 'Where' clause and are set dynamicially at runtime, hence the need for dynamic linq query

    Dim Phasename As String= GetPhasename(CurrentPhase)

     

    Dim Selectionname As String= GetSelectionTypeName(WhatClicked)

     

     

    these two lines put together the string used in 'Where'

    Dim ATTorDefRole As String = If(attordef = EnumCon.WhatClicked.Attacker, "7999", "7998"

    )

     

    Dim ColToCheck As String = Trim(Phasename) & " = 7997 or " & Trim(Phasename) & " = " & ATTorDefRole & " and " & Selectionname &

    " = true"

     

    this is the dynamic linq query itself; apparently I need to use New since I am retrieving two fields only and not the whole datarow from the db table

    Dim

     

     

    Getquery = db.LookUpContextMenus.Where(ColToCheck).Select("new(Activity, ActivityName)"

    )

     

    'the above code works and produces the anonymous type GetQuery. I am now trying to add the suggested class as per the post above in order be able to access the query results. They key line suggested is:

    select new EntityClass { id = t.id, name = t.name };

    In my code, I have translated this to:

     

     

    Dim Getquery = db.LookUpContextMenus.Where(ColToCheck).Select("new ObjectHolder(Activity=Activity, ActivityName=ActivityName)")

    This revised query produces an error in System.Linq.Dynamic, with the message that a "(" is expected - after 'new' as far as I can determine.

    Any ideas as to how to write the query syntax properly, using dynamic linq query in VB?

    For completeness sake, I have defined Objectholder as a class as follows:

    Friend

     

     

    Class

    Objectholder

     

     

    Sub New

    ()

     

     

    End

    Sub

     

     

    Friend Property Activity As

    Integer

     

     

    Friend Property ActivityName As

    String

     

     

    End Class

     

      

     

     


    VBDataStarter
    Thursday, September 15, 2011 10:43 PM

Answers

  • Hi,

    Please take a look at this other thread:

    http://stackoverflow.com/questions/1465700/system-linq-dynamic-select-new-into-a-listt-or-any-other-enumerable

    It explains how to return a new class dynamically.

    Best regards,

    JA Reyes.


    Please remember to Vote & "Mark As Answer" if this post is helpful to you.
    Por favor, recuerda Votar y "Marcar como respuesta" si la solución de esta pregunta te ha sido útil.
    Friday, September 16, 2011 10:45 AM
  • Well,

    The variable newresulttype should be the Generic type you are defining in the Select statement, so it looks ok.

    Try to understand what ParseNew method try to do and debug if the type variable is set to the newresulttype value.

    As I understand, the For statement try to bind the value of the ObjectHolder object with the result of the Linq query.

    Let me know any news about this.

    Regards,

    JA Reyes.


    Please remember to Vote & "Mark As Answer" if this post is helpful to you.
    Por favor, recuerda Votar y "Marcar como respuesta" si la solución de esta pregunta te ha sido útil.
    Friday, September 16, 2011 7:47 PM

All replies

  • Hi,

    Please take a look at this other thread:

    http://stackoverflow.com/questions/1465700/system-linq-dynamic-select-new-into-a-listt-or-any-other-enumerable

    It explains how to return a new class dynamically.

    Best regards,

    JA Reyes.


    Please remember to Vote & "Mark As Answer" if this post is helpful to you.
    Por favor, recuerda Votar y "Marcar como respuesta" si la solución de esta pregunta te ha sido útil.
    Friday, September 16, 2011 10:45 AM
  • Hi,

    Please take a look at this other thread:

    http://stackoverflow.com/questions/1465700/system-linq-dynamic-select-new-into-a-listt-or-any-other-enumerable

    It explains how to return a new class dynamically.

    Best regards,

    JA Reyes.


    Please remember to Vote & "Mark As Answer" if this post is helpful to you.
    Por favor, recuerda Votar y "Marcar como respuesta" si la solución de esta pregunta te ha sido útil.


    Thanks for this. I have to say that I am a bit nervous about using this approach. Essentially, it would require me to make a number of changes to the Dynamic library in System.Linq.Dynamic. As I am not a coding expert, I don't really understand how this code works in the first place (the nice thing about System.Linq.dynamic as it keeps all of this parsing and lambda stuff under-the-hood for me). Plus, I will need to call on the functionality in this library in multiple places in my solution. If I start to change it as the link you provided suggests, how can I be sure that it will work-as-advertised in other situations?

    The C# code example I cited in my first reply made no mention of needing to change the dynamic library. It was just a question of adding the classname into the Select New . . . .statement. I am having trouble with the syntax of doing that in Dynamic Linq Query.

    I did find another approach that might work. It suggested retaining the anonymous type but then accessing specific properties as follows (names changed to fit with my code in my first post):

    foreach (dynamic item in GetQuery) {  
       
    Console.WriteLine(GetQuery.ActivityName); 
    }

    I translate this to VB as

    For each item as system.linq.dynamic in GetQuery

     Console.WriteLine(GetQuery.ActivityName)

    next

    and I get a "Type Expected" Error statement for system.linq.dynamic. Just using dynamic get me a "ambiguous reference" error as System.Dynamic exists as well. 

    So, I am still on the hunt for something simple here. I see many references to achieving the result by reflection but for various reasons would prefer not to use that approach. I can see that it would work however.

    Thanks. 


    VBDataStarter
    Friday, September 16, 2011 2:01 PM
  • Hi again,

    Well, unfortunatelly Dynamic LINQ library doesn't support return strongly-typed objects, so if you want to have this functionality, there is no other way than modify it.

    Steps explained in previous link are simple enough:

    1.- It uses a private variable to keep the type of the argument.

    2.- It checks if this private variable has been initialized, otherwise the code performs the same operation as before.

    3.- You need to build an Extenssion method. It is like an overload a method, so if you don't call it (and you didn't because it is a new method), all you previous code should still works.

    Well, I know that's a valid solution for your problem but maybe there exist others that I don't know at present time.

    Best regards,

    JA Reyes.


    Please remember to Vote & "Mark As Answer" if this post is helpful to you.
    Por favor, recuerda Votar y "Marcar como respuesta" si la solución de esta pregunta te ha sido útil.
    Friday, September 16, 2011 2:17 PM
  • Thanks again. I understand your explanation as to why these changes will not affect other uses of the dynamic library. Great.

    I have given this a shot and much to my surprise I was able to make all the code changes (remember I am doing this in Vb so I had to translate your example into VB code). 

    I am getting a runtime error however. It occurs at the line

    bindings[i] = Expression.Bind(type.GetProperty(properties[i].Name), expressions[i]);  (C# version from your example)

    bindings(i) =

    Expression

    .Bind(type.GetProperty(properties(i).Name), expressions(i))  (VB version from my code)

     

     

    The error message is "Value cannot be null. Parameter name: member"  

    This seems to be linked to expressions(i), which correctly contains two items as I am returning two fields from the database table. properties(i) holds the name of those two fields correctly. Any ideas as to what the value for member is supposed to be and where it should be found? Is it something from the database?

    The where clause of this query works and when I run it as an anonymous type it brings back records (or rather two fields from records).  The returned fields contain data not nulls.

    If I can fix this bug, I will be home free. Thanks for your help on this.

    in ExpressionParser ParseNew
    VBDataStarter
    Friday, September 16, 2011 4:02 PM
  • Hi,
    Well, in theory your code should looks something like this:
            Function ParseNew() As Expression
                NextToken()
                ValidateToken(TokenId.OpenParen, Res.OpenParenExpected)
                NextToken()
                Dim properties As New List(Of DynamicProperty)()
                Dim expressions As New List(Of Expression)()
                Do
                    Dim exprPos = tokenVal.pos
                    Dim expr = ParseExpression()
                    Dim propName As String
                    If TokenIdentifierIs("as") Then
                        NextToken()
                        propName = GetIdentifier()
                        NextToken()
                    Else
                        Dim [me] As MemberExpression = TryCast(expr, MemberExpression)
                        If [me] Is Nothing Then Throw ParseError(exprPos, Res.MissingAsClause)
                        propName = [me].Member.Name
                    End If
                    expressions.Add(expr)
                    properties.Add(New DynamicProperty(propName, expr.Type))
                    If tokenVal.id <> TokenId.Comma Then Exit Do
                    NextToken()
                Loop
                ValidateToken(TokenId.CloseParen, Res.CloseParenOrCommaExpected)
                NextToken()
                Dim type As Type = If(newResultType, DynamicExpression.CreateClass(properties))
                Dim bindings(properties.Count - 1) As MemberBinding
                For i As Integer = 0 To bindings.Length - 1
                    bindings(i) = Expression.Bind(type.GetProperty(properties(i).Name), expressions(i))
                Next
                Return Expression.MemberInit(Expression.[New](type), bindings)
            End Function
    

    But how are you calling the Select method? It should looks more or less like this:
    .Select<ObjectHolder>("new (Activity as Activity, ActivityName as ActivityName)")
    
    Regards,
    JA Reyes.

    Please remember to Vote & "Mark As Answer" if this post is helpful to you.
    Por favor, recuerda Votar y "Marcar como respuesta" si la solución de esta pregunta te ha sido útil.
    Friday, September 16, 2011 5:01 PM
  • Hi, thanks for sticking with me on this one.

    My ParseNew looks just like yours. I had used a different If . . .statement to test for newresulttype so I switched to your line with the same result (error message).

    My Select statement is the same except that I need to use Select(of ObjectHolder) rather than Select<ObjectHolder>.

    I am still getting the same error statement. I am breaking the code and checking the value of newresulttype during Execution. I can see that it's name is ObjectHolder so it appears that it is being assigned properly.


    VBDataStarter
    Friday, September 16, 2011 7:21 PM
  • Well,

    The variable newresulttype should be the Generic type you are defining in the Select statement, so it looks ok.

    Try to understand what ParseNew method try to do and debug if the type variable is set to the newresulttype value.

    As I understand, the For statement try to bind the value of the ObjectHolder object with the result of the Linq query.

    Let me know any news about this.

    Regards,

    JA Reyes.


    Please remember to Vote & "Mark As Answer" if this post is helpful to you.
    Por favor, recuerda Votar y "Marcar como respuesta" si la solución de esta pregunta te ha sido útil.
    Friday, September 16, 2011 7:47 PM