Search code examples
wcfentity-framework-5wcf-data-servicesodata

Consuming WCF Data Services Action Provider for Entity Framework


I have a DataService class on top of a Entity Framework 5 DbContext:

public class MyDataService : DataService<MyDbContext>, IServiceProvider
{

    [WebGet]
    public IQueryable<Product> GetProducts1(int category)
    {
        return from p in this.CurrentDataSource.Products
               where p.Category == category
               select p;
    }
}

Now I want to expose methods from my DbContext to the DataService and used this sample code: http://efactionprovider.codeplex.com/

public class MyDbContext : DbContext
{
    public DbSet<Product> Products { get; set; }

    [NonBindableAction]
    public IQueryable<Product> GetProducts2(int category)
    {
        return from p in this.CurrentDataSource.Products
               where p.Category == category
               select p;
    }

}

Accessing http://localhost:12345/MyDataService.svc/$metadata show that both methods are known, but the first one hat am:HttpMethod="GET"` attribut

<EntityContainer Name="MyDbContext" m:IsDefaultEntityContainer="true">
    ...
    <FunctionImport Name="GetProducts1" ReturnType="Collection(MyNameSpace.Product)" EntitySet="Products" m:HttpMethod="GET">
        <Parameter Name="category" Type="Edm.Int32" Nullable="false"/>
    </FunctionImport>
    <FunctionImport Name="GetProducts1" ReturnType="Collection(MyNameSpace.Product)" EntitySet="Products">
        <Parameter Name="category" Type="Edm.Int32" Nullable="false"/>
    </FunctionImport>
    ...

I can execute GetProducts1 by accessing the url

http://localhost:12345/MyDataService.svc/GetProducts1?category=1

That doesn't work for GetProducts2 (propably because of GET not allowed) But I managed to execute GetProducts2 by using fiddler:

POST: http://localhost:12345/MyDataService.svc/GetProducts1
Request Headers:
    User-Agent: Fiddler
    Host: localhost:12345
    Content-Length: 12
    Content-Type: application/json

Request Body:
    {category:1}

Ok, now here is my problem: I consume this service with a Windows application using a Service Reference. Since the code generation in the DataServiceContext derived class does not cover the actions I need to call them by myself.

For the first one (GetProducts1) I can do:

    public IEnumerable<Product> GetProducts1(int category)
    {
        var proxy = new MyDataServiceContext(
            "http://localhost:12345/MyDataService.svc");
        var queryString = String.Format("{0}/GetProducts1?category={1}",
            proxy.BaseUri, category);
        var uri = new Uri(queryString, UriKind.RelativeOrAbsolute);
        return proxy.Execute<Product>(uri);
    }

But I am struggling with the second one. I tried:

    public IEnumerable<Product> GetProducts2(int category)
    {
        var proxy = new MyDataServiceContext(
            "http://localhost:12345/MyDataService.svc");
        var queryString = String.Format("{0}/GetProducts2",
            proxy.BaseUri);
        var uri = new Uri(queryString, UriKind.RelativeOrAbsolute);
        return proxy.Execute<Product>(uri, "POST", false, 
            new UriOperationParameter("category", category));
    }

but I get a DataServiceClientException: Content-Type-Header value missing. (Status-Code 400)

Is there any way to call this method my using the Execute method? I would prefer to continue using the DataServiceContext and not make any raw Requests myself.

Thanks in advance

btw.

I am using visual studion 2010 and the Microsoft.Data.Services and Microsoft.Data.Services.Client packages from nuget rather than the default System.Data.Services dlls, because I believe I need to set config.DataServiceBehavior.MaxProtocolVersion = DataServiceProtocolVersion.V3; for executing actions in the first place.


Solution

  • Try using BodyOperationParameter instead of UriOperationParameter. Action parameters are included in request body instead of Uri.