Skip to content

Services

Nathan Baulch edited this page Aug 20, 2014 · 2 revisions

The high level service operation API uses LINQ expressions to provide a fluent interface for building service operation requests. It makes calling remote services almost as easy as calling local methods.

Note: LINQ expressions were introduced in .NET 3.5 so the .NET 2.0 version of this library doesn't support the service operation API.

The CallService method takes a lambda expression containing a single method call and uses it to build a service request payload and a target URL path. The method name should match the service name or have an alternate name specified via the SDataServiceOperation attribute. Similarly, the parameter names should match the request property names or specify alternatives via the SDataServiceParameter attribute. There's no need to actually implement the method since it's only used as a convenient way to define the service interface and is never actually executed.

Static Operations

Static service operations don't require entity context. The following example calls the static GetUserContext service and outputs the three user values returned.

var client = new SDataClient("http://example.com/sdata/slx/system/-/")
    {
        UserName = "admin",
        Password = "password"
    };
var user = await client.CallServiceAsync(() => Global.GetCurrentUser());
Console.WriteLine(user.UserId);
Console.WriteLine(user.UserName);
Console.WriteLine(user.PrettyName);

public static class Global
{
    public static CurrentUser GetCurrentUser()
    {
        throw new NotSupportedException();
    }
}

public class CurrentUser
{
    [DataMember(Name = "userId")]
    public string UserId { get; set; }

    [DataMember(Name = "userName")]
    public string UserName { get; set; }

    [DataMember(Name = "prettyName")]
    public string PrettyName { get; set; }
}

Instance Operations

Instance service operations act on a specific entity instance and therefore require entity context. This can be passed in the URL via a selector or in the posted payload as a key or a full resource. The following example reads an activity then completes it.

var activity = await client.GetAsync<Activity>("VDEMOA000001");
var history = await client.CallServiceAsync(
    () => activity.Complete("UDEMOA000001", "Complete", DateTime.Now));
Console.WriteLine(history.Id);
Console.WriteLine(history.UserId);
Console.WriteLine(history.Result);
Console.WriteLine(history.CompletedDate);

[SDataPath("activities")]
public class Activity
{
    [SDataProtocolProperty(SDataProtocolProperty.Key)]
    public string Id { get; set; }

    [SDataServiceOperation(InstancePropertyName = "entity",
                           PassInstanceBy = InstancePassingConvention.ObjectProperty,
                           ResultPropertyName = "Result")]
    public History Complete(string userId, string result, DateTime completeDate)
    {
        throw new NotSupportedException();
    }
}

public class History
{
    [SDataProtocolProperty(SDataProtocolProperty.Key)]
    public string Id { get; set; }

    public string UserId { get; set; }
    public string Result { get; set; }
    public DateTime? CompletedDate { get; set; }
}

The SDataServiceOperation attribute defines important metadata that's used to build the request payload and interpret the response payload. The InstancePropertyName property specifies the name of the instance context property on the request and the ResultPropertyName property specifies the name of the response property that should be deserialized as the method's return value.

Untyped Alternative

Services can also be called without POCOs and stub methods using the more basic Post method. Both the request payload and the target URL path must be built manually when using this approach. The following example completes an activity using an anonymously typed request payload and outputs the ID of the created history record.

var service = new
    {
        Request = new
            {
                entity = "VDEMOA000001",
                userId = "UDEMOA000001",
                result = "Complete",
                completeDate = DateTime.Now
            },
        Response = new
            {
                HistoryId = default(string)
            }
    };
service = await client.PostAsync(service, "activities/$service/Complete");
Console.WriteLine(service.Response.HistoryId);

It's important that the anonymous type include a response property with property stubs so that the desired response data is captured during deserialization.

More information on service operations can be found in section 11 of the SData specification.

Clone this wiki locally