[en] Need help with: Client initiated linq query on the server

Topics: Technical Support
Feb 11, 2013 at 3:23 PM
Edited Feb 11, 2013 at 3:24 PM
Hello !

In essence, I am trying to continue this question:

(my initial question)

In the reply, I got an example

It's just, I am looking for an implementation of that given interface on the server. This:
IEnumerable<T> GetList<T>() where T : class;
was not problematic for me, but this is:
IQueryable<T> Query<T>() where T : class;
I have just to query a simple list, which is like this:
List<Computer> 
where Computer is something like this:
computer
{
    public string ComputerName:
    public bool Enabled;
    public string Group;
}
Can anybody give me a hint, how the method implementation
should be look ?? As said, the query have to be passed to the server
and handled by this method [Query]. The client side is naturally
very easy, just Linq.

Thanks so far and
best regards,

++mabra
Coordinator
Feb 11, 2013 at 8:21 PM
Hi mabra,

you can find a complete Linq example with client and server inside the source code repository.

Please have a look at the following link: http://zyan.codeplex.com/SourceControl/changeset/view/29829#245805
/// <summary>
/// Returns queryable information about server user's desktop folder.
/// </summary>
public IQueryable<T> GetDesktopInfo<T>() where T : class
{
    var folder = Environment.GetFolderPath(Environment.SpecialFolder.Desktop);

    // query desktop files
    if (typeof(T) == typeof(FileInfo))
    {
        var list =
            from fileName in Directory.GetFiles(folder)
            select new FileInfo(fileName);

        return list.OfType<T>().AsQueryable();
    }

    // query desktop folders
    if (typeof(T) == typeof(DirectoryInfo))
    {
        var list =
            from dirName in Directory.GetDirectories(folder)
            select new DirectoryInfo(dirName);
                
        return list.OfType<T>().AsQueryable();
    }

    throw new NotSupportedException(string.Format("Type {0} is not supported", typeof(T).Name));
}
Best regards,
Rainbird
Coordinator
Feb 11, 2013 at 8:51 PM
Edited Feb 11, 2013 at 8:52 PM
Hi mabra, welcome back!
 
As said, the query have to be passed to the server and handled by this method [Query].
 
Well, that's not exactly true.
As you can see in Rainbird's example, you don't have to deal with the query expressions.

All you need to do is to return your strongly-typed IEnumerable or IQueryable collection as is.
 
This:
IEnumerable<T> GetList<T>() where T : class;
was not problematic for me, but this is:
IQueryable<T> Query<T>() where T : class;
I have just to query a simple list, which is like this:
List<Computer>
 
You have two options. Either
  • Change the return type of your method to IEnumerable<T> or
  • Append .AsQueryable() to your collection before returning it:
// for linq-to-objects when the return type of your method is IEnumerable<T>
return myList.OfType<T>();

// for linq-to-objects when the return type is IQueryable<T>
return anotherList.OfType<T>().AsQueryable();
Feb 12, 2013 at 10:18 PM
Edited Feb 12, 2013 at 10:30 PM
Hi guys !

Much thanks for your answers!
rainbird: Sorry, something with my brain ... ;-)
I have never understand this example, because in the implementation [the class,
not the interface] I see Linq on the server!! I have expected to see there something
like "Query.Execute ...".
In the sample, is this code [on the server]:
        var list =
            from fileName in Directory.GetFiles(folder)
            select new FileInfo(fileName);
Why ?? The client has already sent the query ... !!!???? Sorry, I am missing
something substancial [probably in my brain ...].
yallie: This looks like I am thinking [which might be wrong ..].
According to the type I showed ["List<Computer>"], I have the
definition, you gave me, like this [server]:
        public IQueryable<sims.Computer> Query()
        {
            return this.computers.AsQueryable();
        }
and this [client]:
    var query =
            from c in proxy.Query()
                where c.Enabled
                select c;
This compiles fine, but, astoundingly, I am getting this exception now:
>Zyan.Examples.Linq.Client
Exception caught: Type 'System.Linq.EnumerableQuery`1[[MBG.SIMS.Test.Computer, Zyan.Examples.Linq.Interfaces, Version=1.0.0.0, Culture=neutral, Public
KeyToken=null]]' in Assembly 'System.Core, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089' is not marked as serializable.

