Use CSLA with CAB+GAX+SCSF (tutorial)

Use CSLA with CAB+GAX+SCSF (tutorial)

Old forum URL: forums.lhotka.net/forums/t/1940.aspx


tiago posted on Monday, December 11, 2006

CAB (Composite UI Application Block) and SCSF (Smart Client software Factory) seem to be interesting technologies. But "steep learning curve" it's a recurring expression when someone talks about these technologies. Besides there is a lack of proper documentation: there are no books on the market and Microsoft has a lot of "how good it is" but  very few "how to use it" ones.

1 - What is CAB? How does it relate to SCSF? What part plays Guidance Automation Extensions (GAX)?

All 3 technologies are .NET Framework 2.0 only.

CAB is a framework to make the user interface of applications. As it happens with every framework, you have to learn how to use it.

CAB is not easy to use. You have to follow rules and it requires some repetitive coding tasks.

GAX is a code generator engine.

SCSF is the GAX solution for CAB. Instead of hand coding all those CAB classes and methods, you can use SCSF and just fill in the blanks.


2 - How do I setup my computer to use SCSF?
a) Install Enterprise Library January 2006 (choose to compile it at install time)
b) Install CAB (cs or vb)
c) Compile the CAB
d) Install GAX (Guidance Automation Extensions, CTP, June 2006 at the time of writing)
Note - You DON'T need to install the Guidance Automation Toolkit.
e) Install SCSF (Smart Client Software Factory, June 2006 at the time of writing)


3 - How do I use SCSF in Visual Studio 2005?
a) Go to File -> New -> Project...
b) Unfold Guidance Packages
c) Click on Smart Client Development
d) Double click on Smart Client Application and wait until the GAX finishes creating your project
e) Now go to View -> Other Windows -> Guidance Navigator Window


4 - OK. But now how do I use for doing something useful, like writing the UI part of my CSLA application?

Ham... Look, I have to go and by smokes. I'll answer that in another post. While I write it, go to "gotdotnet" at
http://www.gotdotnet.com/codegallery/releases/viewuploads.aspx?id=22f72167-af95-44ce-a6ca-f2eafbf2653c

At the very bottom of the page you will find the
eugeniop     CAB Documentation (CABCHM.zip, 866k, xxxx downloads)

This is a very good starting point for understanding the key elements of the CAB architecture:
- Component
- Module
- WorkItem
- SmartPart
- Workspace
- UIExtensionSite
- Command
- Event
- Service

and some important concepts of CAB's internals:
- Dependency Injection (ObjectBuilder)
- Service Locator (ObjectBuilder)

Cheers
Tiago Freitas Leal

[16 Dec 2006]
Found some interesting pages:
Developer Roadmap For Building Smart Client Applications at http://www.c-sharpcorner.com/UploadFile/tjayram/DeveloperRoadmapForBuildingSmartClientApplications08062006192216PM/DeveloperRoadmapForBuildingSmartClientApplications.aspx
Designing Applications using CAB & the Smart Client Software Factory at http://www.microsoft.com/downloads/details.aspx?FamilyID=5f9a8435-1651-4be2-956d-0446a89a7358&DisplayLang=en

[18 Dec 2006]
There are two main difficulties when learning CAB+SCSF:
- the key concepts
- how to do it
This is a "under construction" tutorial that will cover boths problems. Post contents are subject to change without notice (like prices, you know?).

This series of posts are based on the documentation referred on this post, but not exclusively. So don't blaim them for mistakes I might make or for the "I admit" parts.

tiago replied on Monday, December 11, 2006

General structure
A CAB application is made of Modules (read DLLs). Each Module can use Components (visual and non-visual elements). WorkItem is a component that exists to group other components, namely visual components; let's say it's the "mother of all" visual-components. The visual elements of a WorkItem are displayed in WorkSpaces and UIExtensionSites.

Confused? Let's put up a rather simplistic example:
1. create WorkItem
2. create Workspace in WorkItem
3. create visual element in WorkItem
4. show visual element in Workspace

You find there are too many steps for doing a simple task like showing a visual element? Wait until you get the full picture before jumping to conclusions.

 

Component
The elementary unit of a CAB application is the Component. There are different kinds of Components: those you can see, those that support the first kind and the ones that aren't related to visual display.

Visual elements

Support for visual elements

Non-visual elements

 

Module

