Configuration Options
ConfigurationManager, the commonly used .NET configuration API, is unavailable in Silverlight applications. The minimum code option is to deserialize an XML configuration file, or to read from initParams.
Use MSBuild to merge XML deployment configurations
While most Silverlight applications only need Service endpoints updated in ServiceReferences.ClientConfig, there are occasions where a PRISM module may be needed to support reconfiguration. When this happens, I use an MSBuild task to create and/or merge the correct settings into a module.xml within the PRISM module, using a strongly typed object representing the XML file. I also deserialize the XML file to retrieve its values.
Strongly typed object representing its XML file
[DataContract(Name = "UserModuleConfiguration")]
public class UserModuleConfiguration : INotifyPropertyChanged
{
private String _employeeUploadServiceUrl;
[XmlAttribute]
public String EmployeeUploadServiceUrl
{
get { return _employeeUploadServiceUrl; }
set { _employeeUploadServiceUrl = value; RaisePropertyChanged("EmployeeUploadServiceUrl"); }
}
public event PropertyChangedEventHandler PropertyChanged;
private void RaisePropertyChanged(String propertyName)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
Deserialize the XML file to retrieve its values
<?xml version="1.0" encoding="utf-8" ?> <UserModuleConfiguration EmployeeUploadServiceUrl="http://localhost:62509/Services/UserModule/UserModuleUploadHandler.ashx"> </UserModuleConfiguration>
public class UserModuleConfigurationRepository : IUserModuleConfigurationRepository
{
public UserModuleConfiguration GetSection()
{
var sr = Application.GetResourceStream(new Uri("/UserModule;component/Configuration/UserModuleConfiguration.xml", UriKind.Relative));
var transform = new XmlSerializer(typeof(UserModuleConfiguration));
return (UserModuleConfiguration)transform.Deserialize(sr.Stream);
}
}
Use initParams in standalone XAPs
Another approach to configuration in Silverlight is with initParams. I only use this option sparingly, as complexity and hierarchy cannot be as easily expressed in key value pairs as they can with XML. Wherever a Silverlight application, XAP, is being used for reusable standalone functionality, then initParams is an excellent way to enable configuration at run time.
The only production examples of initParams that I have are for a standalone reusable Silverlight video player and file uploader. The following section describes how we used initParams to build a reusable file uploader, to integrate with our ASP.NET MVC application.
Standalone Reusable Silverlight File Uploader
Traditional approaches to Handling Large File Uploads in ASP.NET have revolved around plain HTML, iframe, JavaScript, and AJAX. These processes require familiarity with the IIS pipeline, and understanding RFC 1867, and some solutions vary between webfarms, web garden, and IIS7 support.
Silverlight's rich client capabilities, together with support for OpenFileDialog, make most traditional approaches obsolete. OpenFileDialog provides the stream for each user selected file, and this permits most of the data partitioning to be done on the client's machine. This could be stream.Read (buffer, readPosition, chunkSize), for instance, which removes the need to reconfigure server side settings such as maxRequestLength in web.config.
If you are interested in understanding how to build one, then this is a basic roadmap.
RadUpload requires an instance of FileUploader to
provide an easily used proxy API for uploading a file over HTTP.
Our reusable file uploader uses a custom WCF service to retrieve and delete files on the server, while the actual uploading functions are left to RadUpload.
Here are the finished UI, user interface, design and integration with our ASP.NET MVC application.
This is how to pass configuration, and enable reusability of the Silverlight control across our MVC application. Each HandlerUrl and ServiceUrl is specific to each instance.
<object data="data:application/x-silverlight-2," type="application/x-silverlight-2" width="600" height="400">
<param name="source" value="/ClientBin/RobustHaven.Silverlight.App.xap"/>
<param name="onError" value="onSilverlightError" />
<param name="background" value="White" />
<param name="minRuntimeVersion" value="4.0.50401.0" />
<param name="autoUpgrade" value="True" />
<param name="initParams" value="HandlerUrl=http://localhost:6387/Products/WcfServices/FileUploadHandler.ashx,Key=1,ServiceUrl=http://localhost:6387/Products/WcfServices/FileUploadService.svc" />
<a href="http://go.microsoft.com/fwlink/?LinkID=149156&v=4.0.50401.0" style="text-decoration:none">
<img src="http://go.microsoft.com/fwlink/?LinkId=161376" alt="Get Microsoft Silverlight" style="border-style:none"/>
</a>
</object>
The configuration repository actually reads the initParams.
using System.Windows;
using RobustHaven.UploadFiles.Entities;
namespace RobustHaven.UploadFiles.Providers.InitParams
{
public class UploadConfigurationRepository : IUploadConfigurationRepository
{
public UploadConfiguration GetSection()
{
return new UploadConfiguration() {
ServiceUrl = Application.Current.Host.InitParams["ServiceUrl"],
HandlerUrl = Application.Current.Host.InitParams["HandlerUrl"],
Key = Application.Current.Host.InitParams["Key"]
};
}
}
}
IUploadView
<telerik:RadUpload x:Name="radUpload" Width="Auto"
Style="{StaticResource RadUploadStyle1}" UploadServiceUrl="{Binding HandlerUrl}" UploadFinished="radUpload_UploadFinished" />
Our PRISM 4.0 registration.
using System.ServiceModel;
using Microsoft.Practices.Composite.Modularity;
using Microsoft.Practices.Composite.Regions;
using Microsoft.Practices.Unity;
using RobustHaven.UploadFiles.Providers;
using RobustHaven.UploadFiles.Providers.InitParams;
using RobustHaven.UploadFiles.Ui;
using RobustHaven.UploadFiles.UploadFileServiceProxy;
namespace RobustHaven.UploadFiles
{
public class UploadModule : IModule
{
private readonly IUnityContainer _container;
private readonly IRegionManager _regionManager;
public UploadModule(IUnityContainer container, IRegionManager regionManager)
{
_container = container;
_regionManager = regionManager;
}
public void Initialize()
{
_container
.RegisterType<IUploadView, FilesView>()
.RegisterType<IUploadViewModel, UploadViewModel>()
.RegisterType<IUploadConfigurationRepository, UploadConfigurationRepository>();
// InjectionConstructor is need to support various endpoints that adhere to our custom WCF service contract to list and delete files.
_container
.RegisterType<FileUploadServiceClient, FileUploadServiceClient>(new InjectionConstructor(new BasicHttpBinding(), new EndpointAddress(new UploadConfigurationRepository().GetSection().ServiceUrl)));
_regionManager.RegisterViewWithRegion(UploadFileRegions.MainRegion, () => _container.Resolve
<IUploadViewModel>
().View);
}
}
}
Our custom WCF contract.
We only need to create the FileUploadServiceClient service proxy once to provide the concrete endpoint through initParams.
using System;
using System.Collections.Generic;
using System.ServiceModel;
namespace RobustHaven.UploadFiles
{
[ServiceContract]
public interface IFileUploadService
{
[OperationContract]
IEnumerable<File> LoadFiles(String key);
[OperationContract]
void DeleteFile(String key, int id);
}
}
Why not share the ashx/HandlerUrl and svc/ServiceUrl files?
The HandlerUrl is the custom service that ties the RadUpload client control to the server. This has to be unique, as it instructs the service whether to place the files in the filesystem or in a database.
public class ExampleFileSystemStorageFileUploadHandler : RadUploadHandler
{
public override string GetTargetFolder()
{
return HttpContext.Current.Server.MapPath("/Areas/Products/Data");
}
}
Unfortunately, the RadUploadHandler class does not provide a contract to manage existing files. Hence, a custom WCF contract needs to be implemented, to list and delete files specific to an instance. (FileSystemStorage or DatabaseStorage)
[AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
public class PriceFileUploadService : IFileUploadService
{
private readonly ProductFilesController _productFilesController;
public PriceFileUploadService(ProductFilesController productFilesController, IHttpContextBase context)
{
_productFilesController = productFilesController;;
Thread.CurrentPrincipal = context.User;
}
[PrincipalPermission(SecurityAction.Demand, Role = "PriceFileCanLoadFiles")]
public IEnumerable<File> LoadFiles(string key)
{
var priceId = Int32.Parse(key);
return _productFilesController.GetFiles(priceId);
}
[PrincipalPermission(SecurityAction.Demand, Role = "PriceFileCanDeleteFile")]
public void DeleteFile(String key, int id)
{
_productFilesController.RemoveFile(id);
}
}

Leave a Response
All comments on this article are moderated