Skip to content

Advanced

Nathan Baulch edited this page Jun 16, 2014 · 1 revision

| Contents

| 1. Tracing | 2. Related Links | 3. WinForms Data Binding | 4. Templates | 5. Conflict Resolution | 6. On Demand Credentials

Tracing

The SDataClient and SDataRequest objects have a Trace property that can be used to intercept individual request and response objects for logging purposes.

Note: Silverlight, Windows 8 and Windows Phone don't support tracing.

The following example attaches a custom TraceListener object that outputs request URLs to the console, then performs a contact query that requires multiple page requests internally.

var client = new SDataClient("http://example.com/sdata/slx/dynamic/-/")
    {
        UserName = "admin",
        Password = "password"
    };
client.Trace.Switch.Level = SourceLevels.Information;
client.Trace.Listeners.Add(new RequestConsoleTraceListener());
var contacts = await client.Query<Contact>().Take(300).ToListAsync();

public class RequestConsoleTraceListener : ConsoleTraceListener
{
    public override void TraceData(TraceEventCache eventCache, string source,
                                   TraceEventType eventType, int id, object data)
    {
        var request = data as SDataRequest;
        if (request != null)
        {
            base.TraceData(eventCache, source, eventType, id, request.Uri);
        }
    }
}

Related Links

Related links can be found on the SDataResource and SDataCollection objects in their respective Links properties. The following example outputs the links associated with a contact resource.

var client = new SDataClient("http://example.com/sdata/slx/dynamic/-/")
    {
        UserName = "admin",
        Password = "password"
    };
var contact = await client.GetAsync("CDEMOA000001", "contacts");
foreach (var link in contact.Links)
{
    Console.WriteLine("{0}: {1}", link.Relation, link.Uri);
}

Links can be mapped to an appropriately typed property on POCO objects by annotating the property with the SDataProtocolProperty attribute.

More information on links can be found in section 3.3 of the SData specification.

WinForms Data Binding

Data binding to untyped SDataResource objects in WinForms is supported through the use of a custom TypeDescriptionProvider.

Note: Silverlight, Windows 8 and Windows Phone don't support type descriptors.

The following example fetches an untyped contact and accesses the full name using a type descriptor.

var client = new SDataClient("http://example.com/sdata/slx/dynamic/-/")
    {
        UserName = "admin",
        Password = "password"
    };
var contact = await client.GetAsync("CDEMOA000001", "contacts");
var nameProp = TypeDescriptor.GetProperties(contact)["FullName"];
Console.WriteLine(nameProp.GetValue(contact));

There's no need for custom type descriptors when working with POCOs.

Templates

This library doesn't have any special handling for templates. The following example requests a contact template by manually building the path and calling Execute.

var client = new SDataClient("http://example.com/sdata/slx/dynamic/-/")
    {
        UserName = "admin",
        Password = "password"
    };
var param = new SDataParameters {Path = "contacts/$template"};
var results = await client.ExecuteAsync<Contact>(param);
var contact = results.Content;

More information on templates can be found in section 8.2 of the SData specification.

Conflict Resolution

In addition to lightening posted payloads, change tracking can also be helpful when update conflicts occur. The following example catches potential conflicts and overlays the local changes onto the updated resource. A user can then review the new resource and resubmit the update if desired.

var client = new SDataClient("http://example.com/sdata/slx/dynamic/-/")
    {
        UserName = "admin",
        Password = "password"
    };
var contact = await client.GetAsync("CDEMOA000001", "contacts");
contact["Status"] = "Inactive";
var conflict = false;
try
{
    contact = await client.PutAsync(contact, "contacts");
}
catch (SDataException ex)
{
    if (ex.StatusCode != HttpStatusCode.PreconditionFailed)
    {
        throw;
    }
    conflict = true;
    var changes = (SDataResource) contact.GetChanges();
    contact = (SDataResource) ex.Content;
    foreach (var change in changes)
    {
        contact[change.Key] = change.Value;
    }
}

This basic implementation doesn't overlay changes in nested resources and collections.

More information on concurrency handling can be found in section 9.3 of the SData specification.

On Demand Credentials

The Credentials property can be used as an alternative to the UserName and Password properties. The following example uses a custom ICredentials object that prompts the user for credentials the first time they're required and retains them for use in future requests.

var client = new SDataClient("http://example.com/sdata/slx/dynamic/-/")
    {
        Credentials = new PromptCredentials()
    };
var contacts = await client.Query<Contact>().ToListAsync();

public class PromptCredentials : ICredentials
{
    private NetworkCredential _creds;

    public NetworkCredential GetCredential(Uri uri, string authType)
    {
        if (_creds == null)
        {
            var userId = new StringBuilder();
            var userPassword = new StringBuilder();
            var info = new CREDUI_INFO();
            info.cbSize = Marshal.SizeOf(info);
            var save = false;
            var returnCode = CredUIPromptForCredentials(
                ref info,
                Application.ProductName,
                IntPtr.Zero,
                0,
                userId, 100,
                userPassword, 100,
                ref save,
                0x40082);
            if (returnCode != 0)
            {
                throw new OperationCanceledException();
            }
            _creds = new NetworkCredential(userId.ToString(), userPassword.ToString());
        }
        return _creds;
    }

    [DllImport("credui")]
    private static extern int CredUIPromptForCredentials(
        ref CREDUI_INFO pUiInfo,
        string pszTargetName,
        IntPtr reserved,
        int dwAuthError,
        StringBuilder pszUserName,
        int ulUserNameMaxChars,
        StringBuilder pszPassword,
        int ulPasswordMaxChars,
        ref bool pfSave,
        int dwFlags);

    private struct CREDUI_INFO
    {
        public int cbSize;
        public IntPtr hwndParent;
        public string pszMessageText;
        public string pszCaptionText;
        public IntPtr hbmBanner;
    }
}

A more complete implementation might store user entered credentials in a CredentialCache object which ties them to the requested URL.

Clone this wiki locally