Part I - Custom Silverlight module for Service Manager 2012 portal
In this blog we will look at how to develop a custom Silverlight module using the portal WCF service. This module will query for change requests assigned to the currently logged in portal user. Please note that this blog would be easier to understand if you have prior web development experience.
Source code for this sample application
Assumptions:
1. Familiarity with C#, Silverlight, WCF, and MVVM concepts.
2. Visual Studio 2010 is required to develop it
3. Installed Service Manager 2012 management server, and portal.
4. Understanding of SM type projections and other model based concepts.
5. Administrator level access to the portal SharePoint site and WCS web servers for deployment.
6. The sample source code was developed for SM 2012 Beta.
Steps
1. Create a new project using Silverlight application template in VS
2. Create a new test host for the SL application if you don’t already have one
3. On portal Web content Server (WCS) and extract BaseSilverlightModule.zip. This will help speed up the development of our custom module. Copy extracted folder to your solution directory.
It abstracts out the commonly needed code
1) for initializing portal WCF client proxy
2) Base viewmodel classes. E.g. for ViewModelBase can be used by all the ViewModel to connect to portal WCF
3) Data converters. E.g. DateTimeToLocalTime converter, BooleanToVisibility converter.
4) Tracing/Logging helper.
In the Silverlight application add the following references. You can find all these references by renaming any one of the SM portal Silverlight XAP to zip and then unzipping it.
E.g. MyRequestsSilverlightModule.xap
1) Microsoft.EnterpriseManagement.ServiceManager.Portal.BaseSilverlightModule.dll
2) Microsoft.EnterpriseManagement.Presentation.Core.dll (in core.zip)
3) Microsoft.EnterpriseManagement.Presentation.DataAccess.dll (in dataaccess.zip)
4) Microsoft.EnterpriseManagement.ServiceManager.Portal.BasicResources.dll (in BasicResources.zip)
5) Microsoft.EnterpriseManagement.ServiceManager.Portal.ToolkitResources.dll (in ToolkitResources.zip)
6) Microsoft.Practices.Prism.dll
7) Microsoft.Practices.Prism.UnityExtensions.dll
8) Microsoft.Practices.ServiceLocation.dll
9) Microsoft.Practices.Unity.Silverlight.dll
10) Microsoft.ServiceManager.Portal.BuildConstants.dll (provides soft references to SM mp elements and subelements).
11) System.Reactive.dll
You can find all these references by renaming any one of the SM portal Silverlight XAP to zip and then unzipping it. E.g. MyRequestsSilverlightModule.xap.
4. Delete MainPage.xaml. Create two folders for Views, and ViewModel. Build the solution and verify that it builds without any errors. At this point the Solution explorer should look like this –
5. Include Silverlight web client configuration file - ServiceReferences.clientConfig used to connect to the WCF service.
6. Move App.xaml and app.xaml.cs to Views folder. Class App derives from BaseApp in BaseSilverlightModule. Override CreateShell from BaseApp to load the basic resources, and set the root visual to the Shell we added in the last step.
protected override void CreateShell(){
TraceManager.WriteInfo("Custom MyChange Requests CreateShell");
ResourceManager.LoadResources(ResourcesEnum.Basic);
this.RootVisual = new Shell();
}
7. Add a new Silverlight page - BaseLayer.xaml to the Views folder. This is the view for the change requests. It uses one DataGrid to display the change requests and another one to display the contained activities in the selected change request.
8. Add viewmodel ChangeRequestsViewModel.cs to the ViewModel folder. It inherits from ViewModelBase in BaseSilverlightModule. ViewModelBase implements INotifyPropertyChanged and the bootstrapping code required to call into the portal WCF service.
9. Add a Silverlight usercontrol Shell.xaml to Views folder. It derives from the BaseShell in Microsoft.EnterpriseManagement.ServiceManager.Portal.BaseSilverlightModule.dll (BaseSilverlightModule). Shell.xaml binds the view BaseLayer.xaml with its viewmodel - ChangeRequestsViewModel.
<CommonSilverlightModule:BaseShell
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:sdk="http://schemas.microsoft.com/winfx/2006/xaml/presentation/sdk"
xmlns:navigation="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Navigation"
mc:Ignorable="d" d:DesignWidth="640" d:DesignHeight="480"
x:Name="ContentFrame"
xmlns:CommonSilverlightModule="clr-namespace:Microsoft.EnterpriseManagement.ServiceManager.Portal.BaseSilverlightModule;assembly=Microsoft.EnterpriseManagement.ServiceManager.Portal.BaseSilverlightModule"
x:Class="MyChangeRequests.Views.Shell"
xmlns:local="clr-namespace:MyChangeRequests.ViewModel"
Source = "/Views/BaseLayer.xaml">
<CommonSilverlightModule:BaseShell.DataContext>
<local:ChangeRequestsViewModel/>
</CommonSilverlightModule:BaseShell.DataContext>
</CommonSilverlightModule:BaseShell>
10. Build the solution. Deploy MyChangeRequests.xap to the SM portal contenthost clientbin (%systemdrive%\inetpub\wwwroot\System Center Service Manager Portal\ContentHost\Clientbin). Then deploy the test page MyChangeRequestsTestPage.aspx to %systemdrive%\inetpub\wwwroot\System Center Service Manager Portal\ContentHost\Testpages.
11. Now let’s look at the how to actually query SMDB data. WCF service returns objects of type IDataObject which can be directly data bound in the Silverlight. ChangeRequestsViewModel contains two observable collections of IDataObjects – one for change requests (CR) and other for activities. We perform an asynchronous query to get the CR’s in GetChangeRequests method as seen below –
private void GetChangeRequests()
{
try
{
TraceManager.WriteInfo("Query for change requests assigned to me");
// display the progress bar to indicate data fetching
this.ProgressBarVisibility = true;
string connectionSessionTicket = null;
// 1. Create data query command
AsyncDataCommand<IDataObject> command = new AsyncDataCommand<IDataObject>(
"Microsoft.EnterpriseManagement.ServiceManager.Portal.DataProviders!EnterpriseManagementObjectProjectionProvider/GetProjectionsByCriteria",
connectionSessionTicket, new ObservableDataModel());
// specify the type projection Id you want to query for
command.Parameters["typeProjectionId"] = ChangeRequestsViewModel.PortalChangeRequestProjectionId;
// only partial querying is supported by portal WCF service methods.
command.Parameters["propertyNames"] = new List<String>() { "Id", "Title", "Status", "Description", "Activity.Id", "Activity.Title", "Activity.Status" };
// query criteria
command.Parameters["criteria"] = ChangeRequestsViewModel.RequestsAssignedToMeCriteria;
// 3. retrieve the query results
command.CommandCompleted += (sender, e) =>
{
// hide the progress bar
this.ProgressBarVisibility = false;
if (e.Result == null)
{
this.ChangeRequestsAssignedToMe = null;
TraceManager.WriteInfo("Number of CR assigned to me = 0");
}
else
{
this.ChangeRequestsAssignedToMe = new ObservableCollection<IDataObject>(e.Result);
TraceManager.WriteInfo("Retrieved change requests assigned to me = " + this.ChangeRequestsAssignedToMe.Count.ToString());
}
};
// 2. execute the query
this.DataGateway.ExecuteAsync(command);
}
catch(Exception ex)
{
// hide the progress bar
this.ProgressBarVisibility = false;
// use TraceManager from BaseSilverlightmodule to log the exception
TraceManager.WriteError(ex.Message, ex.StackTrace);
}
}
WCF service exposes create, update, and delete operations for the SM instance space. AsyncDataCommand<IDataObject> is used to do perform an asynchronous query. First you need to create the AsyncDataCommand. It needs a unique identifier for the specific service operation you want to invoke, parameters like type projection id and output columns.
E.g. We use the operation "Microsoft.EnterpriseManagement.ServiceManager.Portal.DataProviders!EnterpriseManagementObjectProjectionProvider/GetProjectionsByCriteria" to query for all change requests and its contained activities we need to use a type projection Id, criteria to get all CR assigned to current user, and columns you would like to get back. In terms of a SQL query think of the Typeprojection Id as the join of the SQL tables, criteria as the where clause of the query and columns as the select part of the query.
Secondly execute the aysnc data command.
// 2. execute the query
this.DataGateway.ExecuteAsync(command);
Finally the retrieve the query results in the async data command completed event handler.
// 3. retrieve the query results
command.CommandCompleted += (sender, e) => {..}
Note: you can use the same operation to query over any type projection in SM. E.g. you can get all computers related to a specific change request. To do this provide the corresponding computer TypeProjection Id, criteria, and output columns.
12. Access MyChangeRequestsTestPage.aspx from the SM portal to view the change requests assigned to the current portal user.
Notes:
This test page is not a SharePoint site page so it cannot be added to the SM Sharepoint site pages collection.
Stay tuned for Part II of this blog – Creating a custom ASP.NET web part which hosts the above Silverlight module in Sharepoint.