locked
Dynamic Queries - Client DataServiceQuery RRS feed

  • Question

  •  

    Is there any way to query via ADO.Net dataservices using client library where one does not know at compile time the type of item that is to be returned. 

     

    Current Code:

     

    DataServiceQuery<ASE_PROJECT> projectQuery = ctx.CreateQuery<ASE_PROJECT>("ASE_PROJECT");

     

    I would like to be able to determine both the collection and the return type at run time rather than compile time to enable my application to be extended more readily without deploying new software to the client.  Is there another query option that would enable this ?
    Wednesday, July 2, 2008 8:33 PM

Answers

  • Hi Bob,

     you can get the enumerator for the results off of the DataServiceResponse and then use it without having to typecast the results at compile time.

     

    Code Snippet

    //Get the GetEnumerator Method off of the DSR

    MethodInfo getEnumeratorMethod = results.GetType().GetMethod("GetEnumerator");

     

    //Get the ENumerator representing the results

    IEnumerator dummy = getEnumeratorMethod.Invoke(results, null) as IEnumerator;

     

    if( null!= dummy){

     

       //work with the results

       while ( dummy.MoveNext() ) {
        System.Console.WriteLine(dummy.Current.ToString());
       }

    }

     

     

    Hope this helps.

    I think I'll make a blog post compiling all of the techniques described in this thread.

    Monday, July 14, 2008 7:09 PM
    Moderator

