views:

623

answers:

3

I have a WCF Service. It uses Linq-to-objects to select from a Dictionary. The object type is simple:

public class User 
{
   public Guid Id;
   public String Name;
}

There is a collection of stored in a Dictionary<Guid,User>.

I want to have a WCF OperationContract method like this:

public IEnumerable<Guid> GetAllUsers()
{
    var selection = from user in list.Values
        select user.Id;
     return selection;
}

It compiles fine, but when I run it I get:

The server encountered an error processing the request. The exception message is 'Cannot serialize parameter of type 'System.Linq.Enumerable+WhereSelectEnumerableIterator2[Cheeso.Samples.Webservices._2010.Jan.User,System.Guid]' (for operation 'GetAllUsers', contract 'IJsonService') because it is not the exact type 'System.Collections.Generic.IEnumerable1[System.Guid]' in the method signature and is not in the known types collection. In order to serialize the parameter, add the type to the known types collection for the operation using ServiceKnownTypeAttribute.'. See server logs for more details.

How can I coerce the selection to be an IEnumerable<Guid> ?


EDIT
If I modify the code to do this, it works well - good interoperability.

public List<Guid> GetAllUsers()
{
    var selection = from user in list.Values
        select user.Id;
     return new List<Guid>(selection);
}

Is there a way for me to avoid the creation/instantiation of the List<T> ?

A: 

You need to pass interoperable collections to and from your WCF methods.

WCF plays best with simple types and arrays.

Pass in an array from your client and then turn it into an IEnumerable in the service.

Stuff like IEnumerable is not interoperable, which is what WCF is trying to be.

There may be a way to get around it with known types, but I always strive to make my components as flexible as possible.

Terry Donaghe
I guess I'm not so much trying to pass out an IEnumerable<Guid>. What I really want to deliver out is a list of Guids. but I didn't want to have to do unnecessary casting in order to make that happen. In fact I can just change the return type of the method to a List<Guid>, and then create a new List<Guid> passing the selection into the List constructor - that works just fine and the client gets the list the way I want. Simple enough. I was hoping the explicit creation of the List<Guid> would be unnecessary.
Cheeso
IEnumerable<> is just an interface and it is not entirely correct to say that it is not interoperable as that is dependent on the initialized type that implements IEnumerable<> that is returned by the method. I have utilized IEnumerable<> as a return from service operations without any issue, but the key is that the return value of the operation must be initialized properly.
MattK
MattK - so how can I use linq-to-objects to perform a query that results in an IEnumerable<Clazz> that I can return from a WCF-exposed method?
Cheeso
+1  A: 

No, one must return a concrete class from a web service. Make the return type List and be done with it.

Cheeso
A: 

You have to use the ServiceKnownTypes attribute.

using System;
using System.Collections.Generic;
using System.ServiceModel;
using System.ServiceModel.Web;

namespace Test.Service
{
    [ServiceContract(Name = "Service", Namespace = "")]
    public interface IService
    {
        [OperationContract]
        [WebInvoke(
            Method = "GET",
            BodyStyle = WebMessageBodyStyle.WrappedRequest,
            RequestFormat = WebMessageFormat.Json,
            ResponseFormat = WebMessageFormat.Json)]
        [ServiceKnownType(typeof(List<EventData>))]
        IEnumerable<EventData> Method(Guid userId);
    }
}

Basically you need to know the concrete type of what you're returning. Pretty simple.

Antwan W. A-Dubb