locked
ServiceOperations not loaded in wcfclient RRS feed

  • Question

  • I have an WCF Dataservices endpoint with a serviceoperation defined. MY code is this:

    public static void InitializeService(DataServiceConfiguration config)
    {
        config.SetEntitySetAccessRule("Outages", EntitySetRights.All);
        config.UseVerboseErrors = true;
        config.DataServiceBehavior.MaxProtocolVersion = DataServiceProtocolVersion.V3;
        config.SetServiceOperationAccessRule("GetOutagesByTimespan", ServiceOperationRights.All);
    }
    
    [WebGet]
    public IQueryable<Outage> GetOutagesByTimespan(int minutes)
    {
        var context = new OutageTrackingContext();
        return context.Outages
            .Where(i => i.EndDateTime != null);
    }

    I have a test project with a service reference defined via a direct link to the webservice project in Visual Studio itself.

    The metadata correctly exposes the service operation and I can call it using HTTP with eg:

    http://localhost:59884/OutageTrackingService.svc/GetOutagesByTimespan?minutes=12

    However - when referencing the endpoint in Visual Studio 2012, the service operations are not detected.

    Shouldn't it be exposed thru the service reference?


    /Jesper www.idippedut.dk

    Monday, September 9, 2013 8:55 AM

Answers

  • Hi,

    Sorry for that I misunderstood your question.

    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:

    [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;
    }

    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();

    And there is a blog regarding this issue:

    http://blogs.msdn.com/b/writingdata_services/archive/2011/03/28/calling-service-operations-from-the-client.aspx

    Regards.


    <THE CONTENT IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND, WHETHER EXPRESS OR IMPLIED>
    Thanks
    MSDN Community Support

    Please remember to "Mark as Answer" the responses that resolved your issue. It is a common way to recognize those who have helped you, and makes it easier for other visitors to find the resolution later.



    Tuesday, September 10, 2013 10:58 AM
    Moderator

All replies

  • Hello,

    Thanks for posting question to MSDN.

    According to your description, you want to know why the service operations are not detected.

    In my opinion, the class [WebGet] just marks the GetOutagesByTimespan which can be call by url.

    If we want to know call GetOutagesByTimespan by instance, we need to give the GetOutagesByTimespan a OperationContract label like below:

        [OperationContract]
        [WebGet]
        public IQueryable<Outage> GetOutagesByTimespan(int minutes)
        {
        var context = new OutageTrackingContext();
        return context.Outages
            .Where(i => i.EndDateTime != null);
        }

    More information about OperationContract:

    http://msdn.microsoft.com/en-us/library/system.servicemodel.operationcontractattribute.aspx

    Regards.


    <THE CONTENT IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND, WHETHER EXPRESS OR IMPLIED>
    Thanks
    MSDN Community Support

    Please remember to "Mark as Answer" the responses that resolved your issue. It is a common way to recognize those who have helped you, and makes it easier for other visitors to find the resolution later.

    Tuesday, September 10, 2013 6:29 AM
    Moderator
  • Hi,

    thanks for the reply.

    but - the OperationContract attribute ... isn't that a traditional WCF attribute (and not WCF Data Services)? When I apply the OperationContractAttribute to the method in question I get an error prompting me to:

    "The OperationContractAttribute declared on method 'GetOutagesByTimespan' in type 'OutageTracking.OutageTrackingService' is invalid. OperationContractAttributes are only valid on methods that are declared in a type that has ServiceContractAttribute. Either add ServiceContractAttribute to type OutageTracking.OutageTrackingService' or remove OperationContractAttribute from method 'GetOutagesByTimespan'. "

    But when I add the ServiceContactAttribute to the containing type, it gives me more or less a "multiple inheritance-error" saying that:

    "System.InvalidOperationException: The service class of type OutageTracking.OutageTrackingService 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.  Consider moving the ServiceContractAttribute on type System.Data.Services.IRequestHandler to a separate interface that type System.Data.Services.IRequestHandler implements."

    Does that make sense to you?

    thanks,

    /Jesper

    Copenhagen, Denmark


    /Jesper www.idippedut.dk


    Tuesday, September 10, 2013 9:13 AM
  • Hi,

    Sorry for that I misunderstood your question.

    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:

    [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;
    }

    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();

    And there is a blog regarding this issue:

    http://blogs.msdn.com/b/writingdata_services/archive/2011/03/28/calling-service-operations-from-the-client.aspx

    Regards.


    <THE CONTENT IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND, WHETHER EXPRESS OR IMPLIED>
    Thanks
    MSDN Community Support

    Please remember to "Mark as Answer" the responses that resolved your issue. It is a common way to recognize those who have helped you, and makes it easier for other visitors to find the resolution later.



    Tuesday, September 10, 2013 10:58 AM
    Moderator
  • What do you mean by "they are not detected"? Services operations are normally not shown in the service document - they are only shown in the $metadata endpoint, since there is no way to express parameters in the service documents.

    Hope this helps.

    Thanks

    Pratik


    This posting is provided "AS IS" with no warranties, and confers no rights.

    Tuesday, September 10, 2013 9:21 PM
    Moderator