-
Notifications
You must be signed in to change notification settings - Fork 4
Advanced
| 1. Tracing | 2. Related Links | 3. WinForms Data Binding | 4. Templates | 5. Conflict Resolution | 6. On Demand Credentials
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 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.
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.
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.
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.
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.