Tuesday, February 26, 2008

CAB SCSF- Loading the profilecatalog.xml file from a web service

The Composite UI Application Block provides a service to load modules when the application starts. By default, it uses an XML catalog file to determine the modules to load. The default name for this file is ProfileCatalog.xml.

The profile catalog is just a configuration that specifies which modules and services need to be loaded into the application. By default, the catalog is just an XML file that resides in the application directory. This XML file specifies which modules need to be loaded. By writing and registering a custom IModuleEnumerator class, you can override this behavior to get the catalog from another location, such as a Web service.

This article will discuss how to load the ProfileCatalog.XML file based on user Roles.
We will use a web service that will return the respective profile catalog based on user roles.

For example suppose you have a system where you want to hide the admin module from the normal users in the application. Instead of scattering my code with if (User.IsInRole("Admin")) statements, I decided to download the profile catalog based on the user credentials with the appropriate modules.

Here’s a simple example, I’ve created a web service method that returns my profile catalog file based on the roles I pass in as parameter.

Setting up the WEB Service

using System;
using System.Data;
using System.Web;
using System.Collections;
using System.Web.Services;
using System.Web.Services.Protocols;
using System.ComponentModel;
using System.IO;

namespace ProfileCatalogASMXService
{
///
/// Summary description for ProfileCatalogGenerator
///

[WebService(Namespace = "http://tempuri.org/")]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
[ToolboxItem(false)]
public class ProfileCatalogGenerator : System.Web.Services.WebService
{

[WebMethod]
public string GetProfileCatalog(string[] roles)
{
return GetProfileCatalog(roles, Server.MapPath("./"));
}


private string GetProfileCatalog(string[] roles, string basePath)
{
string catalogFilePath;
catalogFilePath = GetCatalogFilePath(roles, basePath);
string result;
try
{
result = File.ReadAllText(catalogFilePath);
}
catch
{
result = null;
}
return result;
}

private string GetCatalogFilePath(string[] roles, string basePath)
{
string catalogFilePath;
if (Array.IndexOf(roles, "User") > -1)
catalogFilePath = "UserProfileCatalog.xml";
else
catalogFilePath = "ProfileCatalog.xml";
return Path.Combine(basePath, catalogFilePath);
}

}
}

Setting up the WEB Service

When I created my SCSF application using the Smart Client Software factory Guidance Package, the default implementation was to load the profilecatalog.xml file from my application root directory.

I have to change the default implementation service to the custom service that calls the web service to get the profile catalog based on roles.

Steps:

Add a reference to the web service to the Infrastructure.Library Project in the application


In the Infrastructure.Library.Services folder create an interface for the profile catalog service and then the service that implements that interface.

using System;
namespace ProfileCatalogSample.Infrastructure.Library.Services
{
public interface IProfileCatalogService
{
string GetProfileCatalog(string[] roles);
string Url { get; set; }
}
}


using System;
using System.Collections.Generic;
using System.Text;
using ProfileCatalogSample.Infrastructure.Library;

namespace ProfileCatalogSample.Infrastructure.Library.Services
{
public class ProfileCatalogService: IProfileCatalogService, IDisposable
{
private ProfileCatalogServiceProxy.ProfileCatalogGenerator proxy;

public ProfileCatalogService()
{
proxy = new ProfileCatalogServiceProxy.ProfileCatalogGenerator();
}

public string GetProfileCatalog(string[] roles)
{
return proxy.GetProfileCatalog(roles);
}

public string Url
{
get
{
return proxy.Url;
}
set
{
proxy.Url = value;
}
}

~ProfileCatalogService()
{
Dispose(false);
}

public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}

protected virtual void Dispose(bool disposing)
{
if (disposing)
{
proxy.Dispose();
}
}

}
}


Open the SmartClientApplication.cs file from the Infrastructure.Library project.

Comment the following line in the AddServices() function that calls the default profile catalog service

RootWorkItem.Services.AddNew();

Add the following lines to the AddServices() function

RootWorkItem.Services.AddNew();
RootWorkItem.Services.AddNew();
Change the WebServiceCatalogModuleInfoStore class which is responsible for loading the modules from the profile catalog file.

Open the Class file and change the _catalogUrl to the proxy URL.

_catalogUrl = ProfileCatalogSample.Infrastructure.Library.Properties.Settings.Default.Infrastructure_Library_ProfileCatalogServiceProxy_ProfileCatalogGenerator;

Adding roles into the WebServiceCatalogModuleInfoStore

In the SmartClientApplication.cs file add the following code

protected override void BeforeShellCreated()
{
base.BeforeShellCreated();
InitializeWebServiceCatalogModuleInfoStore();
}

private void InitializeWebServiceCatalogModuleInfoStore()
{
WebServiceCatalogModuleInfoStore store =
RootWorkItem.Services.Get() as WebServiceCatalogModuleInfoStore;
//The roles should be generated by a login service based on the user provided credentials
store.Roles = new string[]{"Users"};
}


Copy and paste the profilecatalog.xml file from the Shell project to the web service root path.

Next time you load your application, based on the roles provided, the respective profile catalog will be returned.

Try changing the roles you have passed as “Admin” and “Users” to see which profile catalog gets loaded.