Unhandled Exception: System.Runtime.Serialization.SerializationException: Type 'System.Linq.EnumerableQuery`1[[MBG.SIMS.Test.Computer, Zyan.Examples.L
inq.Interfaces, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]]' in Assembly 'System.Core, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b7
7a5c561934e089' is not marked as serializable.
I have modified the original example and have this as an VS2010 solution.
BTW, naturally, my class IS serializable [and used in other contexts already].

Additionally, id the query was sent to the server, in the server side method
implementation, I would think, to see something like this:
        public IQueryable<sims.Computer> Query()
        {
            return this.computers.AsQueryable(CLIENTSIDEQUERYorEXPRESSIONTREE); //???
        }
Could someone of you see my problem and probably offer some help ??
I may send the full stacktrace also.
Was I clear enough ??

Thanks so far and
best regards,

++mabra
Coordinator
Feb 13, 2013 at 4:13 PM
Edited Feb 13, 2013 at 4:45 PM
Hi!
 
I have never understand this example, because in the implementation [the class,
not the interface] I see Linq on the server!!
 
Ah, I see.

To enable LINQ queries, your method signature should meet two requirements:
  1. The method must be generic and parameterless
  2. The return type must be either IEnumerable<T> or IQueryable<T>.
Method name and implementation doesn't matter.

In your server method, the only parameter you can handle is the type parameter: T.
That's why in my example there is a switch statement by typeof(T).
 
Why ?? The client has already sent the query ... !!!????
Sorry, I am missing something substancial [probably in my brain ...].
 
As said, the only thing you have to do is to return your collection. Where do you normally get the collection of FileInfos? From Directory.GetFiles(). That's why we have here a LINQ query. There wasn't anything special in this query, just an implementation detail.
 
public IQueryable<sims.Computer> Query()
 
Linq-enabled method must be generic: IQueryable<T> Query<T>().
Your method should look like this:
public IQueryable<T> Query<T>()
{
   // LINQ-enabled method will support 
   // multiple data sources: computers, printers, etc.

    if (typeof(T) == typeof(sims.Computer))
    {
        return this.computers.OfType<T>().AsQueryable();
    }

    if (typeof(T) == typeof(sims.Printer))
    {
        return this.printers.OfType<T>().AsQueryable();
    }

    // unsupported type T
    throw new InvalidOperationException("Can't query type: " + typeof(T).FullName);
}
 
Additionally, id the query was sent to the server, in the server side method
implementation, I would think, to see something like this:
 
No, this part is handled automatically :)
The query is processed by Zyan.

By the way, you can also use another approach if you find it easier to understand:
public IEnumerable<Computer> GetComputers(Expression<Func<T, bool>> filter)
{
    return this.computers // get the full list
        .AsQueryable() // make it queryable so we can apply a filter expression
        .Where(filter) // apply the filter
        .ToArray(); // create a serializable collection: an array or a list
}
It's not a LINQ-specific method however. It's an ordinary method which takes an expression as parameter and returns a filtered list. It doesn't support multiple data sources, it can only query computers.
Feb 15, 2013 at 4:01 PM
Hi All, Yallie !

Many, many thanks !

Your method [1] does, what I want.

For my understand, your [2] example would had helped me.

Also I have really used Linq for some time, it looks like I have some massive lacks
in understanding several details. Thanks for the clarification about the requirement
to have it generic!! To keep it more simple in my beginning, I just made the opposite ....

If you do not feel too much disturbed by me, probably let me replay the thing with the
original sample [from your sources]. My question still remains [its changed with this
sentence], but I was very unprecise. I have that question about the first client request
for the IEnumerable "GetProcessInfo", requesting a list of assemblies. Do I see this right:
  • the server quyries and restricts result from real existent assemblies
  • the client just make further selections on the result.
Is this right ?? This was the place, where I did'nt exactly understand, who makes
what.

Anyway, much thanks for all the help!

Best regards,
++mabra
Coordinator
Feb 15, 2013 at 5:24 PM
Hi mabra, you're welcome!
 
Do I see this right:
  • the server quyries and restricts result from real existent assemblies
  • the client just make further selections on the result.
    Is this right ?? This was the place, where I did'nt exactly understand, who makes what.
 
Yes, that's basically right.