Tuesday, September 4, 2007

Designing and Building a CAB Application

1. Create the shell and the form, and displaying the form
2. creating and loading the module
3. Adding the tabbed workspace to the form and running the module
4. Creating and adding the SmartPart user control to the module, and displaying the results through this SmartPart


Creating the Shell and the Form

1. Application name -> ShellApplication

2. Form1.cs -> ShellForm.cs

3. Add References
Microsoft.Practices.CompositeUI.dll
Microsoft.Practices.CompositeUI.WinForms.dll
Microsoft.Practices.ObjectBuilder.dll
Adding a new WorkItem

4. Add New class named ShellWorkItem.cs
Using Microsoft.Practices.CompositeUI

5. Class should be public and inherit from WorkItem
public class ShellWorkItem : WorkItem
{
}
6. Rename Program.cs to ShellApplication.cs
using Microsoft.Practices.CompositeUI.WinForms;
7. Replace the static class with public and inherit from FormShellApplication
public class ShellApplication : FormShellApplication<ShellWorkItem, ShellForm>
{
}
8. Add the [STAThread] attribute and intiate ShellApplication class and call Run method.
[STAThread]
static void Main()
{
new ShellApplication().Run();
}


Creating and Loading the Module

1. Create a new Class Library named MyModule

2. set Outputpath to ..\ShellApplication\bin\Debug (from project properties build output page)

3. Add reference
Microsoft.Practices.CompositeUI.dll
Microsoft.Practices.CompositeUI.WinForms.dll
Microsoft.Practices.ObjectBuilder.dll
4. Add a class named MyWorkItem.cs. Make it public and derive from WorkItem
using Microsoft.Practices.CompositeUI;
public class MyWorkItem : WorkItem
{
}
5. Add a class named MyModuleInit.cs and make it public and derive from ModuleInit. Add a variable myCatalogService reference the WorkItemTypeCatalogService. so that you can access it to register your WorkItem.
using Microsoft.Practices.CompositeUI;
using Microsoft.Practices.CompositeUI.Services;
using System.Windows.Forms;

public class MyModuleInit : ModuleInit
{
private IWorkItemTypeCatalogService myCatalogService;
}
6. Add the following public property
[ServiceDependency]
public IWorkItemTypeCatalogService myWorkItemCatalog
{
set { myCatalogService = value; }
}
7. Override the Load method of ModuleInit
public override void Load()
{
base.Load();
myCatalogService.RegisterWorkItem<MyWorkItem>();
}
8. Add a new XML file named ProfileCatalog.xml
<?xml version="1.0" encoding="utf-8" ?>
<SolutionProfile xmlns="http://schemas.microsoft.com/pag/cab-profile">
<Modules>
<ModuleInfo AssemblyFile="MyModule.dll" />
</Modules>
</SolutionProfile>
9. select property of ProfileCatalog.xml and change the Copy To Output Directory property to Copy Always


Adding the TabWorkspace

1. Open the ShellForm.cs and drag a SplitContainer control onto the form

(Note: right click the Toolbox, click items.. and then browse and select
Microsoft.Practices.CompositeUI.Winforms.dll)
2. Drag a TabWorkSpace onto the form and drop it onto the left hand panel of the SplitContainer.

3. Right click the TabWorkSpace and click Remove Tab.


Creating and Showing the SmartPart

Create an Interface

1. Add a new Interface named IMyView.cs and make it public
public interface IMyView
{
event EventHandler Load;
string Message { get; set; }
}
Create a SmartPart user control

1. Add a new User Control named MyView.cs

2. Drag a Label control

3. Derive the class Myview from IMyView
public partial class MyView : UserControl, IMyView
4. Right-click IMyView and click Implement Interface.

5. Replace the two throw statements
public string Message
{
get
{
return this.label1.Text;
}
set
{
this.label1.Text = value;
}
}
Create a Presenter