A CAB application is made of Modules. Modules are distinct deployment units (read DLLs) of a CAB application. Typically, they include some combination of services, WorkItems, SmartParts, controllers, presenters, and business entities.

 

The CAB 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.

 

When the CAB loads a module, it uses reflection to determine whether the module includes a class that implements the IModule interface. You implement this interface (typically by deriving from ModuleInit) to initialize and run the module's WorkItems. Modules can also load and unload components in the root WorkItem.

 

The Smart Client Software Factory automates the creation of two different types of modules:

 

So an apps is made of modules and modules have WorkItems. That's nice to know. What's this reflection business again? We have slightly changed the subject and now we are talking about Dependency Injection. ObjectBuilder is the CAB (and Enterprise Library) part that takes care of DI. You can read a very good article about DI by Martin Fowler at http://www.martinfowler.com/articles/injection.html

The Spring framework is the most popular DI framework. It was made in Java and later ported to .NET.

The documentation referred on the first post includes some details about ObjectBuilder as a DI pattern. Anyway, we shall come back on that subject later on.

Cheers
Tiago Freitas Leal

tiago replied on Monday, December 11, 2006

A WorkItem is a run-time container (read set of collections) for components. The components can be both visual and non-visual parts of the application, such as SmartParts, Controls, and Services. Components of a given WorkItem are created and disposed of together.

 

WorkItems have one collection for each component type:
- SmartParts

- Items (views and controls)

- EventTopics

- Services

- Commands

- UIExtensionSites

- Workspaces

- WorkItems (child WorkItems)

 

Every CAB application has a single root WorkItem. The root WorkItem contains components shared accross the application (eg. MenuStrip, StatusStrip). The CAB creates the root WorkItem when the application starts. As an application executes, it creates additional WorkItems to implement application use cases. The application adds these additional WorkItems to a parent WorkItem (for example, to the root WorkItem) .You can think of a CAB smart client application as a WorkItem hierarchy, with the root of the hierarchy being the root WorkItem. WorkItems can be activated and deactivated.

Hierarchy of WorkItems

Every CAB application has a single root WorkItem. Other WorkItems descend from the root WorkItem: they can be child, grand child, grand-grand child (etc) of the root WorkItem. You can think of a CAB application as a WorkItem hierarchy, with the root of the hierarchy being the root WorkItem.

Components can access any other component of their own or ascending WorkItems. Each component can access components of:

- the same WorkItem

- the parent WorkItem

- the grand parent WorkItem (etc).


Of course all components (eg. executing code) can access components of the root WorkItem. That's why t
he root WorkItem contains components that are shared accross the application (eg. MenuStrip, StatusStrip).


WorkItems can be activated and deactivated. At all times there is at least one active WorkItem.

Note - I admit I'm not sure if there can be more than one active WorkItem at a time.

 

Hierarchy like rootWorkItem.childWorkItem.grandChildWorkItem?

 

Well, yes... I mean no! There is no object hierarchy. Every WorkItem is an object of type WorkItem that lives in the namespace Microsoft.Practices.CompositeUI. Hierarchy is kept on each WorkItem's fields:


private WorkItem parent;

...
public WorkItem Parent
{
  get { return parent; }
  ...
}

// now the child workitems

private ManagedObjectCollection<WorkItem> workItemCollection;

...
public ManagedObjectCollection<WorkItem> WorkItems
{
  get { return workItemCollection; }
}


Patterns

 

The WorkItem concept supports both the Model-View-Controller (MVC) and the Model-View-Presenter (MVP) patterns. Though SCSF automates only the use of MVP.

 

Cheers
Tiago Freitas Leal

tiago replied on Monday, December 11, 2006

SmartParts are the visual components of a CAB application. You can create SmartParts by customizing a standard user control.

How do I tell the CAB that my customized user control is a SmartPart? You must identify the SmartPart by decorating the user control class with a SmartPart attribute.

Say you build a control CustomerDetailView. The code behind your control should look like this:

[SmartPart]
public sealed partial class CustomerDetailView : UserControl
{
   /* methods go here */
}

You can display SmartParts in a SmartPartPlaceHolder or in a Workspace.

To be continued

Cheers
Tiago Freitas Leal

tiago replied on Monday, December 11, 2006

