views:

48

answers:

1

Hi!

Me and some buddy of mine are trying to get started with WCF Data Services, so let me first describe what we have done so far:

  1. We have created a fairly simple WCF Data Service with a data source that implements the IUpdatable interface and exposes some data through some public IQueryable<> attributes (the code is attached at the bottom). Using Visual Studio 2010, at first we ran our service in IIS 7, but due to errors we couldn't figure out, we decided to run it with Cassini (Webdev webserver) instead.

  2. We wrote a client in C# to consume the service. The client works as supposed with all the different data operations (create, read, update and delete). So far so good! When hosting the service at the IIS 7 webserver we had to use POST tunneling to make updates and deletes work, but now it's working as intended.

  3. Our problem arises when we try to consume the service with our Java (Restlet) and Ruby (ruby_odata) clients: we are unable to update data with these clients (we get a “500 Internal Server Error” and “Method Not Allowed” reply from the server) . We used two fairly simple tutorials[a,b] which both seems pretty straight forward, to create our clients. We therefore believe our problems lies within our service.

a. ruby_odata: http: //rdoc.info/projects/visoft/ruby_odata b. restlet: http: //wiki.restlet.org/docs_2.0/13-restlet/28-restlet/287-restlet/288-restlet.html

Both those clients are listed as OData SDKs ( http://www.odata.org/developers/odata-sdk ) and should be working fine to consume the OData feed.

One thing we noticed when monitoring the HTTP requests, is that the C# client uses HTTP MERGE verb for updates (take a look here for more information: http: //blogs.msdn.com/b/astoriateam/archive/2008/05/20/merge-vs-replace-semantics-for-update-operations.aspx), while both Java and Ruby uses HTTP PUT for updates. Could this be the reason why only our C# client works? What can we do to enable PUT updates?

We've just started with .NET, and would appreciate if that could be taken into consideration when you answer :-)

Thanks for any help!

using System; using System.Collections.Generic; using System.Data.Services; using System.Linq; using System.Data.Services.Providers; using System.Reflection; using System.ServiceModel.Web; using System.Data.Services.Common;

namespace WCFDataServiceApp {

public class Product
{
    public int ID { get; set; }
    public string Name { get; set; }
    public string Color { get; set; }
    public Category ProductCategory { get; set; }
}

public class Category
{
    public int ID { get; set; }
    public string Name { get; set; }


}

public class AWData : IUpdatable
{

    static List<Category> categories;
    static List<Product> products;

    static AWData()
    {

        categories = new List<Category>() {
            new Category { ID = 1, Name = "Bikes" },
            new Category { ID = 2, Name = "Parts" },
            new Category { ID = 3, Name = "Wheels"},
        };

        products = new List<Product>() {
            new Product { ID = 1, Name = "Red Bike", Color = "Red", ProductCategory = categories[0] },
            new Product { ID = 2, Name = "Blue Bike", Color = "Blue", ProductCategory = categories[0] },
            new Product { ID = 3, Name = "Green Bike", Color = "Green", ProductCategory = categories[0] },
            new Product { ID = 4, Name = "Yellow Bike", Color = "Yellow", ProductCategory = categories[0] },
            new Product { ID = 5, Name = "Pink Bike", Color = "Pink", ProductCategory = categories[0] },
            new Product { ID = 6, Name = "Black Bike", Color = "Black", ProductCategory = categories[0] }
        };           

    }

    public IQueryable<Category> Categories
    {
        get { return categories.AsQueryable(); }
    }

    public IQueryable<Product> Products
    {
        get { return products.AsQueryable(); }
    }




    void IUpdatable.AddReferenceToCollection(object targetResource, string propertyName, object resourceToBeAdded)
    {
        System.Diagnostics.Debug.WriteLine("No support for AddReferenceToCollection(object targetResource, string propertyName, object resourceToBeAdded)");
    }

    void IUpdatable.ClearChanges()
    {
        System.Diagnostics.Debug.WriteLine("ClearChanges()");
    }

    public object CreateResource(string containerName, string fullTypeName)
    {
        System.Diagnostics.Debug.WriteLine("CreateResource(string containerName, string fullTypeName)");
        Type t = Type.GetType(fullTypeName);

        // Check if resource exists
        if (t != null)
        {
            object resource = Activator.CreateInstance(t);
            if (containerName.Equals("Categories"))
            {
                categories.Add((Category)resource);
            }
            else if (containerName.Equals("Products"))
            {
                products.Add((Product)resource);
            }
            return resource;
        }
        // Current resource does not exist
        return new Exception("Could not create a resource of type " + containerName);

    }

    void IUpdatable.DeleteResource(object targetResource)
    {
        // 1. Check object type

        if (targetResource.GetType().IsInstanceOfType(new Category()))
        {
            System.Diagnostics.Debug.WriteLine("Category deleted!");
            categories.Remove((Category)targetResource);
        }
        else if (targetResource.GetType().IsInstanceOfType(new Product()))
        {
            System.Diagnostics.Debug.WriteLine("Product deleted!");
            products.Remove((Product)targetResource);
        }

    }

    object IUpdatable.GetResource(IQueryable query, string fullTypeName)
    {
        System.Diagnostics.Debug.WriteLine("GetResource(IQueryable query, string fullTypeName)");
        object obj = null;
        foreach (object o in query)
        {
            obj = o;
        }
        return obj;

    }



    object IUpdatable.GetValue(object targetResource, string propertyName)
    {
        System.Diagnostics.Debug.WriteLine("GetValue(object targetResource, string propertyName)");
        return null;
    }

    void IUpdatable.RemoveReferenceFromCollection(object targetResource, string propertyName, object resourceToBeRemoved)
    {
        System.Diagnostics.Debug.WriteLine("RemoveReferenceFromCollection(object targetResource, string propertyName, object resourceToBeRemoved)");
    }

    object IUpdatable.ResetResource(object resource)
    {

        System.Diagnostics.Debug.WriteLine("ResetResource(object resource)");
        return null;
    }

    object IUpdatable.ResolveResource(object resource)
    {
        return resource;
    }

    void IUpdatable.SaveChanges()
    {
        System.Diagnostics.Debug.WriteLine("SaveChanges()");
    }

    void IUpdatable.SetReference(object targetResource, string propertyName, object propertyValue)
    {
        System.Diagnostics.Debug.WriteLine("SetReference(object targetResource, string propertyName, object propertyValue)");
    }

    void IUpdatable.SetValue(object targetResource, string propertyName, object propertyValue)
    {
        PropertyInfo pi = targetResource.GetType().GetProperty(propertyName);

        if (pi == null)
            throw new Exception("Can't find property");
        pi.SetValue(targetResource, propertyValue, null);

        System.Diagnostics.Debug.WriteLine("Object " + targetResource + " updated value " + propertyName + " to " + propertyValue);
    }

    public void SetConcurrencyValues(object resourceCookie, bool? checkForEquality, IEnumerable<KeyValuePair<string, object>> concurrencyValues)
    {
        System.Diagnostics.Debug.WriteLine("SetConcurrencyValues(object resourceCookie, bool? checkForEquality, IEnumerable<KeyValuePair<string, object>> concurrencyValues) was called");
        throw new Exception("SetConcurrencyValues(object resourceCookie, bool? checkForEquality, IEnumerable<KeyValuePair<string, object>> concurrencyValues) not implemented");
    }
}

public class aw : DataService<AWData> //, IServiceProvider
{
    // This method is called only once to initialize service-wide policies.
    public static void InitializeService(DataServiceConfiguration config)
    {
        config.SetEntitySetAccessRule("*", EntitySetRights.All);
        //config.DataServiceBehavior.MaxProtocolVersion = DataServiceProtocolVersion.V2;

    }
    /*
    public object GetService(Type serviceType)
    {

        System.Diagnostics.Debug.WriteLine(serviceType.ToString());
        return this;
    }*/
}

}

A: 

The same question answered here: http://social.msdn.microsoft.com/Forums/en-US/adodotnetdataservices/thread/31d3f2f0-3dd2-479f-8b44-45f59eef0c53/

The problem is that PUT will end up calling ResetResource which is expected to return a valid resource instance and not null.

Vitek Karas MSFT