6. Create a new class named MyPresenter.cs and add a variable of type IMyView
public class MyPresenter
{
IMyView view;
7. Create a constructor for the class. The constructor takes a reference to the view, sets it as the view for this presenter, and subscribes to the Load event of the view
public MyPresenter(IMyView view)
{
this.view = view;
view.Load += new EventHandler(view_Load);
}
8. Create an Event handler for the Load event. This sets the Message property of the Label control
void view_Load(object sender, EventArgs e)
{
view.Message = "Hello World from a Module";
}
Reference to the WorkItem

1. Open the MyModuleInit.cs and add a variable parentWorkItem. It reference to the root ShellWorkItem
private WorkItem parentWorkItem;

2. Add a public property to the class. It references the existing WorkItem, and passes back a reference to it.
[ServiceDependency]
public WorkItem ParentWorkItem
{
set { parentWorkItem = value; }
}
3. Modify the Load method
public override void Load()
{
base.Load();
MyWorkItem myWorkItem = parentWorkItem.WorkItems.AddNew<MyWorkItem>();
myWorkItem.Run(parentWorkItem.Workspaces["tabWorkspace1"]);
}

Create and show the view

4. Open the file MyWorkItem.cs
using Microsoft.Practices.CompositeUI.SmartParts;
5. Create a public Run method that accepts as a parameter a reference to the TabWorkspace (IWorkspace will let you change the type of workspace in future)
public void Run(IWorkspace TabWorkspace)
{
}
6. Add statements to the Run method that create a new instance of the MyView class and a new instance of the MyPresenter class. Passing the view instance you just created to the presenter connects them together
IMyView view = this.Items.AddNew<MyView>();
MyPresenter presenter = new MyPresenter(view);
7. Add statements to the Run method that add the new presenter to the current WorkItem and call the Show method of the TabWorkspace to display the view.
this.Items.Add(presenter);
TabWorkspace.Show(view);

Saturday, September 1, 2007

The Endpoint Catalog Application block

Is a simple service that uses a dictionary to store
  1. Endpoints - An endpoint consists of a name and a series of network names appropriate for that endpoint.
  2. Associated credential for each endpoint - A credential consists of two or three String values: a user name, a password, and optinally a logon domain name.
The following XML shows a typical configuration section in an App.config file.
<?xml version="1.0" encoding="utf-8" ?>
<configuration?>
...
<Endpoints?>
<EndpointItems?>

<add Name="my-host-name" Address="http://default-host.com/path"
UserName="user-name" Password="password"?>
<NetworkItems?>
<add Name="Internet" Address="http://internet-host.com/path"
UserName="user-name" Password="password" /?>
<add Name="Work" Address="http://work-host.com/path"
UserName="user-name" Password="password" Domain="domain-name" /?>
</NetworkItems?>
</add?>

</EndpointItems?>
</Endpoints?>

</configuration?>


Configuring the Endpoint Catalog Application Block

Define a configuration section for the application block in the application configuration file.
<configSections >
<section name="Endpoints" type="Microsoft.Practices.SmartClient.EndpointCatalog.Configuration.EndpointSection, Microsoft.Practices.SmartClient.EndpointCatalog" />
</configSections>

Creating an Endpoint Catelog Class Instance

Create a new EndpointCatalog and add it to a WorkItem

1. Add a refrence to the Endpoint Catalog Application block to your project and import the namespace
using Microsoft.Practices.SmartClient.EndpointCatalog;
2. If you intend to work with individual credentials, you must also import
using System.Net;
3. Create a new instance of the EndpointCatalogFactory class.

Save the new insatnce of the factory class as an IEndpointCatalogFactory interface type in case you decide to extend the application block later by creating a custom class that implements this interface.
// Specify the section name in the configuration file.
String configName = "Endpoints";
IEndpointCatalogFactory catalogFactory = new EndpointCatalogFactory(configName);
4. Use the CreateCatalog method of the EndpointCatalogFactory, that contains details of all the configured endpoints for the application.
IEndpointCatalog catalog = catalogFactory.CreateCatalog();

5. Add it to the root WorkItem of your application.
myWorkItem.Services.Add(catalog);


Getting an Endpoint Catalog Instance from a WorkItem

There are 3 methods

1. use a [ServiceDependency] attribute on a parameter of your class constructor to accept an injected reference and store it in a class-level variable.

public class MyNewClass
{
private EndpointCatalog catalog;

public MyNewClass([ServiceDependency] EndpointCatalog catalog)
{
this.catalog = catalog;
}
...
}
2. Expose a public property for the EndpointCatalog and have ObjectBuilder set it to the EndpointCatalog instance in the WorkItem.
public class MyNewClass
{
private EndpointCatalog catalog;

[ServiceDependency]
public EndpointCatalog EndpointCatalog
{
get { return this.catalog; }
set { this.catalog = value; }
}
...
}

3. Query the Services collection of the WorkItem directly to obtain a reference to the service you want
public class MyNewClass
{
private EndpointCatalog catalog;

public MyNewClass()
{
this.catalog = myWorkItem.Services.Get<endpointcatalog>();
}
...
}

Listing and Getting Information about Endpoints

Use EndpointExists method to Check whether an endpoint exists in the catalog
Use EndpointCatalog class to get information about an endpoint

// Get the number of endpoints in the catalog.
int endpointCount = catalog.Count;
// Get the address for an endpoint if it exists.
String epName = "MyWebServiceEndpoint";
String epNetworkName = "MyHost";
if (catalog.AddressExistsForEndpoint(epName, epNetworkName))
{
String epAddress = catalog.GetAddressForEndpoint(epName, epNetworkName);
// Get the credentials for this endpoint.
NetworkCredential epCredentials = catalog.GetCredentialForEndpoint(epName, epNetworkName);
String epUsername = epCredentials.UserName;
String epPassword = epCredentials.Password;
String epDomain = epCredentials.Domain;
}

The Disconnected Service Agent Application Block

Which provides management features for executing Web services from occationally connected smart client applications.

The application can maintain a queue of Web service requests when offline and then replay them when a connection to the server application becomes available.

Core Classes
  1. RequestManager class - manages the request queues and uses the services of the RequestDispatcher class to dispatch these requests. It stores the queues of pending and failed requests.
  2. Request class - is a store for a Web service request, including the arguments or parameters required by the Web service method.
  3. ConnectionMonitorAdapter class provides information about the connection used by the request

Request Manager Subsystem

Controls the dispatching of Web service requests. It takes the messages from the queue and dispatches them when the application is online
It allows you to stop and start automatic dispatch, dispatch all pending requests, and dispatch individual requests.
The requests remain in the database until successful submission to remote server or until the request expires or when exceeding the specified number of retries.

You can access the single instance of the RequestManager class through its Instance property and by calling the initialize method with following parameters
  1. The queues for pending and failed requests
  2. A reference to a class that implements the IConnectionMonitor interface
  3. A reference to a class that implements the IRequestDispatcher interface
  4. An instance of the EndPointCatalog class that contains the endpoints to use for the request.

The Request Class Hierarchy

Provide features required to create individual requests to send to a remote Web service. It exposes the properties that define the behavior, parameters, endpoints, and other details for the request.
The Enqueue method add the request to the queue or dispatch it immediately.
It exposes the CallParameters property to contain the arguments for the Web service method.

You can also influence the behavior of a request through the OfflineBehavior class. It specifies the features of the request, such as the expiration, maximum number of retries, relaive importance of request, Tag value.

The OfflineBehavior class exposes two properties that allow you to define callback handlers for its ReturnCallback and ExceptionCallback properties as instances of the CommandCallback class.


The ConnectionMonitorAdapter Class

It exposes information about the underlying connection.
It allow you to handle in your code to get information about changes to the connection state. connectionStatusChange event occurs when the current connection state changes.


Initializing the Request Manager

Assume that the queues use the table names Requests and DeadLetter for pending and failed queues.

1. add referene
Microsoft.Practices.Smartclient.DisconnectedAgent
Microsoft.Practices.Smartclient.EnterpriseLibrary
2. Invoke any of the overloads of the static method Initialize.
RequestManager requestManager = DatabaseRequestManagerIntializer.Initialize();
it using the default database specified in the Data Access application Block configuration

// or

RequestManager requestManager = DatabaseRequestManagerIntializer.Initialize("databaseName");
You can specify a particular data base.

Creating a Simple Request

1. Create a new instance of the Request class and set the properties for this request. you must specify the name of the Web service method exposed by the remote server and the name of the endpoint.
Request req = new Request();
req.MethodName = "DeliveryRouteUpdate";
req.Endpoint = "MyWebServiceHost";
2. You must also specify the online proxy type for the request to allow the dispatcher to submit it to the remotr server. The proxy class is the one that Visual Studio generates when you add a Web reference to your project
req.OnlineProxyType = typeof(MyWebServiceProxy);
3. To specify the behavior of the request, set the properties of its OfflineBehavior instance using the Behavior property exposed by the Request class.
behavior.ProxyFactoryType = typeof(WebServiceProxyFactory);
4. Set the values of the other OfflineBehavior properties are required. eg Tag, MaxRetries, Stamps


Adding parameters to a Request

Create an array of type Object containig the parameters you want to pass to the Web service by using the static ToArray method ofthe CallParameters helper classs
int customerCode = 1234;
string comment = "New value for comment";
req.CallParameters = CallParameters.ToArray(customerCode, comment);

Handling Callbacks for a Request

1. The RequestDispatcher class calls one of two methods in your application code when a request succeeds or fails.
Create a class to contain these methods
public class MyDisconnectedServiceAgentCallbackClass
{
...
}
2. Create a method that takes
- references to the Request.
- an Object array for the parameters you submitted to the service.
- an Object for any value returned by the web service

public void MyReturnCallbackMethod(Request req, Object[] qParams,
Object result)
{
MessageBox.Show("Request succeeded. Return value is: "
+ result.ToString());
}

3. To get an indication of when a request fails, you create a method that takes as parameters a reference to the Request and an Exception instance. The method should return a value from the OnExceptionAction enumeration, either Dismiss or Retry.
public OnExceptionAction MyExceptionCallbackMethod(Request req,
Exception ex)
{
MessageBox.Show("Your request failed with error: " + ex.Message);
return OnExceptionAction.Dismiss;
}
4. Specify that the dispatcher should call these methods in your class by setting the relevant properties of the OfflineBehavior instance for your request.
req.Behavior.ReturnCallback = new CommandCallback(typeof(MyDisconnectedServiceAgentCallbackClass), "MyReturnCallbackMethod");
req.Behavior.ExceptionCallback = new CommandCallback(typeof(MyDisconnectedServiceAgentCallbackClass), "MyExceptionCallbackMethod");

Adding a Request to a Queue

Pass the configured request to the Enqueue method of the DatabaseRequestQueue instance.
reqManager.RequestQueue.Enqueue(req);

Removing a Request from the Queue

use the Remove method of the DatabaseRequestQueue instance
reqManager.DeadLetterQueue.Remove(req);

Accessing Requests in a Queue

You can reference the pending request queue using the RequestQueue property
IRequestQueue theQueue = reqManager.RequestQueue;
// or:

the "dead letter" or failed request queue using the DeadLetterQueue property
IRequestQueue theQueue = reqManager.DeadLetterQueue;
To remove a request from the queue use the Remove method of the queue.
theQueue.Remove(req);
Dispatching a Single Request Immediately

Call the DispatchRequest method of the RequestManager, passing to it the request to dispatch. The dispatching process is asynchronous.
reqManager.RequestQueue.Enqueue(req);
reqManager.DispatchRequest(req);
Dispatching all pending requests
reqManager.DispatchAllPendingRequests();
Dispatching Specific pending Requests

To dispatch requests having a specific value for their Tag property (say "Sales Order")
reqManager.DispatchPendingRequestsByTag("Sales Order");
Starting and Stoping the Dispatcher Service

To stop the dispatch of requests, call the StopAutomaticDispath method of RequestManager.
if (reqManager.Running)
{
reqManager.StopAutomaticDispatch();
}
To start the dispatch of requests, call the StartautomaticDispatch method of the RequestManager
if (! reqManager.Running)
{
reqManager.StartAutomaticDispatch();
}