Workspaces are components that encapsulate a particular visual way of displaying controls and SmartParts. Let's translate this OOPish dialect to plain english. Workspaces are intended to organize the display of visual elements. To give you an example, SCSF splits the main form area in order to create two extra Workspaces: LeftWorkspace and RightWorkspace. Just like I do with my socks drawer: white socks on the left and black socks on the right. Of course there are other ways to organize your display: tiled, tabbed, MDI, etc.

The CAB includes the following types of workspaces:

Each Workspace is identified by a unique string value. In order to use MyWorkspace of type DeckWorkspace, you have to create it, set its name and a few other things:

 

this.MyWorkspace = new Microsoft.Practices.CompositeUI.WinForms.DeckWorkspace();
this.MyWorkspace.Dock = System.Windows.Forms.DockStyle.Fill;
this.MyWorkspace.Location = new System.Drawing.Point(0, 0);
this.MyWorkspace.Name = "MyWorkspace"; // this is the unique string value
this.MyWorkspace.Size = new System.Drawing.Size(698, 516);
this.MyWorkspace.TabIndex = 1;
this.MyWorkspace.Text = "MyWorkspace";
this.Controls.Add(this.MyWorkspace);

 

Remember two details:

- Workspaces serve the purpose of displaying SmartParts

- the WorkItem keeps a Workspaces list

Supose you have a SmartPart (class) CustomerDetailView and you want to display it on MyWorkspace:

 

CustomerDetailView customerDetailView = WorkItem.SmartParts.AddNew<CustomerDetailView>();
WorkItem.Workspaces["MyWorkspace"].Show(customerDetailView);

 

We will see later on that SCSF doesn't use strings to identify the Workspace: it uses constants instead.

 

Cheers
Tiago Freitas Leal

tiago replied on Monday, December 11, 2006

Named user interface elements that can support child elements. Each UIExtensionSite is identified by a unique string value. This string is used to identify elements when interacting with and adding child elements to that site, such as when adding a menu item to a menu bar. Examples:

To be continued

 

Cheers
Tiago Freitas Leal

tiago replied on Monday, December 11, 2006

Commands enables you to assign the same executable code to multiple UIElements. This can be useful when you have a toolbar button and menu item that execute the same code and when you have multiple menu bars that contain similar items applicable to different SmartParts.

 

You declare Commands by adding the CommandHandler attribute to the declaration specifying a command name.

 

To associate a Command with a UIElement you call the AddInvoker method of the specific command handler to wire up the Click event, passing in a reference to the menu item and the event name:

 

To be continued

 

Cheers
Tiago Freitas Leal

tiago replied on Monday, December 11, 2006

Components can use events to publish or receive notifications. The CAB contains an event broker system that enables you to implement a multicast event scheme (that is, a scheme where zero or more event publications work with zero or more event subscriptions).

 

The WorkItem.EventTopics collection holds a collection of EventTopic instances, each of which defines the topic between an event source (every class marked with EventPublication attribute) and its subscribers (every class marked with EventSubscription attribute).

 

1. Publish an Event

 

1.1. You publish an event by adding the EventPublication attribute to an event declaration, This attribute uses two parameters: the event name and the event scope. The event name is a string that identifies the event. The publishing scope can be:

To publish a global event:


[EventPublication("event://UpdatesAvailable/New", PublicationScope.Global)]
public event SomeEventHandler UpdatesAvailable;

 

1.2. You can also publish an event programatically: You write code to get an instance of the EventTopic you want to publish via the EventTopics collection of a WorkItem instance, and then call its methods to register and unregister publishers.

 

2. Subscribe to an Event

 

2.1. Methods can subscribe to events using the EventSubscription attribute on a method declaration, as long as the method is within the publication scope. More than one method can subscribe to the same event, and you can specify the thread that the event subscription runs on. However, you cannot control which method will execute first because the Publisher-Subscriber pattern decouples publishers from subscribers.

To subscribe to the above event and run it on the same thread as the user  interface:


[EventSubscription("event://UpdatesAvailable/New", Thread=ThreadOption.UserInterface)]
public void NewUpdates(object sender, SomeEventArgs numUpdates)
{
 MessageBox.Show(numUpdates.ToString(), "Updates available");
}

 

2.2. You can also subscribe to an event programatically: You write code to get an instance of the EventTopic you want to subscribe via the EventTopics collection of a WorkItem instance, and then call its methods to register and unregister subscribers.

 

Cheers
Tiago Freitas Leal

