The previous section looked at querying an OData service using the OData model class. Although accessing the OData service model classes directly from a viewmodel is possible, it can make unit testing difficult because it relies on the actual OData producer.
In the sample for this chapter, an intermediary class called EbayClient
performs all OData queries. EbayClient
implements the custom IEbayClient
interface, shown in the following excerpt:
public interface IEbayClient
{
void SearchItemsAsync(string query,
int beginIndex = 0, int maxItems = 0);
event EventHandler<SearchItemsCompleteEventArgs> SearchItemsComplete;
}
By using the IEbayClient
interface and not a concrete implementation, we are able to substitute the EbayClient
implementation for a mock type to enable testing of the user interface without needing to send data over the wire.
The IEbayClient
interface is designed to be used asynchronously; the SearchItemsAsync
method has a void return type, and an event is used to signal that the call has completed. When the search completes, the SearchItemsComplete
method is raised using a custom extension method (see Listing 27.2).
The static HttpUtility.UrlEncode
method transforms any special characters entered by the user into URL compatible characters. This prevents the user from injecting OData query options, or inadvertently causing the request to fail.
public class EbayClient : IEbayClient
{
DataServiceCollection<Item> searchResult;
public void SearchItemsAsync(string query,
int beginIndex = 0, int maxItems = 0)
{
EBayData ebayData
= new EBayData(new Uri("http://ebayodata.cloudapp.net/"));
searchResult = new DataServiceCollection<Item>(ebayData);
searchResult.LoadCompleted
+= (sender, args) =>
{
if (args.Error != null)
{
SearchItemsComplete.Raise(this,
new SearchItemsCompleteEventArgs(
null, query, args.Error));
return;
}
SearchItemsComplete.Raise(this,
new SearchItemsCompleteEventArgs(searchResult, query));
};
string parameter = HttpUtility.UrlEncode(query);
IQueryable<Item> serviceQuery
= ebayData.Items.AddQueryOption("search", parameter)
.AddQueryOption("$skip", beginIndex)
.AddQueryOption("$top", maxItems);
//serviceQuery = from item in serviceQuery
// where item.CurrentPrice > 100.0
// select item;
searchResult.LoadAsync(serviceQuery);
}
public event EventHandler<SearchItemsCompleteEventArgs> SearchItemsComplete;
}
The SearchItemsCompleteEventArgs
class extends the custom ResultEventArgs
, which contains the result produced by the OData proxy, the type of which is defined by a generic parameter. An optional parameter allows it to supply an Exception
instance if the call fails (see Listing 27.3).
public class ResultEventArgs<T> : EventArgs
{
public T Result { get; private set; }
public Exception Error { get; private set; }
public ResultEventArgs(T result, Exception error = null)
{
Result = result;
Error = error;
}
}
The SearchItemsCompleteEventArgs
adds a string Query
property, which identifies the original query (see Listing 27.4).
public class SearchItemsCompleteEventArgs
: ResultEventArgs<ObservableCollection<Item>>
{
public string Query { get; private set; }
public SearchItemsCompleteEventArgs(
ObservableCollection<Item> result, string query, Exception error = null)
: base(result, error)
{
Query = query;
}
}
The sample also comprises a view named EbaySearchView
and its viewmodel: EbaySearchViewModel
.
3.147.79.241