Integrating WebServices or WCF into a Silverlight application often introduces significant overhead. Developers frequently encounter configuration challenges, such as updating service addresses when server IPs change or managing complex cross-domain clientaccesspolicy.xml files. While dynamic binding is an option, it still requires external configuration and can impact performance compared to direct communication methods.
For scenarios like validating file upload constraints—where you need to query a database for file type permissions or size limits before starting a transfer—a more streamlined approach is using an ASP.NET Ganeric Handler (.ashx). This bypasses the heavy SOAP/WCF stack while remaining flexible and efficient.
The following implementation demonstrates how to communicate with a Generic Handler using HttpWebRequest. First, we define a state container to maintain context across asynchronous operations:
public class TransferState
{
public HttpWebRequest Request { get; set; }
public FileMetadata Metadata { get; set; }
}
public class FileMetadata
{
public string Name { get; set; }
public Stream Content { get; set; }
}
When a file is selected for processing, we construct the request. It is recommended to pass the handler's path via initParams in the Silverlight host page to keep the application portable.
private void ValidateAndAddFile(FileInfo info)
{
var metadata = new FileMetadata
{
Name = info.Name,
Content = info.OpenRead()
};
string handlerPath = ConfigurationManager.GetHandlerUri("ValidationHandler");
if (!string.IsNullOrEmpty(handlerPath))
{
UriBuilder uriBuilder = new UriBuilder(handlerPath);
string queryParams = string.Format("fileName={0}&ext={1}",
HttpUtility.UrlEncode(metadata.Name),
HttpUtility.UrlEncode(info.Extension));
uriBuilder.Query = string.IsNullOrEmpty(uriBuilder.Query)
? queryParams
: uriBuilder.Query.Substring(1) + "&" + queryParams;
HttpWebRequest webReq = (HttpWebRequest)WebRequest.Create(uriBuilder.Uri);
webReq.Method = "POST";
var state = new TransferState
{
Metadata = metadata,
Request = webReq
};
// Begin the asynchronous request pipeline
webReq.BeginGetRequestStream(new AsyncCallback(OpenRequestStream), state);
}
else
{
_fileCollection.Add(metadata);
}
}
Since Silverlight networking is asynchronous, we handle the request and response in separate callback methods. Note the importance of using the Dispatcher when updating the UI or observable collections from a background thread.
private void OpenRequestStream(IAsyncResult ar)
{
var state = (TransferState)ar.AsyncState;
Stream postStream = state.Request.EndGetRequestStream(ar);
// In this example, we simply close the stream to trigger the POST,
// but you could write additional data to the body here.
postStream.Close();
state.Request.BeginGetResponse(new AsyncCallback(HandleHandlerResponse), state);
}
private void HandleHandlerResponse(IAsyncResult ar)
{
var state = (TransferState)ar.AsyncState;
var response = (HttpWebResponse)state.Request.EndGetResponse(ar);
using (var reader = new StreamReader(response.GetResponseStream()))
{
string result = reader.ReadToEnd();
decimal allowedLimit = Convert.ToDecimal(result);
// Check if the file size is within the allowed limit (in KB)
if (allowedLimit == 0 || (state.Metadata.Content.Length / 1024) <= allowedLimit)
{
this.Dispatcher.BeginInvoke(() => _fileCollection.Add(state.Metadata));
}
else if (allowedLimit < 0)
{
this.Dispatcher.BeginInvoke(() =>
{
HtmlPage.Window.Alert("This file extension is not permitted.");
});
}
else
{
this.Dispatcher.BeginInvoke(() =>
{
string msg = string.Format("File exceeds the {0}KB limit.", allowedLimit);
HtmlPage.Window.Alert(msg);
});
}
}
}
By using this pattern, you avoid the complexities of service references while maintaining high performance. This method is particularly usseful for utility tasks, configuration fetching, or lightweight data validation within Silverlight applications.