tiago replied on Monday, December 11, 2006

A Service is a supporting class that provides functionality to other components in a loosely coupled way.

 

In the CAB, the term service describes a component that implements some infrastructure-related functionality that other components in the application may need to use; for example, services providing authorization, logging, or hardware communication functionality. It does not mean a Web service.

 

Remember Services are components of WorkItems. A WorkItem exposes a Services property that you can use to add a service to the WorkItem.

 

The CAB includes a set of basic services that you can use in your applications (eg. ModuleLoaderService). The ModuleLoaderService uses the information returned from the service implementation of IModuleEnumerator to determine which modules should be loaded at run time.  You can also build your own services that provide infrastructure capabilities specific to your applications.

 

To be continued

 

Cheers
Tiago Freitas Leal

tiago replied on Monday, December 11, 2006

You can use ObjectBuilder by using decorated attributes like InjectionConstructor, CreateNew, Service and ServiceDependency. You can also programmatically (without using attributes) use the ObjectBuilder factory to create instances. Instead of directly constructing an object instance, you use the AddNew method of CAB container classes.

 

To be continued

 

Cheers
Tiago Freitas Leal

tiago replied on Monday, December 11, 2006

 

1. Publish a Service

 

1.1. You can use the Service attribute on a public class to declare that class to be registered as a service in the root WorkItem. The CAB infrastructure creates an instance of this class and registers it as a service during application startup. The constructor parameter tells the CAB the interface key to use for the registration of the service, for location purposes. To register class MyService as a service in the root WorkItem:

 

[Service]

public class MyService

{

}

 

1.2. You can add a service by specifying it in the shell application configuration file.

 

1.3. You can also programmatically add services to WorkItems. To do this, call the Add or AddNew methods of the Services collection of the WorkItem within which you want to use the service. This can be the root WorkItem or another WorkItem that you create. To add a new service to rootWorkItem:

 

rootWorkItem.Services.AddNew<CustomerService>();

 

To use an existing service instance you have already created, use the Add method.  To add a service to myWorkItem:

 

myWorkItem.Services.Add<CustomerService>(myServiceInstance);

 

This means you can have one service instance on different WorkItems.

 

2. Subscribe to a Service

 

2.1. To obtain a reference to a service declaratively, add the ServiceDependency attribute to a property in your class that is of the type of service or interface you require:

 

private IMyService service;

 

[ServiceDependency]

public IMyService service

{

  set { service = value; }

}

 

When this attribute is present, ObjectBuilder locates an instance of the service and passes back a reference to it. To locate the service, ObjectBuilder first looks in the current WorkItem. If the service is not found, ObjectBuilder then looks at the services in the parent WorkItem. This process continues until the service is located or ObjectBuilder reaches the root WorkItem. If the service is not found, ObjectBuilder throws an exception.

 

Frequently, the ServiceDependency attribute is used for the arguments in a constructor. This means that ObjectBuilder will instantiate the required services when it creates the dependent object.

 

2.2. To obtain a reference to a service programmatically, call the Get method of the Services collection of the WorkItem, passing in the type of service:

 

IMyService myServ = (IMyService)myWorkItem.Services.Get(typeof(IMyService));

// or using generics

IMyService myServ = myWorkItem.Services.Get<>(IMyService);

 

You can read a very good article about Dependency Injection versus Service Locator patterns by Martin Fowler at http://www.martinfowler.com/articles/injection.html

 

Cheers
Tiago Freitas Leal

tiago replied on Monday, December 11, 2006

Nice to know something about the CAB elements. But how do we glue it together? There are some sample implementations for us to study. That's where the learning curve gets steep.

This is a place holder as I intend to give some examples (without code) that should be usefull enough.

[18 Dec 2006]
Sorry no can do. The examples must have some code or you won't be able to do anything at all. This means this post is under heavy construction.

As to the coding part, once you understand CAB, SCSF can do much of the work.

To be continued

Cheers
Tiago Freitas Leal

tiago replied on Monday, December 11, 2006

How does CSLA fit in the MVP pattern?

Read about Model-View-Presenter pattern: The Humble Dialog Box  at http://www.objectmentor.com/resources/articles/TheHumbleDialogBox.pdf

To be continued

Cheers
Tiago Freitas Leal

tiago replied on Monday, December 11, 2006

DI can be used for several ends.

