Why is my ServiceOperation method missing from my WCF Data Services client proxy code?
Asked Answered
J

3

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

Joanniejoao answered 18/3, 2010 at 5:0 Comment(1)
Operations are not visible through the .svc file. They apppear in the metadata file you can see with this url : localhost:3059/TestService.svc/$metadataFoliose
J
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();
Joanniejoao answered 18/3, 2010 at 8:20 Comment(3)
Thanks for posting the answer after you have found it!Mandler
Yet another way that WCF data services is horribly broken... Thank you for this solution.Incompressible
Here's a blog post by someone at Microsoft detailing the above. blogs.msdn.com/b/writingdata_services/archive/2011/03/28/…. Basically the same info but maybe worthwhile to someone.Manlike
T
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])

Tephrite answered 18/3, 2010 at 5:32 Comment(5)
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.".Joanniejoao
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.Joanniejoao
@Joanniejoao - OK; ignore this then. I'll leave this here just in case anyone else falls into the same mistake. I'll fire up astoria...Tephrite
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 (preview.tinyurl.com/wcfdsbug).Joanniejoao
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.Joanniejoao
C
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.

Contraceptive answered 9/2, 2011 at 15:9 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.