All replies

  • Hi ,

     Do you want to accomplish this in the desktop library or the Silverlight library ?

     and also , will you know the entity type at runtime ? what should the method look like ?

    Wednesday, July 2, 2008 9:34 PM
    Moderator
  •  

    This will be in the Silverlight library, I will know the entity type at runtime.  The only way I can think of is to use dynamic code execution, but am hoping for a more elegant solution.

     

    As for what the solution should look like I guess what would be nice is something like:

     

    DataServiceQuery<object> projectQuery = ctx.CreateQuery<object>("ProjectSet");

     

    or

     

    DataServiceQuery<typeof("Project")> projectQuery = ctx.CreateQuery<typeof("Project")>("ProjectSet");

     

    Bob

    Thursday, July 3, 2008 12:44 PM
  •  

    You can do this with reflection .

    The code would look something like this ..

     

    Code Snippet

    Type entityType = <YourEntityTypeHere>

    string entitySetName = <YourEntitySetNameHere>

     

    DataServiceQuery CreateQueryRunTime(string entitySetName , Type entityType,ref DataServiceContext ctx ) {

     

    //Get the MethodInfo for the CreateQuery Method

    MethodInfo execMethod = ctx.GetType().GetMethod("CreateQuery", BindingFlags.Instance | BindingFlags.Public);

     

    //Convert it to the Generic MethodInfo creating CreateQuery<entityType>

    MethodInfo execGenericMethod = execMethod.MakeGenericMethod(entityType);

     

    //Call the CreateQuery<entityType> method

    DataServiceQuery dscq = execGenericMethod.Invoke(ctx, new object[] { entitySetName }) as DataServiceQuery;

    return dscq;

    }

     

     

    Thursday, July 3, 2008 6:11 PM
    Moderator
  • Thank you so much,  I will be off for the holidays but will follow up in about a week with the results.  I appreciate the help.

     

    Bob

     

    Friday, July 4, 2008 1:47 PM
  • Phan,

     

    I have a similar requirement and likewise I am using Silverlight. 

     

    Maybe a daft question but as Silverlight is expecting an asynchronous call wouldn't I need to call BeginExecute and EndExecute on the DataServiceQuery? If that is the case wouldn't I need to cast the DataServiceQuery to a DataServiceQuery<TElement> in which case how do I create a type of TElement based on the entityType?

     

    Thanks

    Richie

     

    Thursday, July 10, 2008 11:30 AM
  • Hi Richie,

     You could pass the Type information along with the query in the async state in the case of Async calls to Execute ..

    Although this makes the code cryptic and unweildly , here is the complete sample

     

    ex :

     

    //This function returns a DataServiceQuery

     

    Code Snippet

    object CreateQueryRunTime(string entitySetName , Type entityType,ref DataServiceContext ctx ) {

    //Get the MethodInfo for the CreateQuery Method
    MethodInfo execMethod = ctx.GetType().GetMethod("CreateQuery", BindingFlags.Instance | BindingFlags.Public);

    //Convert it to the Generic MethodInfo creating CreateQuery
    MethodInfo execGenericMethod = execMethod.MakeGenericMethod(entityType);

    //Call the CreateQuery method , return the results
    return execGenericMethod.Invoke(ctx, new object[] { entitySetName }) as DataServiceQuery;;
    }

     

     

    //User Code to use the retrieve entities based on a dynamic Type Name

     

    Code Snippet

    object dsqEntityType = CreateQueryRunTime("ASE_PROJECT",typeof(ASE_PROJECT),ref dsContext);

    MethodInfo beDSEntityType =dsqEntityType.GetType().GetMethod("BeginExecute");

    //Invoke the BeginExecute Method
    beDSEntityType.Invoke(dsqEntityType ,  new object[]{
    //Async Callback
    (AsyncCallback)callbackFunction,
    //Object State to be persisted accross the callback
    new {Query = dsqEntityType,EntityType = typeof(ASE_PROJECT)}
     });

     

     

    //Callback function for BeginExecute

     


    Code Snippet

    private void callbackFunction(IAsyncResult asyncResult) {

     

    PropertyInfo[] properties = asyncResult.AsyncState.GetType().GetProperties();


    //Get the Query From the State
    object dsQuery  = properties[0].GetValue(asyncResult.AsyncState);


    //Get the Entity Type for the query
    Type EntityType = properties[1].GetValue(asyncResult.AsyncState) as Type;

    //Get the DataServiceQuery<T> Type
    Type dataServiceQueryType = typeof(DataServiceQuery<>).MakeGenericType(EntityType);

    //Get the EndExecute method on the DataServiceQuery<T> instance
    MethodInfo endExecuteMethod = dataServiceQueryType.GetMethod("EndExecute");
    //Execute the EndExecute method on the DataServiceQuery<T> instance
    var results = endExecuteMethod.Invoke( dsQuery , new object[] { asyncResult });

    //Work with the results here
    }

     

     

    Thursday, July 10, 2008 8:07 PM
    Moderator
  • Phani Raju,

     

    I am back from vacation and back at the problem.  I have integrated your function and it seems to work perfectly, thank you.  The last issue is how to reference the results, again with runtime type determination.  I cannot find a way to enumerate through the results set without setting a compile time type such as:

     

    foreach (object foo in ((IEnumerable<ASE_PROJECT>)results).ToList())

    {

    }

     

     

    If I do not include the IEnumerable cast references to results receives a compile error stating that the object does not have an GetEnumerator method.  I tried using reflection to directly call the get_Current and MoveNext methods but security caught me.  I know that this is probably now a bit off topic and will gladly post the question elsewhere if needed.

     

    Thanks in advance.

     

    Bob

    Monday, July 14, 2008 6:18 PM
  • Hi Bob,

     you can get the enumerator for the results off of the DataServiceResponse and then use it without having to typecast the results at compile time.

     

    Code Snippet

    //Get the GetEnumerator Method off of the DSR

    MethodInfo getEnumeratorMethod = results.GetType().GetMethod("GetEnumerator");

     

    //Get the ENumerator representing the results

    IEnumerator dummy = getEnumeratorMethod.Invoke(results, null) as IEnumerator;

     

    if( null!= dummy){

     

       //work with the results

       while ( dummy.MoveNext() ) {
        System.Console.WriteLine(dummy.Current.ToString());
       }

    }

     

     

    Hope this helps.

    I think I'll make a blog post compiling all of the techniques described in this thread.

    Monday, July 14, 2008 7:09 PM
    Moderator
  • In the silverlight libraries the results.GetType().GetMethod("GetEnumerator") returns null.   

     

    In the immediate window the GetMethods function returns

    ?results.GetType().GetMethods()

    {System.Reflection.MethodInfo[7]}

    [0]: {System.Object get_Current()}

    [1]: {Void Dispose()}

    [2]: {Boolean MoveNext()}

    [3]: {System.String ToString()}

    [4]: {Boolean Equals(System.Object)}

    [5]: {Int32 GetHashCode()}

    Devil: {System.Type GetType()}

     

    I tried to get the Current via the property and via getCurrent() but received a security error, I am using Silverlight 2 Beta 2 if that comes into play. 

     

    Bob

    Monday, July 14, 2008 7:58 PM
  • I Got it, Thanks so much.  Here is the last piece:

     

    Type EnumeratableEntityType = typeof(IEnumerable<>).MakeGenericType(EntityType);

     

    MethodInfo getEnumeratorMethod = EnumeratableEntityType.GetMethod("GetEnumerator");

     

     

    I appreciate the help.

     

    Bob

     

    Monday, July 14, 2008 8:57 PM