Imagine you have this application that must support different SQL server engines like:
- SQL Server
- Oracle
- MySQL
- PostgreSQL
- Sybase
- SQLite
- DB2

This not just a problem a connection strings and stored procedures. For example, SQLite has no stored procedures and your CRUD methods need to be different.

What you need is your application to load a different module for every (or only for some) SQL engine. ObjectBuilder can take care of that.

The problem is how the CSLA data portal will handle it.

To be continued

Cheers
Tiago Freitas Leal

Bayu replied on Wednesday, December 13, 2006

tiago:

DI can be used for several ends.

Imagine you have this application that must support different SQL server engines like:
- SQL Server
- Oracle
- MySQL
- PostgreSQL
- Sybase
- SQLite
- DB2

This not just a problem a connection strings and stored procedures. For example, SQLite has no stored procedures and your CRUD methods need to be different.

What you need is your application to load a different module for every (or only for some) SQL engine. ObjectBuilder can take care of that.

The problem is how the CSLA data portal will handle it.

To be continued



Hey,

I like your review of CAB and friends, it will serve as a good reference for future use. ;-)

Regarding multi-database support: NHibernate supports almost all those DB vendors you enumerated. The nice thing with NHibernate is that you will only need 1 implementation for your dataportal methods and all DB-specific variations are dealt with by NHibernate.

So, to answer you question ...

tiago:

The problem is how the CSLA data portal will handle it.



NHibernate could handle it for you. ;-)

Here you can verify that NHibernate currently supports most of the features present in:



Keep up the good work in this thread!

Regards,
Bayu

rkelley replied on Wednesday, July 16, 2008

Sorry to dig up an old message but I have some questions for you guys. We have just recently been looking at SCSF and CAB, more specifically CAB and are evaluating it for us in several of our enterprise applications. So I have some questions that hopefully you can help me with:

1. How do you specifically hook up your business objects from your CSLA library project into your UI( in the control, controller, or work item)?

2. What do you do about databinding?

3. Bayu, I know you have said before that you use common "list" controls to display a number of different BO lists, how does that work?

Thanks, hopefully my questions are not to "n00bish"

Ryan

triplea replied on Thursday, July 17, 2008

Hi Ryan

rkelley:

1. How do you specifically hook up your business objects from your CSLA library project into your UI( in the control, controller, or work item)?

If you haven't done so already, take a look here: http://www.codeproject.com/KB/smart/SCSF_CSLA_ProjectTracker.aspx
It should answer a couple of questions regarding where to fetch the data from (usually services) and how to bind them on your UI.

rkelley:

2. What do you do about databinding?

Well not sure what you mean :-) Just follow the rules in PT on how to bind/unbind your forms properly and you should be just fine. You could write some further helper methods like UnbindBindingSource to help you complete repetitive tasks.

Finally if you are starting off just now, it might be worth taking a look at the WPF "equivalent" of CAB here: http://www.codeplex.com/CompositeWPF

Hope this helps

rkelley replied on Thursday, July 17, 2008

I guess my real question on databinding is how do you databind one object across multiple controls? Say for example an order has a billing and shipping customer as child objects, those are perfect candidates to be a common control, so would you bind them just as you would with standard windows forms?

 

Also, thanks for pointing out the WPF "equivalent" but I don't think we are totally ready for WPF as a company just yet. We are just trying to make some decisions on the direction of where our enterprise software development is headed and trying to make our lives a little better for the future in the process.

triplea replied on Thursday, July 17, 2008

Well there are different ways to go about this I guess... What I do in such cases I make these controls have an Initialise(YourBO bo) and an Unbind(bool save, bool rebind) method. So in the scenario above you would have something like:

To load the view you would call the .Initialise(Order order). Assuming you load the other views from within your presenter, after they are loaded you would call myBillingView.Initialise(order.Billing) and my shippingCustomerView(order.ShippingCustomer). That should take care of databinding.

More tricky is the unbinding of these controls when you save. On your save method on OrderView, when unbinding your datasources and applying/cancelling your edit levels, you need to do so via the Unbind(bool, bool) methods that BillingView and ShippingCustomerView provide. Its a manual task and I guess it couples your views but then again you are (or should be) working on use cases so you know what views lie within your context.

But again CAB/SCSF is quite extensible so this might not be the ideal solution. It has worked for me though :-)

Copyright (c) Marimer LLC