7

I have a simple WCF Data Services service and I want to expose a Service Operation as follows:

[System.ServiceModel.ServiceBehavior(IncludeExceptionDetailInFaults = true)]
public class ProductDataService : DataService<ProductRepository>
{
    // This method is called only once to initialize service-wide policies.
    public static void InitializeService(IDataServiceConfiguration config)
    {
      config.SetEntitySetAccessRule("*", 
            EntitySetRights.ReadMultiple | EntitySetRights.ReadSingle);
      config.SetServiceOperationAccessRule("*", 
            ServiceOperationRights.All);
      config.UseVerboseErrors = true;
    }

// This operation isn't getting generated client side
[WebGet]
public IQueryable<Product> GetProducts()
{
    // Simple example for testing
    return (new ProductRepository()).Product;
}

Why isn't the GetProducts method visible when I add the service reference on the client?

I'm running Visual Studio 2008 SP1 and .NET Framework 3.5 SP1. I also downloaded and installed this update:

MS KB: 976127 - An update is available that provides additional features and improvements for ADO.NET Data Services in the .NET Framework 3.5 SP1 on a computer that is running Windows 7 or Windows Server 2008 R2

Kev
  • 118,037
  • 53
  • 300
  • 385
  • Operations are not visible through the .svc file. They apppear in the metadata file you can see with this url : http://localhost:3059/TestService.svc/$metadata –  Dec 07 '11 at 14:01

3 Answers3

10

Finally solved this. To call a service operation on a data service class you need to use data service context object's CreateQuery or Execute methods. For example:

ProductDataService ctx = new ProductDataService(
    new Uri("http://localhost:1234/ProductDataService.svc/"));

// Method 1:
DataServiceQuery<Product> q = ctx.CreateQuery<Product>("GetProducts");
List<Product> products = q.Execute().ToList();

// Method 2:
Uri uri = new Uri(String.Format("{0}GetProducts", ctx.BaseUri), 
             UriKind.RelativeOrAbsolute);
List<Product> products = ctx.Execute<Product>(uri).ToList();

If parameters were required, say a product category on a service operation that had this signature:

[WebGet]
public IQueryable<Product> GetProducts(string category)

We would do:

// Method 1:
DataServiceQuery<Product> q = ctx.CreateQuery<Product>("GetProducts")
                                .AddQueryOption("category", "Boats") ;
List<Product> products = q.Execute().ToList();

// Method 2:
Uri uri = new Uri(String.Format("{0}GetProducts?category={1}", 
                    ctx.BaseUri, "Boats"), UriKind.RelativeOrAbsolute);
List<Product> products = ctx.Execute<Product>(uri).ToList();
Kev
  • 118,037
  • 53
  • 300
  • 385
  • Thanks for posting the answer after you have found it! – Adrian Grigore Feb 22 '11 at 16:04
  • Yet another way that WCF data services is horribly broken... Thank you for this solution. – Yuck Jun 16 '11 at 19:28
  • 1
    Here's a blog post by someone at Microsoft detailing the above. http://blogs.msdn.com/b/writingdata_services/archive/2011/03/28/calling-service-operations-from-the-client.aspx. Basically the same info but maybe worthwhile to someone. – Rex Miller Jul 21 '11 at 17:16
1

(this answer is incorrect (see comments), but is deliberately left here to stop other answers stumbling blindly into the same hole)


IIRC, it also needs to be an [OperationContract]

[OperationContract, WebGet]
public IQueryable<Product> GetProducts()
{
    // Simple example for testing
    return (new ProductRepository()).Product;
}

(and ideally the service itself would be a [ServiceContract])

Marc Gravell
  • 1,026,079
  • 266
  • 2,566
  • 2,900
  • 1
    Morning Marc. I tried decorating with the `OperationContract` attribute that but when adding the service reference on the client I get an error: "OperationContractAttributes are only valid on methods that are declared in a type that has ServiceContractAttribute.". – Kev Mar 18 '10 at 05:52
  • Then...If I add the `[ServiceContract]` attribute to the class as suggested I then get a different error: The service class of type .. both defines a ServiceContract and inherits a ServiceContract from type System.Data.Services.IRequestHandler. Contract inheritance can only be used among interface types. If a class is marked with ServiceContractAttribute, it must be the only type in the hierarchy with ServiceContractAttribute." I'd ideally rather not end up with two services. – Kev Mar 18 '10 at 05:53
  • @Kev - OK; ignore this then. I'll leave this here just in case anyone else falls into the same mistake. I'll fire up astoria... – Marc Gravell Mar 18 '10 at 06:13
  • Hmm...looks like I need to use the `CreateQuery` or `Execute` methods on the client side `DataServiceContext` object. And of course it looks like `CreateQuery` has a bug where it introduces an unwanted parenthesis on the end of the service operation method name which I can repro (http://preview.tinyurl.com/wcfdsbug). – Kev Mar 18 '10 at 06:15
  • I think there's something fundamentally broken in the WCF Data Services client side mechanisms for calling service operations. `CreateQuery` has the bug linked to above. Execute seems broken as well and no matter what I try to return (IEnumerable, IQueryable, primitives such as strings) I always end up with the error "Only a single enumeration is supported by this IEnumerable" or the enumeration yielding no results. Think I may have to add a second proper WCF service just for these misc operations I need. Testing with raw URL's in the browse work just fine. – Kev Mar 18 '10 at 07:05
0

I had a similar issue with following sample

 [System.ServiceModel.ServiceBehavior(IncludeExceptionDetailInFaults = true)]
 public class TestService : DataService<MyService>
{
    // This method is called only once to initialize service-wide policies.
    public static void InitializeService(DataServiceConfiguration config)
    {

        config.SetEntitySetAccessRule("*", EntitySetRights.AllRead);
        config.SetServiceOperationAccessRule("*", ServiceOperationRights.AllRead);
        config.DataServiceBehavior.MaxProtocolVersion = DataServiceProtocolVersion.V2;

    }

    [WebGet]
    public IQueryable<string> GetStrings(int index)
    {
        string[] list = { "One", "two" };
        return list.AsQueryable(); 

    }
}

When I browsed the service http://localhost:3059/TestService.svc, the list didn't list the method with the WebGet attribute, but I am able to access it using http://localhost:3059/TestService.svc/GetStrings?index=1

This suggests to me that the WCF data service definition doesn’t list the operations when it's browsed through a web browser or there is some undocumented way to get both in the list.

GraphicsMuncher
  • 4,583
  • 4
  • 35
  • 50
Mahesh
  • 1