You are on page 1of 131

Using CSLA .

NET
Version 3.0

Rockford Lhotka

Using CSLA .NET 3.0


Revision: 3
Copyright 2007 by Rockford Lhotka
All rights reserved. No part of this work may be reproduced or transmitted in any form or by any
means, electronic or mechanical, including photocopying, recording, or by any information
storage or retrieval system, without the prior written permission of the copyright owner.
Trademarked names may appear in this book. Rather than use a trademark symbol with every
occurrence of a trademarked name, we use the names only in an editorial fashion and to the
benefit of the trademark owner, with no intention of infringement of the trademark.

Editor:

Teresa Lhotka

Technical reviewers:

Brant Estes
Joe Fallon

The information in this book is distributed on an as is basis, without warranty. Although every
precaution has been taken in the preparation of this work, the author shall not have any liability
to any person or entity with respect to any loss or damage caused or alleged to be caused directly
or indirectly by the information contained in this work.
The source code for this book (CSLA .NET 3.0.2) is available at http://www.lhotka.net/cslanet.

Acknowledgements
Neither this book, nor CSLA .NET version 3.0,
would have been possible without
out support from
Magenic. Magenic is the premier .NET
development company in the US, and is a
Microsoft Gold Certified Partner.
You can reach Magenic at
http://www.magenic.com.

CSLA .NET has attracted a community of very


thoughtful, intelligent and dedicated people.
You can find many of them at
http://forums.lhotka.net.
The bug fixes and feature enhancements
described in this book come, in no small part,
through the encouragement
ment and feedback
provided by this stellar community.
Thank you all!

I want to acknowledge Paul Stovell, who wrote


an article describing an ErrorProvider-like
like WPF
control from which I gained a lot of information
that I used to write my own controls.
I also want to thank Paul Czywczynski and
Aaron Nottestad for all their help with the
design and debugging of the WPF controls and
other WPF support.
Thanks to Christian Weyer for his help with the
WCF data portal channel, and with getting
custom authentication
cation and authorization
working with username credentials.
Thank you to Brant Estes for designing the
RuleDescription class.

About the Author


Rockford Lhotka is the author of numerous books, including Expert VB 2005 Business Objects and
Expert C# 2005 Business Objects. He is a Microsoft regional director, a Microsoft MVP, and an
INETA speaker. Rockford speaks at many conferences and user groups around the world and is a
columnist for MSDN Online. Rockford is the principal technology evangelist for Magenic, one of the
nations premiere Microsoft gold certified partners dedicated to solving todays most challenging
business problems using 100-percent Microsoft tools and technology.

Contents
Introduction...........................................................................................................................................................7
Before Reading this Book..........................................................................................................................................7
Organization of the Book..........................................................................................................................................7
Breaking Changes from CSLA .NET Version 2.1.........................................................................................................7
Known Issues with CSLA .NET Version 3.0.................................................................................................................8
Summary of Changes and Enhancements.................................................................................................................9
Technical Requirements..........................................................................................................................................10
Building CSLA .NET for .NET 2.0 .........................................................................................................................10
Building CSLA .NET for .NET 3.0 .........................................................................................................................11
Chapter 1: Windows Communication Foundation................................................................................................ 12
WCF Data Portal Channel .......................................................................................................................................12
Setting up a WCF Application Server .................................................................................................................13
Adding an Empty Web Site............................................................................................................................13
Referencing Assemblies ................................................................................................................................14
Creating the svc File ......................................................................................................................................14
Configuring WCF............................................................................................................................................14
Configuring a WCF Client Application ................................................................................................................16
Using a Custom Endpoint Name ........................................................................................................................17
Supporting DataContract and DataMember ..........................................................................................................18
When to use DataContract.................................................................................................................................19
Serialization in CSLA .NET...................................................................................................................................19
Configuring CSLA .NET to use NetDataContractSerializer .............................................................................19
Creating a WCF Service using Business Objects ......................................................................................................20
Creating the WCF Service...................................................................................................................................21
Service Contract ............................................................................................................................................21
Data Contract ................................................................................................................................................22
Service Definition File....................................................................................................................................24
Service Implementation ................................................................................................................................24
Configuring the Service .................................................................................................................................26
Consuming the WCF Service ..............................................................................................................................27
Custom Authentication...........................................................................................................................................29
Acquiring an X.509 Certificate............................................................................................................................29
Creating a Test Certificate..................................................................................................................................30
Creating the Certificate .................................................................................................................................30
Granting Access to the Certificate to IIS........................................................................................................31
Trusting the Certificate..................................................................................................................................32
Configuring WCF to use Message Security.........................................................................................................33
Configuring the Service .................................................................................................................................33
Configuring the Client....................................................................................................................................34
Configuring a Service to use Username Credentials ..........................................................................................35
Using CSLA .NET 3.0
Copyright 2007 Rockford Lhotka

Page i

Modifying PTPrincipal....................................................................................................................................37
Custom UserNamePasswordValidator ..........................................................................................................39
Custom Authorization Policy .........................................................................................................................40
Server Configuration .....................................................................................................................................42
Providing a Username from the Client...............................................................................................................43
Client Implementation ..................................................................................................................................43
Client Configuration ......................................................................................................................................44
Chapter 2: Windows Presentation Foundation .................................................................................................... 46
Custom Authentication in WPF ...............................................................................................................................47
Managing the Principal Object...........................................................................................................................47
Storing the Principal Reference.....................................................................................................................47
Using the DataPortalInitInvoke Event ...........................................................................................................48
CslaDataProvider Control........................................................................................................................................49
Basic Use ............................................................................................................................................................51
Define the Csla.Wpf Namespace...................................................................................................................51
Define Your Business Namespace .................................................................................................................51
Define CslaDataProvider as a Resource ........................................................................................................51
Using the Data Provider Resource.................................................................................................................52
Passing Parameters to the Factory Method.......................................................................................................53
Setting Parameters in XAML..........................................................................................................................53
Setting Parameters Through Data Binding....................................................................................................54
Setting Parameters in the Code Behind ........................................................................................................55
Managing the Object Lifetime............................................................................................................................58
Enabling Lifetime Management....................................................................................................................59
Declaring a Save Button ................................................................................................................................59
Declaring a Cancel Button .............................................................................................................................60
Declaring an Add New Button .......................................................................................................................61
Handling Exceptions...........................................................................................................................................62
Binding a ComboBox to a Name/Value List .......................................................................................................62
Validator Control.....................................................................................................................................................64
Using the Validator Control................................................................................................................................65
Using a Custom Style for Controls in Error.........................................................................................................66
Authorizer Control...................................................................................................................................................69
Using the Authorizer Control .............................................................................................................................70
Forcing a Manual Refesh ...............................................................................................................................70
Read Authorization........................................................................................................................................71
Write Authorization ...........................................................................................................................................72
ObjectStatus Control...............................................................................................................................................72
Using the ObjectStatus Control..........................................................................................................................73
IdentityConverter Control .......................................................................................................................................75
Using the IdentityConverter Control..................................................................................................................76
Chapter 3: Workflow ........................................................................................................................................... 77
Building Activities using Objects .............................................................................................................................79
Implementing a Code Activity............................................................................................................................80
State and Performance..................................................................................................................................81
Implementing an External Activity .....................................................................................................................83
Implementing a SequentialActivity ...............................................................................................................84
Using CSLA .NET 3.0
Copyright 2007 Rockford Lhotka

Page ii

Implementing an Activity ..............................................................................................................................85


Using External Activities in a Workflow.........................................................................................................86
Business Objects as Workflow or Activity State.................................................................................................88
Using the OptionalField Attribute .................................................................................................................89
Starting a Workflow from an Object.......................................................................................................................90
WorkflowManager Class ....................................................................................................................................91
Synchronous Execution of a Workflow .........................................................................................................92
Dealing with Idled or Suspended Workflows ................................................................................................93
Executing a Workflow from a Business Object ..................................................................................................95
Using a Command Object..............................................................................................................................96
Executing a Workflow as Part of Data Access ...............................................................................................97
Chapter 4: Validation......................................................................................................................................... 101
StringMinLength Rule ...........................................................................................................................................102
RegExMatch Rule ..................................................................................................................................................102
Friendly Property Names.......................................................................................................................................102
RuleArgs Property ............................................................................................................................................103
PropertyName Method....................................................................................................................................103
Format Mask Support...........................................................................................................................................104
Short-Circuiting Bug Fix.........................................................................................................................................104
Improved Code Generation Support .....................................................................................................................105
DecoratedRuleArgs ..........................................................................................................................................106
Creating Rule Methods................................................................................................................................107
CommonRules Parameters..........................................................................................................................108
RuleDescription.....................................................................................................................................................109
Chapter 5: Authorization ................................................................................................................................... 111
CanExecuteMethod Method .................................................................................................................................111
AllowExecute and DenyExecute Methods.............................................................................................................112
Chapter 6: Data Binding .................................................................................................................................... 113
Edit Level Mismatch Exception..............................................................................................................................113
Windows Forms Data Binding...............................................................................................................................114
Binding to an Object.........................................................................................................................................115
Unbinding from an Object................................................................................................................................115
Implementing an Apply Button........................................................................................................................117
Implementing a Save Button............................................................................................................................118
Implementing a Cancel Button.........................................................................................................................119
Implementing a Close Button...........................................................................................................................120
Disabling IEditableObject .................................................................................................................................120
BusinessListBase Bug Fixes ...................................................................................................................................121
Chapter 7: Miscellaneous................................................................................................................................... 122
SmartDate.............................................................................................................................................................122
SortedBindingList ..................................................................................................................................................122
Using CSLA .NET 3.0
Copyright 2007 Rockford Lhotka

Page iii

Obtaining Reference to Original List................................................................................................................122


CompareTo Method Bug Fix ............................................................................................................................123
FilteredBindingList ................................................................................................................................................123
NameValueListBase ..............................................................................................................................................123
Data Portal............................................................................................................................................................123
ReleaseProxy Method ......................................................................................................................................124
Automatic Cloning on Update..........................................................................................................................124
Index.................................................................................................................................................................. 126

Using CSLA .NET 3.0


Copyright 2007 Rockford Lhotka

Page iv

List of Tables
Table 1. Known issues with version 3.0
Table 2. Functional enhancements in version 3.0
Table 3. Properties defined in a svc file.
Table 4. Makecert.exe arguments.
Table 5. Certmgr.exe arguments.
Table 6. Properties of the serviceCertificate element.
Table 7. CslaDataSource control properties
Table 8. Options for the NotVisibleMode property.
Table 9. Status properties from BusinessBase<T>.
Table 10. Versioning issues when reloading a workflow.
Table 11. Methods provided by WorkflowManager.
Table 12. WorkflowManager Status property values.
Table 13. Summary of new validation features and enhancements.
Table 14. NullResultOptions values.
Table 15. Parameters for the rule methods in CommonRules.
Table 16. Properties of the RuleDescription object.
Table 17. Conditions when UndoException is thrown.

8
9
24
31
32
34
50
71
73
89
92
93
101
102
109
110
113

List of Figures
Figure 1. Setting the NET20 compiler symbol.
Figure 2. Adding a WCF Service web site.
Figure 3. Browser display when navigating to a WCF data portal host.
Figure 4. WCF services as an interface to your business objects.
Figure 5. Add Service Reference dialog.
Figure 6. Installing the certificate into the Other People store.
Figure 7. Authentication and authorization during WCF initialization.
Figure 8. Default display of a Windows Forms ErrorProvider control.
Figure 9. Default display of a CSLA .NET Validator control in WPF.
Figure 10. Validation error with custom styling.
Figure 11. Validation error style similar to Windows Forms ErrorProvider.
Figure 12. ObjectStatus data bound to CheckBox controls.
Figure 13. High level architecture using objects and workflows.
Figure 14. A workflow activity as a UI relative to the business layer.
Figure 15. Sequential workflow to close a project.
Figure 16. Revised workflow to close a project.
Figure 17. Using a code activity within a SequentialActivity.
Using CSLA .NET 3.0
Copyright 2007 Rockford Lhotka

10
14
16
20
28
33
36
64
65
68
69
74
78
79
80
82
84
Page v

Figure 18. Visual Studio activity designer showing a simple Activity.


Figure 19. Workflow using external activities.
Figure 20. Properties for the getProject1 activity.
Figure 21. Binding getProject1.ProjectId to the workflows ProjectId.
Figure 22. Binding closeProject1.Project to getProject1.Project.
Figure 23. ProjectWorkflow that doesnt save the Project object.
Figure 24. Windows Forms data binding and the BindingSource.

Using CSLA .NET 3.0


Copyright 2007 Rockford Lhotka

85
86
87
87
88
97
114

Page vi

Introduction
Welcome to Using CSLA .NET 3.0. This book will show you how to use the new features,
enhancements and changes made to the CSLA .NET framework from version 2.1 to 3.0.

Before Reading this Book


CSLA .NET version 3.0 is an upgrade from version 2.1.4, with support for Microsoft .NET 3.0. In
addition to support for WPF, WCF and WF, CSLA .NET 3.0 adds some enhancements to other parts
of the framework.
CSLA .NET version 2.0 is part of the Expert C# 2005 Business Objects and Expert VB 2005
Business Objects books, published by Apress (http://www.apress.com). These books are ISBN
1590596323 and ISBN 1590596315 respectively.
CSLA .NET version 2.1 is described in the CSLA .NET Version 2.1 Handbook, available from
http://store.lhotka.net.
This book assumes that you have read and are familiar with the content from the Expert C# 2005
Business Objects or Expert VB 2005 Business Objects book and the CSLA .NET Version 2.1
Handbook.
Note: CSLA stands for Component-based, Scalable, Logical Architecture
The CSLA .NET framework is licensed according to the license at
http://www.lhotka.net/cslanet/license.aspx.

Organization of the Book


The enhancements made in version 3.0 are largely focused on support for WPF and WCF. However,
there are also a number of fairly wide-ranging enhancements to the pre-existing 2.1 functionality
which affect many parts of the framework itself, and enable a number of new capabilities for
developers building applications based on business objects.
This book is organized into chapters based on technology: WCF, WPF and WF. Following these
chapters there are chapters covering the other changes that are not specific to Microsoft .NET 3.0.

Breaking Changes from CSLA .NET Version 2.1


CSLA .NET 3.0 has no major breaking changes from version 2.1. Any time bugs are fixed or new
features are added, there is the possibility of that change breaking existing code. You should test your
application code thoroughly upon upgrade.
One important change that may cause some short-term issues, is the addition of checks to ensure
that the edit levels of all objects in an object graph remain in sync during n-level undo. If object edit
levels get out of sync, an exception will be thrown to indicate the problem.
If this happens, it means that the code calling the business objects is not correctly using n-level
undo. In short, it means that youve had a bug all along, and this change is now highlighting the bug
so you can fix it.
Using CSLA .NET 3.0
Copyright 2007 Rockford Lhotka

Page 7

Known Issues with CSLA .NET Version 3.0


There are known issues where version 3.0 does not function as expected. Table 1 identifies the
known issues.

Class
CslaDataSource

Summary
You cannot add a CslaDataSource to a page by choosing to add a
new data source from within the GridView or DetailsView controls.
Attempting to do this will result in an exception that prevents the
control from displaying properly. I have been unable to resolve this
issue, but there is a viable workaround.
You must manually add a CslaDataSource control to your page, either
using drag-and-drop from the Toolbox, or by typing the tag into the
page. At this point you can configure the assembly/type information
in the data source control. You can then choose this data source
control as the data source for your GridView or DetailsView control.

ReadOnlyListBase

There is a known issue where the Visual Studio debugger will lock
up (actually it will eventually crash due to a stack overflow) when
an exception is thrown in the DataPortal_Fetch() of a subclass of
ReadOnlyListBase.
The issue appears to be due to a bug in the Visual Studio debuggers
exception assistant.
The workaround is to click Tools|Options, then
Debugging|General and uncheck the Unwind the call
stack on unhandled exceptions box (You may need to
check the Show all settings box at the bottom of the dialog to
see this option).

Table 1. Known issues with version 3.0


These issues can be overcome by following the workarounds listed in Table 1. Where the issue
appears to be due to a problem with Visual Studio or .NET, I have reported the problem to Microsoft.

Using CSLA .NET 3.0


Copyright 2007 Rockford Lhotka

Page 8

Summary of Changes and Enhancements


While CSLA .NET version 3.0 is an evolutionary update from version 2.1.4, it does include some
substantial changes, which involve parts of the CSLA .NET framework code, and enable some
powerful capabilities for your business development efforts.
At a high level, the changes can be grouped into a set of functional enhancements as listed in
Table 2.

Enhancement
WCF

Summary
Add support for WCF as a data portal channel that is
interchangeable with the previous channels.
Provide global support for use of the DataContract and
DataMember attributes in CSLA .NET business objects.

WPF

Add a set of WPF controls to make it easier to build a


powerful WPF UI using a CSLA .NET business layer,
including CslaDataProvider, Authorizer, Validator ,
ObjectStatus and IdentityConverter

Validation

Enhancements to make the creation and use of rule methods


easier overall, and to simplify some code generation
scenarios.

Authorization

Add CanExecuteMethod() functionality, that works like


CanReadProperty() or CanWriteProperty(), but for methods.

Data binding

Throw an exception when n-level undo gets out of sync


between parent and child objects, making it easier to find and
fix data binding errors, especially in Windows Forms.
Enhance and fix BusinessListBase in several ways to address
data binding inconsistencies.

Interfaces

Provide a higher level of consistency to some of the low-level


interfaces provided by CSLA .NET, making it easier to
implement UI frameworks.

Miscellaneous

Several minor enhancements and bug fixes to various preexisting features.

Table 2. Functional enhancements in version 3.0


The rest of this book will address the changes in each of these functional areas.

Using CSLA .NET 3.0


Copyright 2007 Rockford Lhotka

Page 9

Technical Requirements
CSLA .NET 3.0 is designed to run on either Microsoft .NET 2.0 or Microsoft .NET 3.0. Obviously,
if you are using only the Microsoft .NET 2.0 runtime, then you cannot use any features specific to
Microsoft .NET 3.0, but the bulk of CSLA .NET 3.0s features remain at your disposal.
Note: In the process of writing this book some changes were made to the framework.
The exact version of the framework that corresponds to this book is version 3.0.2,
which you can download from
http://www.lhotka.net/cslanet/download.aspx.

Building CSLA .NET for .NET 2.0


The Csla solution in the download can be opened in Visual Studio 2005. If you do not have the
Microsoft .NET 3.0 runtime installed youll need to define a NET20 compiler symbol before building.
This symbol instructs the compiler to ignore all code specific to Microsoft .NET 3.0.
To set the NET20 compiler symbol take the following steps before building the solution:
1. Open the solution in Visual Studio 2005
2. Open the projects Properties designer
3. Select the Build tab as shown in Figure 1.
4. Type NET20 into the Conditional compilation symbols text box

Figure 1. Setting the NET20 compiler symbol.


At this point you can close the Properties designer and build the solution. No features of
Microsoft .NET 3.0 will be compiled into the project.
Note: You will see some compiler warnings when building the project, because the
project still references some Microsoft .NET 3.0 assemblies.
The project does reference the following Microsoft .NET 3.0 assemblies:

PresentationCore

PresentationFramework

Using CSLA .NET 3.0


Copyright 2007 Rockford Lhotka

Page 10

System.Printing

System.ServiceModel

System.Windows.Forms

System.Workflow.Runtime

WindowsBase

You can safely remove these references from the project, thus eliminating the compiler warnings
youll otherwise receive (to notify you that these assemblies could not be located).

Building CSLA .NET for .NET 3.0


The Csla solution in the download is a Visual Studio 2005 solution. If you have the Microsoft .NET
3.0 runtime installed on your development machine (and on your deployment machines), you can
safely build CSLA .NET 3.0 directly from Visual Studio 2005. No extra steps are required to build
the solution for Microsoft .NET 3.0.
You can also choose to open the Csla solution in Visual Studio 2008 (Orcas) Beta 2 or higher.
Visual Studio will run an upgrade wizard to bring the solution and project files up to the new version
and you can then build the solution. Again, no extra steps are required to build the solution in Visual
Studio 2008.

Using CSLA .NET 3.0


Copyright 2007 Rockford Lhotka

Page 11

Chapter 1:
Windows Communication Foundation
One of the pillars of Microsoft .NET 3.0 is the Windows Communication Foundation (WCF). WCF
is essentially the replacement for web services, Web Service Extensions (WSE) 3.0, .NET Remoting,
Enterprise Services and Microsoft Message Queue (MSMQ). It provides a common API for creating
service-oriented, client/server, n-tier or message-based applications.
In this chapter I assume you have a basic familiarity with WCF, including how to create and
consume WCF services in your applications.
WCF impacts CSLA .NET in three ways:
1. WCF is a new way for the data portal to communicate between client and server.
2. WCF includes a replacement for the Serializable attribute: DataContract, and this
directly impacts the way CSLA .NET can serialize objects.
3. WCF allows you to create a WCF service, similar to a Web service, that is implemented
using business objects behind the scenes.
If you have pre-existing CSLA .NET applications, you can use the new WCF data portal channel
by making some configuration changes to your application. No coding changes are required.
You may also choose to start using the new DataContract and DataMember attributes instead of
the Serializable attribute in your business classes.
WCF is also the next generation technology for creating web services, replacing the older asmx
and WSE (Web service extensions) technologies. You can create WCF services that accept and
return standard XML messages, but which use CSLA .NET style business objects to implement the
business logic within the service itself.
Lets look at each of these concepts.

WCF Data Portal Channel


The data portal now includes a channel allowing the use of WCF in addition to .NET Remoting, Web
services or Enterprise Services. Those other channels still exist and work, the WCF channel is just
another option at your disposal.
Note: Since Microsoft is putting WCF forward as the future of distributed
communications, I recommend that the WCF data portal channel be used in favor of
the older channels when possible.
The WCF channel is compatible with the other channels. This means that you can transparently
switch from one of the older channels to the WCF channel without having to change any of your UI,
business object or data access code.
There are substantial benefits to using the WCF data portal channel, because you can tap into the
power and flexibility of WCF itself. WCF allows you to configure your network transport to use
different protocols, along with optional capabilities like encryption and various types of
authentication.
Using CSLA .NET 3.0
Copyright 2007 Rockford Lhotka

Page 12

The details of exploiting all the power of WCF is outside the scope of this book. However, it is
important to realize that the WCF data portal channel is specifically designed to allow you to use
most WCF features by setting up the appropriate configuration on both the client and server.

Setting up a WCF Application Server


WCF can be hosted in IIS, using ASP.NET, on the following platforms:
Windows XP SP2 or higher
Windows Server 2003
Windows Vista
Windows Server 2008
WCF can also be hosted using the Windows Activation Service (WAS), on the following
platforms:
Windows Server 2008
Since Windows Server 2008 is not yet released, Ill focus on how to host a WCF data portal
service in IIS, using ASP.NET.
WCF can use many different network protocols to communicate between client and server,
including HTTP, TCP, named pipes and others. The only constraint the data portal imposes on your
choice of protocol is that the protocol must be synchronous.
WCF does support some asynchronous protocols, and they will not work with the data portal,
because the data portal is designed for synchronous communication.
To set up an application server for your application, follow these steps:
1. Create an empty web project in Visual Studio (I use a directory-based web site, but you
can use the project model if you wish).
2. Add a reference to your business assembly or assemblies.
3. Ensure Csla.dll is in the Bin directory.
4. Add a WcfPortal.svc file.
5. Add a web.config file .
6. Add a <system.servicemodel> element to expose the data portal.
Lets walk through these steps, using the WcfHost project from ProjectTracker as an example.

Adding an Empty Web Site


Figure 2 shows the Visual Studio dialog used to add a new empty web site to a solution. You can
choose various options on this dialog, including whether to add the site to the file system, or to IIS
directly. Choose options that fit into your environment and click OK.

Using CSLA .NET 3.0


Copyright 2007 Rockford Lhotka

Page 13

Figure 2. Adding a WCF Service web site.


The result is an empty web site in your solution.

Referencing Assemblies
The simplest way to get Csla.dll , along with your business assembly, into the Bin directory of the
web site is to add a reference to your business assembly (or project if it is in the same solution).
Alternately, you can manually copy Csla.dll and your business assembly into the Bin directory of
the site on the web server.

Creating the svc File


Next, you need to add a file called WcfPortal.svc, with the following content:
<% @ServiceHost Service="Csla.Server.Hosts.WcfPortal" %>

The service name as specified in this file must correspond to the name of the service you define in
your web.config file. When a client request comes into your service, this name is used to locate the
configuration for the service in web.config.

Configuring WCF
Finally, open web.config in the editor. You need to add the following configuration section to the
file:

Using CSLA .NET 3.0


Copyright 2007 Rockford Lhotka

Page 14

<?xml version="1.0"?>
<configuration xmlns="http://schemas.microsoft.com/.NetConfiguration/v2.0">
<system.serviceModel>
<services>
<service name="Csla.Server.Hosts.WcfPortal">
<endpoint contract="Csla.Server.Hosts.IWcfPortal" binding="wsHttpBinding"/>
</service>
</services>
</system.serviceModel>

This <system.serviceModel> element configures the WCF service that corresponds to the svc file
you just created. Notice that the name property exactly matches the Service name from the svc file.
WCF services are configured based on their address, binding and contract.
In this case, the address is mostly defined by IIS. This is because the address flows from your web
server name, the virtual root name and the name of the svc file (WcfHost). The result is the following
address:
http://serverName/virtualRoot/WcfHost.svc

The http:// part of the address actually corresponds to the binding. The binding specifies how
the client is allowed to communicate with the server. The most common binding is wsHttpBinding,
which uses the standard HTTP protocol. When hosting within IIS, you must use an HTTP binding.
Note: If you create your own host in a Windows service, or use WAS, you can use
other bindings that use protocols such as TCP, named pipes or others.
Finally, theres the contract, which is specified using the contract property. The contract is
defined by CSLA .NET, and must be Csla.Server.Hosts.IWcfPortal.
Notice that all configuration options for the address, binding and channel are controlled through
the standard <system.serviceModel> element used by all WCF services. This means that you have
complete flexibility in defining how a client is allowed to communicate with your data portal service
(as long as the binding is synchronous).
In most cases youll also need to include the standard CSLA .NET
setting:

appSettings authentication

<appSettings>
<add key="CslaAuthentication" value="Csla"/>
</appSettings>

Youll probably also need to set your connectionStrings, so your business objects can interact
with the database while running on your application server.
At this point, your WCF application server should be ready to use.
You can do a basic functionality test by navigating to your services address using a browser such
as Internet Explorer. You should see a result similar to Figure 3.

Using CSLA .NET 3.0


Copyright 2007 Rockford Lhotka

Page 15

Figure 3. Browser display when navigating to a WCF data portal host.


This test isnt conclusive, but you should see a web page informing you that metadata publishing
for this service is currently disabled. That message indicates that your basic configuration is probably
correct.

Configuring a WCF Client Application


Configuring a client application to use the WCF data portal channel is a two step process. You must
configure CSLA .NET to use the WCF data portal channel, and then you need to configure WCF to
specify the address, binding and contract of the application server.
Configuring CSLA .NET to use the WCF data portal channel is no different than configuring for
any other remote data portal channel: you set the CslaDataPortalProxy setting in the appSettings
element of the clients app.config or web.config file:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<configSections>
</configSections>
<appSettings>
<add key="CslaAuthentication" value="Csla" />
<add key="CslaDataPortalProxy"
value="Csla.DataPortalClient.WcfProxy, Csla"/>
</appSettings>

This specifies that the data portal should use a WcfProxy object to communicate with the server.
The WcfProxy object relies on the configuration of WCF options to find the server, so you also need
to add a standard <system.serviceModel> element to your clients config file:

Using CSLA .NET 3.0


Copyright 2007 Rockford Lhotka

Page 16

<system.serviceModel>
<client>
<endpoint name="WcfDataPortal"
address="http://localhost:2961/WcfHost/WcfPortal.svc"
binding="wsHttpBinding"
contract="Csla.Server.Hosts.IWcfPortal" />
</client>
</system.serviceModel>

The name property is used by WcfProxy to locate the correct endpoint in the configuration, and this
value should usually not be changed. If you need to change it, Ill discuss using a custom endpoint
name later in this chapter.
The only value that you will normally need to change from this example is the address property.
The address value must correspond to the actual address of the service on your application server.
The binding property must correspond to the binding you configured on your application server.
In most cases wsHttpBinding is a good option. However, any synchronous binding can be used. The
important thing is that the binding on the client and server must match.
The contract property cannot be changed. The contract name is specified by WcfProxy in CSLA
.NET itself. Changing this value will prevent the data portal channel from functioning, and youll get
an exception at runtime.

Using a Custom Endpoint Name


In the WCF configuration on the client, the name property of the endpoint is used by WcfProxy to
locate the correct endpoint in the configuration. Normally, you will just use the default value shown
in the earlier example. However, in some cases you may want or need to provide a different name for
your endpoint.
The important thing to remember is that the endpoint name in your configuration file and the
name of the endpoint expected by WcfProxy must match. WcfProxy exposes a protected property
named EndPoint, which you can use to change the endpoint name. Because this is a protected
property, changing the endpoint name means that you must create a subclass of WcfProxy in your
application. For example:
public class CustomWcfProxy : WcfProxy
{
public CustomWcfProxy()
{
EndPoint = "CustomEndpointName";
}
}

You must then use this proxy class instead of the standard WcfProxy class in your configuration
of the data portal:
<add key="CslaDataPortalProxy"
value="MyApplication.CustomWcfProxy, MyApplication"/>

The data portal will then use your custom subclass of WcfProxy. This changes the endpoint name,
allowing you to change the endpoint name in your <system.serviceModel> element:

Using CSLA .NET 3.0


Copyright 2007 Rockford Lhotka

Page 17

<system.serviceModel>
<client>
<endpoint name="CustomEndpointName"
address="http://localhost:2961/WcfHost/WcfPortal.svc"
binding="wsHttpBinding"
contract="Csla.Server.Hosts.IWcfPortal" />
</client>
</system.serviceModel>

This is an advanced scenario and is not the sort of thing most people will need to worry about.
At this point, you should understand how to use the WCF data portal channel by configuring an
application server with WCF, and setting the appropriate values in the client applications
app.config or web.config file.

Supporting DataContract and DataMember


WCF includes two new serialization engines: DataContractSerializer and
NetDataContractSerializer. Both of these engines support two new attributes: DataContract and
DataMember.
can be thought of as the replacement for the older XmlSerializer. This
serializer is primarily designed to support service-oriented scenarios by creating and consuming
SOAP-compliant XML blobs. Not all types or object graphs can be serialized using this component.
DataContractSerializer

NetDataContractSerializer (NDCS) can be thought of as the replacement for the older


BinaryFormatter. This serializer is primarily designed to support client/server or n-tier scenarios

by
providing full fidelity for even complex .NET data types. Any Serializable or DataContract type
(or object graph of Serializable or DataContract types) can be serialized using this component.
Ill focus on the use of the NDCS in the rest of this section, because CSLA .NET uses the
only NDCS provides comparable functionality in WCF.

BinaryFormatter and

The DataContract attribute is somewhat like the Serializable attribute, in that it marks an object
as being eligible for serialization by one of the new serializers.
The Serializable attribute uses an opt-out model, where all fields are serialized unless you
explicitly mark the field with the NonSerialized attribute. DataContract, on the other hand, uses an
opt-in model; where fields (or properties) are only serialized if you explicitly mark them with the
DataMember attribute.
The BinaryFormatter, of course, only understands the Serializable attribute. If you only mark a
class with DataContract, the BinaryFormatter will throw an exception because it views the object as
not being serializable.
The NDCS understands both the DataContract and Serializable attributes. This means that you
can use NDCS to serialize objects that are Serializable, objects that are a DataContract and even
object graphs composed of both types of objects at once.
This makes sense, because all the core .NET types to date are Serializable, not DataContract.
They cant be changed without massive repercussions throughout the existing .NET framework itself.
Any serializer intended to replace the BinaryFormatter must do what the BinaryFormatter does, and
then do more.

Using CSLA .NET 3.0


Copyright 2007 Rockford Lhotka

Page 18

When to use DataContract


Given that DataContract and Serializable provide the same functionality, though with an opt-in or
opt-out philosophy, the obvious question is when to use each option. Or to be blunt: when should
anyone use DataContract?
In general, I dont recommend switching from Serializable to DataContract. For most business
objects, the desired behavior is to include all fields when an object is serialized, and so the opt-out
model used by Serializable is better.
If you use the opt-in model of DataContract, it is too easy to forget to put the DataMember
attribute on a field, in which case that field is ignored when the object is serialized. The resulting bug
can be difficult to discover and debug.
You should also be aware, that Windows Workflow Foundation (WF) uses the BinaryFormatter
to serialize objects when a workflow is suspended. If you intend to use your business objects within a
workflow, you should avoid using DataContract to ensure that your objects can be serialized if
necessary.
The DataContract and DataMember attributes are designed to support service-oriented design.
Later in this chapter, Ill show how you can build service-oriented WCF services. That is the
appropriate place for using these attributes.

Serialization in CSLA .NET


CSLA .NET directly uses the BinaryFormatter in only a few places: when cloning an object graph,
in the n-level undo implementation and in some of the data portal channels.
If you are using DataContract, you should be using the WCF data portal channel, and so I have
made no effort to get the older data portal channels to support the DataContract attribute.
Note: If you are using the DataContract and DataMember attributes in your business
classes you can only use the local or WCF data portal channels. Remoting, web
services and Enterprise Services will all throw serialization exceptions if you attempt
to use them.
However, I did update CSLA .NET to support the use of DataContract when cloning an object
graph and when using n-level undo. To do this, CSLA .NET must use the NDCS instead of the
BinaryFormatter to do any explicit serialization.

Configuring CSLA .NET to use NetDataContractSerializer


You must configure CSLA .NET to use NDCS rather than the BinaryFormatter. For reasons of
backward compatibility, the default is to use the BinaryFormatter.
Note: I recommend that you only configure CSLA .NET to use the NDCS if you use
the DataContract attribute instead of the Serializable attribute in your business
classes.
To configure CSLA .NET to use the NDCS you must add an element to the appSettings of your
app.config or web.config file:

Using CSLA .NET 3.0


Copyright 2007 Rockford Lhotka

Page 19

<?xml version="1.0" encoding="utf-8" ?>


<configuration>
<appSettings>
<add key="CslaAuthentication" value="Csla" />
<add key="CslaSerializationFormatter" value="NetDataContractSerializer"/>

This will cause both the clone and n-level undo implementations in CSLA .NET to use NDCS.
The result is that you can use the DataContract attribute instead of, or in combination with, the
Serializable attribute in your business classes.
Again, please remember that only the WCF data portal channel uses the NDCS, so if you use the
your business objects you cannot use any of the older data portal channels.

DataContract attribute in

Creating a WCF Service using Business Objects


WCF is, at its core, the next generation of Web services technology. To this end, WCF includes
many features designed to help build and consume XML services. By using XML and widely
accepted standards, these services provide a high degree of interoperability between applications,
languages and platforms.
In Expert C# 2005 Business Objects, Chapter 11 covers the creation of Web services. Specifically,
using business objects to implement the service. From an architecture and design perspective, WCF
services are no different from Web services. The key point is illustrated in Figure 4, which is that the
WCF service is just another type of interface to your business objects.

Figure 4. WCF services as an interface to your business objects.


A WCF service interface is for use by other applications, while a WPF, Windows Forms or Web
Forms interface is for use by end users. Either way, the interface relies entirely on the business
objects in the business layer to encapsulate all business logic.
In this section, I will walk through the process of creating a WCF service using business objects,
but I recommend reading Chapter 11 from Expert C# 2005 Business Objects to better understand the
theory and philosophy employed here.

Using CSLA .NET 3.0


Copyright 2007 Rockford Lhotka

Page 20

Creating the WCF Service


A WCF service is very similar to an asmx Web service. The service is hosted on a server, it has a svc
file (similar to an asmx file) and it has code behind the svc file that implements the service. However,
WCF services are far more powerful and flexible than their older asmx counterparts.
A complete discussion of WCF and all its options is outside the scope of this book, but it is
important that you understand the concepts of hosting, addresses, bindings and contracts.
WCF can be hosted in IIS, using ASP.NET, on the following platforms:
Windows XP SP2 or higher
Windows Server 2003
Windows Vista
Windows Server 2008
WCF can also be hosted using the Windows Activation Service (WAS), on the following
platforms:
Windows Server 2008
Since Windows Server 2008 is not yet released, Ill focus on how to host a WCF service in IIS,
using ASP.NET.
A WCF service is defined by its address, binding and contract.
The address is used by a client to locate and call the service. The address is typically a URL, and
is defined by the binding (protocol), host name, virtual root name and the name of the svc file. For
example:
http://myserver/myroot/myservice.svc

The binding specifies how the client communicates with the service. The binding defines the
protocol (such as HTTP, TCP sockets, named pipes and so forth). It also defines whether the data
should be encrypted during transfer and many other possible settings.
The contract is a two part concept. Theres the service contract that defines the methods exposed
by the service to its clients. There are also data contracts that define the shape of any data passed
into or out of those methods.

Service Contract
The first thing to do when creating a WCF service, is to create the service contract. A service
contract is just a normal .NET interface with some extra attributes on the interface and the methods.
The App_Code directory contains IPTService.cs:
[ServiceContract]
public interface IPTService
{
[OperationContract]
ProjectData[] GetProjectList();
}

The ServiceContract attribute specifies that this interface defines a service contract for a WCF
service.
Using CSLA .NET 3.0
Copyright 2007 Rockford Lhotka

Page 21

The OperationContract attribute specifies that the method is part of the public service interface.
It is important to realize that you can choose which methods are part of the service contract, and
which are just part of the .NET interface by applying OperationContract to some methods and not
others. You can use this technique to define service implementations that provide more functionality
to other code in the project than is provided to clients of the service.
Note: If you dont want to put your code in the App_Code directory of your service,
you can create a separate Class Library project to contain your service contract, data
contract and service implementation.
In this example, the GetProjectList() method is part of the service contract, and so the method can
be invoked by clients of the service.

Data Contract
Notice that the GetProjectList() method returns an array of type ProjectData. Any data passed
into or returned from a service method should be defined by a data contract.
A data contract is just a simple .NET class with public read-write properties. The DataContract
and DataMember attributes are used to define the shape of the public contract for use by WCF.
ProjectData is defined in the App_Code directory:
[DataContract]
public class ProjectData
{
private Guid _id;
private string _name;
private string _started;
private string _ended;
private string _description;
private List<ProjectResourceData>
_resources = new List<ProjectResourceData>();
[DataMember]
public Guid Id
{
get { return _id; }
set { _id = value; }
}
[DataMember]
public string Name
{
get { return _name; }
set { _name = value; }
}
[DataMember]
public string Started
{
get { return _started; }
set { _started = value; }
}
[DataMember]
public string Ended
{
get { return _ended; }
set { _ended = value; }
}
[DataMember]
Using CSLA .NET 3.0
Copyright 2007 Rockford Lhotka

Page 22

public string Description


{
get { return _description; }
set { _description = value; }
}
public void AddResource(ProjectResourceData resource)
{
_resources.Add(resource);
}
[DataMember]
public ProjectResourceData[] ProjectResources
{
get
{
if (_resources.Count > 0)
return _resources.ToArray();
return null;
}
set
{
_resources = new List<ProjectResourceData>(value);
}
}
}

The DataContract attribute specifies that this class defines a data contract.
The DataMember attribute is applied to a property or field, and specifies that the property or field is
part of the contract. Youll typically apply DataMember to properties rather than fields, because the
serialized XML stream includes the name of the element, and property names (such as Name) are
typically more appropriate than field names (such as _name).
Notice that the AddResource() method doesnt have a DataMember attribute. The DataMember
attribute is only valid for properties and fields, and cant be applied to methods. Any methods
included in a data contract class are only available to code in the service, not to code in the client of
the service.
The ProjectResourceData class is another data contract, also in the App_Code directory. I wont
list it here, because it is just a simple class, also using the DataContract and DataMember attributes.
The ProjectData class does provide an interesting implementation of the ProjectResources
property:
[DataMember]
public ProjectResourceData[] ProjectResources
{
get
{
if (_resources.Count > 0)
return _resources.ToArray();
return null;
}
set
{
_resources = new List<ProjectResourceData>(value);
}
}

I chose to maintain the list of ProjectResourceData child objects in a


ease of use. However, the public interface defined by the
DataMember attribute only includes this list as an array.
List<ProjectResourceData> for

Using CSLA .NET 3.0


Copyright 2007 Rockford Lhotka

Page 23

This illustrates how you can use encapsulation to provide a friendly internal implementation,
while offering a simpler and more standard external interface.
Combined, the service contract and data contracts define how clients will see and interact with
your service.

Service Definition File


The PTWcfService project in ProjectTracker is a simple Visual Studio web site project. In the root
of this web site is PTService.svc, which defines a service endpoint, and effectively defines the
address of the service.
The svc file defines the address of the service, because the rest of the address is pre-defined by
IIS:
http://myserver/myroot/PTService.svc

Hosting in IIS means that http:// is the only possible protocol, and the web server defines the
myserver name. The myroot name is defined by the name of the virtual root on the server, so only the
name PTService.svc is under the control of the service itself.
The svc file contains just one line to define the service:
<%@ ServiceHost Language=C# Debug="true"
Service="PTService" CodeBehind="~/App_Code/PTService.cs" %>

Table 3 lists the properties and their purpose.

Property
Language

Description
Defines the programming language used in the code behind file that
implements the service.

Debug

Indicates whether debugging is enabled for this service.

Service

Defines the name of the class that implements the service.

CodeBehind

Specifies the path to the file containing the service implementation.

Table 3. Properties defined in a svc file.


The svc file is primarily a pointer to the code behind that contains the service implementation.

Service Implementation
The service implementation is in PTService.cs in the App_Code directory. The implementation class
is a normal .NET class that implements the interface defined as the service contract:
public class PTService : IPTService
{
// ...
}

Using CSLA .NET 3.0


Copyright 2007 Rockford Lhotka

Page 24

By implementing the IPTService interface, the class is required to implement the methods
defined by the interface. You can use implicit or explicit implementations for these methods. WCF
will invoke the methods through the interface.
These methods are implemented almost exactly the same as the methods youd implement for an
service. Heres the GetProjectList() method:

asmx Web

public ProjectData[] GetProjectList()


{
// anonymous access allowed
Security.UseAnonymous();
try
{
ProjectList list = ProjectList.GetProjectList();
List<ProjectData> result = new List<ProjectData>();
foreach (ProjectInfo item in list)
{
ProjectData info = new ProjectData();
Csla.Data.DataMapper.Map(item, info);
result.Add(info);
}
return result.ToArray();
}
catch (Csla.DataPortalException ex)
{
throw ex.BusinessException;
}
catch (Exception ex)
{
throw new Exception(ex.Message);
}
}

The highlighted lines of code show the implementation of the services behavior. Notice how this
code essentially acts as a bridge between the data contract objects exposed to the client of the service,
and the business objects used within the service.
I recommend that you never directly expose your business objects as the data contract, as that
would break the encapsulation provided by the service. Your business objects are your internal
implementation and should not be exposed to outside callers.
Similarly, I dont recommend having your business objects make direct use of the data contract
objects. The data contract objects are part of your services public contract, and they will be changed
based on how you version your service, not on how you might change or evolve your business
objects themselves.
The service implementation provides a clear buffer, an abstraction layer, between the external
service contract and the internal implementation.
Notice that the GetProjectList() method doesnt require any user credentials. Because all users
are allowed to view the list of projects, the current principal is simply set to an unauthenticated
custom principal in the UseAnonymous() method:
public static void UseAnonymous()
{
ProjectTracker.Library.Security.PTPrincipal.Logout();
}

If your method needs an authenticated principal object, youll need to use one of the many
techniques provided by WCF to get the users credentials from the client to your service. You can
Using CSLA .NET 3.0
Copyright 2007 Rockford Lhotka

Page 25

then use those credentials to set the current principal appropriately. This is not easy, and is discussed
later in this chapter in the section on custom authentication.

Configuring the Service


The final step when building a WCF service is to configure WCF. This is most commonly done
through the web.config file, in the <system.serviceModel> element:
<system.s erviceModel>
<services>
<service behaviorConfiguration="PTServiceBehavior" name="PTService">
<endpoint address="" binding="wsHttpBinding" contract="IPTService" />
<endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange" />
<host>
<baseAddresses>
<add baseAddress="http://localhost:8080/PTService/" />
</baseAddresses>
</host>
</service>
</services>
<behaviors>
<serviceBehaviors>
<behavior name="PTServiceBehavior">
<serviceMetadata httpGetEnabled="true" />
<serviceDebug includeExceptionDetailInFaults="true" />
</behavior>
</serviceBehaviors>
</behaviors>
</system.serviceModel>

Theres a lot going on here, but the two highlighted lines of code are the most important. The first
defines the service itself, and the second defines the HTTP endpoint used by clients when calling the
service.
The configuration also includes a second <endpoint> element:
<endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange" />

This endpoint can be used by Visual Studio or other tools to access your services metadata.
During development of a client application, youll want to expose this endpoint so the client
developer has access to the metadata for your service.
The use of the term mex refers to metadata exchange, which is the WSDL (web service definition
language) standard used to exchange metadata about a service.
Also, during development you may want access to more detailed server-side exception details.
This is the purpose of the behaviorConfiguration property of the <service> element:
<service behaviorConfiguration="PTServiceBehavior" name="PTService">

The behavior itself is defined in the <behaviors> element:


<behaviors>
<serviceBehaviors>
<behavior name="PTServiceBehavior">
<serviceMetadata httpGetEnabled="true" />
<serviceDebug includeExceptionDetailInFaults="true" />
</behavior>
</serviceBehaviors>
</behaviors>

Using CSLA .NET 3.0


Copyright 2007 Rockford Lhotka

Page 26

The serviceMetadata tag indicates that clients can retrieve metadata from the service through an
HTTP GET.
The serviceDebug tag indicates that service faults returned to the client should include exception
details from the server. In production, doing this can be a major security issue, because you could
expose sensitive implementation details to unknown clients. But during development, this is a
powerful tool for debugging.
In production, you may choose to use a simpler configuration, such as:
<system.serviceModel>
<services>
<service name="PTService">
<endpoint address="" binding="wsHttpBinding" contract="IPTService" />
</service>
</services>
</system.serviceModel>

This simpler configuration is all that is technically needed to make your service available to
clients; assuming the clients dont need to access your services metadata to determine your service
contract, and assuming you dont want to return detailed server-side exception information to your
callers.
The WCF service should now be complete. It has an address, binding and contract. Behind the svc
file is an implementation that implements the service contract, consuming and returning data defined
by the data contract.

Consuming the WCF Service


Consuming a WCF service through Visual Studio is very similar to consuming an asmx Web service.
In your client application, you simply add a service reference. This allows Visual Studio to retrieve
the metadata from the service so it can create a set of proxy objects on your behalf.
Figure 5 shows the Add Service Reference dialog, having discovered the services
available in the ProjectTracker solution. The IPTService contract has been selected so the services
operations are listed.

Using CSLA .NET 3.0


Copyright 2007 Rockford Lhotka

Page 27

Figure 5. Add Service Reference dialog.


Once the service reference has been added to your project, you can use the proxy objects to
interact with the service. In the PTWcfClient application, for example, standard Windows Forms data
binding is used to display the data on a form.The following code is used to call the service when the
form loads:
private void Form1_Load(object sender, EventArgs e)
{
PTWcfService.ProjectData[] list = null;
PTWcfService.PTServiceClient svc = new PTWcfClient.PTWcfService.PTServiceClient();
try
{
list = svc.GetProjectList();
}
finally
{
svc.Close();
}
this.projectDataBindingSource.DataSource = list;
}

The code creates an instance of the service proxy:


PTWcfService.PTServiceClient svc = new PTWcfClient.PTWcfService.PTServiceClient();

Then the GetProjectList() method is called:


list = svc.GetProjectList();

Notice that the result is an array of type PTWcfService.ProjectData. This is not the same type as
on the server. It is important to remember that Visual Studio created a set of proxy types that have
the same shape as the data contract defined by your service, but they are not the same types. In other
Using CSLA .NET 3.0
Copyright 2007 Rockford Lhotka

Page 28

words, this ProjectData type doesnt use a List<T> internally like the version on the server. It
merely stores any child data in an array, because thats what the WSDL defined.
Finally, the service proxy is closed:
svc.Close();

Even though service proxies do implement IDisposable, they should be closed, not disposed,
when you are done using them. If you do dispose a proxy object, you will get runtime exceptions
from WCF.
At this point, you should understand how to use WCF with CSLA .NET through the WCF data
portal channel, the optional use of the DataContract and DataMember attributes on your business
classes and how to create a WCF service that is implemented using business objects behind the
service.

Custom Authentication
If you are using custom authentication in your business objects, youll typically want the client to
pass a username and password to the service. The service can then authenticate those credentials and
set up your custom principal object for use by the server code.
Authenticating the credentials and setting up the principal object is easily accomplished using a
CSLA .NET style custom principal object. For example, in ProjectTracker the PTPrincipal class
has a Login() method to do this:
PTPrincipal.Login(username, password);

Unfortunately, getting the username and password from a client into a WCF service is very
challenging. Remember that this is a function of security, and so a lot of infrastructure and
configuration work is required to get all the security pieces set up before you can safely pass a
username and password across the network.
WCF is designed to be secure, and so it wont allow you to pass a username and password without
first having a secure and encrypted binding. To do that, you first need a server certificate, either for
SSL or WCFs built-in message level security. Creating, installing and configuring a test certificate is
challenging. After that, some of the WCF configuration and code to use the certificate is also a bit
complex.
There are three processes involved:
1. Acquire or create an X.509 certificate.
2. Configure WCF to use message level security.
3. Configure WCF to use username credentials.
Lets walk through each of these steps.

Acquiring an X.509 Certificate


Securing a service or a web site is typically done with something commonly called an SSL
certificate, which is more formally called an X.509 certificate.

Using CSLA .NET 3.0


Copyright 2007 Rockford Lhotka

Page 29

In a production Internet environment, these certificates are typically issued by a commercial firm
like VeriSign or RapidSSL. These commercial certificates are cryptographically linked to a trusted
root certificate, and all the major vendors have their root certificates installed automatically on
Windows, so all Windows clients automatically trust the certificates they sell to their customers.
In a production intranet environment, the certificates might come from your organization itself.
Many organizations have their own internal certificate authority and their own internally trusted root
certificate. Certificates issued by the organization are only trusted by computers within the
organization (because they install the organizations trusted root certificate), not by all computers in
the world.
In a development or test environment, the certificates often are created by the developer. A
developer might use a tool like makecert.exe that comes with the Microsoft .NET SDK. These
certificates arent linked to any trusted root certificate, and so no one trusts them unless the certificate
is explicitly trusted by the user on a specific machine.

Creating a Test Certificate


Since most of us do development without access to either production or corporate certificates, it is
important to understand how to create and install a test certificate. This is the focus of the rest of this
section.
Note: If you do have access to the private key of a production or organizational
certificate, you can use that instead of creating your own as discussed here.
Creating and installing a certificate is a multi-step process:
1. Create the certificate using makecert.exe.
2. Install the certificate in the LocalMachine certificate store.
3. Grant IIS access to the certificate file.
4. Copy the certificate to the TrustedPeople certificate store on any test client machines.

Creating the Certificate


The Microsoft .NET SDK comes with a tool called makecert.exe. This tool is quite complex and has
a lot of different uses and options. This article describes in detail how to use the tool to create a
certificate http://msdn2.microsoft.com/en-us/library/aa354512.aspx.
In summary, you can use the following command line to create a test certificate:
C:\> makecert n CN=localhost localhost.cer sky exchange ss My sr LocalMachine

Table 4 lists the arguments and their meaning.

Using CSLA .NET 3.0


Copyright 2007 Rockford Lhotka

Page 30

Argument
-n CN=localhost

Meaning
Defines the name of the certificate. If possible this should be the
network domain name of your server, but you can provide a name
unique to your service if desired.

localhost.cer

This optional argument indicates that the certificate should be


saved to a file named localhost.cer. This can simplify the
process of installing the certificate on other test client machines.

-sky exchange

Specifies that the certificate should support key exchange. This is


required for WCF, because there is a key exchange process as the
secure channel is established.

-ss My

Indicates that the certificate should be installed into the certificate


store named My.

-sr LocalMachine

Indicates that the certificate should be installed to the


LocalMachine certificate store location.

Table 4. Makecert.exe arguments.


It is important to realize that this command not only creates the certificate, but it also installs it for
use by the server. This means that you should run this command on the machine that will be hosting
your WCF service.
In my example, I use the name CN=localhost because Im running both the client and service
components on the same development machine. The machines domain name is localhost for all
code on the same machine, and having the certificate name match the machines domain name
simplifies WCF configuration.
If you plan to use test clients that are on different machines, youll want to use a public machine
name for your certificate name, or some arbitrary name such as the name of your service. That last
option requires a little more configuration of WCF.

Granting Access to the Certificate to IIS


Even though the certificate is installed into the LocalMachine certificate store, it isnt entirely
available to IIS without some extra work. The reason is that web sites run under a limited user
account on the server. They dont, by default, have access to the key file that contains the
certificates private key information.
Granting access to the key file, is complicated by the fact that actually finding the key file can be
challenging. To help address this, theres a sample application provided by the WCF team at
Microsoft called FindPrivateKey . This article provides details and download information about
FindPrivateKey: http://msdn2.microsoft.com/EN-US/library/aa717039.aspx.
Once youve downloaded and built the sample application, you can run it as follows:
C:> findprivatekey My LocalMachine n "CN=localhost" a

The result is the full path to the certificate file corresponding to CN=localhost in the
LocalHost/My certificate store.
Using CSLA .NET 3.0
Copyright 2007 Rockford Lhotka

Page 31

You can then use the cacls.exe tool to grant access to the IIS account. For example, youd enter
the following command (on one line):
cacls.exe "C:\Documents and Settings\All Users \Application
Data\Microsoft\Crypto\RSA\MachineKeys\8aeda5eb81555f14f8f9960745b5a40d_38f7de48-5ee9-452d-8a5a92789d7110b1" /E /G "NETWORK_SERVICE":R

In this example, you can see both the full path to the certificate file, as well as the account that is
granted access (NETWORK_SERVICE). This is because I am working on Windows Server 2003, and it
would be the same on Windows Vista. On Windows XP, however, youd grant access to the ASPNET
account.
The result is that your service, when hosted in IIS, will have access to the certificates private key,
so it can use that information to sign and encrypt messages.

Trusting the Certificate


Though the makecert.exe command shown earlier installs the certificate for server use, it doesnt
make your machine trust the certificate. Because the certificate wasnt issued by a trusted root
authority, you must manually establish that you trust the certificate.
To do this, the certificate must be copied into your TrustedPeople certificate store. This can be
done using the certmgr.exe tool:
C:> certmgr

-add c n localhost r LocalMachine s My r CurrentUser s TrustedPeople

This article discusses the process in more detail as it relates to WCF:


http://msdn2.microsoft.com/en-us/library/aa702621.aspx.
Table 5 lists the arguments to the command and their purpose:

Argument
-add

Meaning
Specifies that the certificate should be added to a certificate store.

-c

Indicates that the certificate should be copied from another


certificate store.

-n localhost

Specifies the name of the certificate to the copied.

-r LocalMachine

Indicates the source certificate store location.

-s My

Indicates the source certificate store name.

-r CurrentUser

Indicates the target certificate store location.

-s TrustedPeople

Indicates the target certificate store name.

Table 5. Certmgr.exe arguments.


In short, this command copies the certificate from LocalMachine/My to
Server components use the LocalMachine/My certificate store, while
interactive user applications use CurrentUser/TrustedPeople.
CurrentUser/TrustedPeople.

Using CSLA .NET 3.0


Copyright 2007 Rockford Lhotka

Page 32

Manual Installation
Another way you can install the certificate is by double-clicking on the .cer file. You need to use
this technique to install the certificate onto other client machines. This will bring up a dialog
allowing you to view details about the certificate.
Click the Install Certificate button to bring up the Certificate Import Wizard. Figure 6
shows the key panel of the wizard, where you must specify to install the certificate into the Other
People store, which corresponds to CurrentUser/TrustedPeople.

Figure 6. Installing the certificate into the Other People store.


The result is the same: the current user can now use the certificate to communicate with the
server.

Configuring WCF to use Message Security


WCF can use transport level security, like SSL, message level security like Ill discuss in this section,
or a combination of both. The important thing is that you must use some form of security (encryption
and signing of data) before you can send a custom username and password from the client to your
service.
If you are already set up to use SSL security, you dont need to switch to message security. But if
you are not using any security, it is often simpler to set up message security as shown here.
The process involves customizing the behavior for the service endpoint, and the corresponding
client configuration as well.

Configuring the Service


By default the wsHttpBinding uses message level security, but you need to provide a certificate for
the security to actually be enabled. Configuring the service to use a certificate is done through a
Using CSLA .NET 3.0
Copyright 2007 Rockford Lhotka

Page 33

behavior. As with everything in WCF, you can configure the behavior through code, but it is more
commonly done through the configuration file.
The PTWcfService projects configuration already uses the
changed that to include the certificate configuration as well:

PTServiceBehavior,

so Ive just

<behaviors>
<serviceBehaviors>
<behavior name="PTServiceBehavior">
<serviceCredentials>
<serviceCertificate
findValue="localhost"
storeLocation="LocalMachine"
storeName="My"
x509FindType="FindBySubjectName" />
</serviceCredentials>
<serviceMetadata httpGetEnabled="true" />
<serviceDebug includeExceptionDetailInFaults="true" />
</behavior>
</serviceBehaviors>
</behaviors>

The serviceCertificate element indicates how to find the certificate to be used to secure the
communication channel. Table 6 lists the properties of this element.

Property
findValue

Description
The name of the certificate to find. This name must correspond to
the name used earlier to name the certificate (CN=localhost).

storeLocation

The certificate store location containing the certificate.

storeName

The certificate store name containing the certificate.

X509FindType

Specifies which properties to use when searching for the


certificate. In this case the search is by subject name
(the CN= value).

Table 6. Properties of the serviceCertificate element.


The result is that this behavior now requires that the communication be encrypted using the
defined certificate.

Configuring the Client


All clients calling the service must also understand that a certificate is required for secure
communication. The following illustrates the changes required in the clients configuration file:
<behaviors>
<endpointBehaviors>
<behavior name="ServiceCertificate">
<clientCredentials>
<serviceCertificate>
<authentication certificateValidationMode="PeerTrust"/>
</serviceCertificate>
</clientCredentials>
</behavior>
</endpointBehaviors>
</behaviors>

Using CSLA .NET 3.0


Copyright 2007 Rockford Lhotka

Page 34

<client>
<endpoint
behaviorConfiguration="ServiceCertificate"
address="http://localhost/PTWcfServicecs/PTService.svc"
binding="wsHttpBinding" bindingConfiguration="WSHttpBinding_IPTService"
contract="PTWcfService.IPTService" name="WSHttpBinding_IPTService">
<identity>
<dns value="localhost"/>
</identity>
</endpoint>
</client>

There are two important changes here.


First, the client endpoint now uses a customized behavior named ServiceCertificate. This
behavior specifies that the service requires a certificate, and that the client should validate the
certificate using PeerTrust .
The default is ChainTrust, meaning that trust flows down from a trusted root certificate. With a
test certificate however, there is no trusted root so that would fail. The PeerTrust option indicates
that trust flows from the TrustedPeople certificate store location, where the certificate was installed
in the previous section.
This configuration setting is just for testing. In a production environment you would typically use
the default ChainTrust, because a production certificate would have a trusted root authority from a
commercial vendor or from your organization itself.
Second, the identity element specifies the name of the certificate to use. This is not required here
, because the certificate name, localhost , matches the domain name for the server:
http://localhost/<etc>. However, if your certificate name does not match the server domain
name, then you use the identity element as shown to specify the name of the certificate.
At this point, the service and client are both configured to use message level secure
communications.

Configuring a Service to use Username Credentials


By default, most common WCF bindings use Windows credentials from the client. This is true even
if a username and password are provided by the client, as they are assumed to correspond to a
Windows user account in the servers Windows domain.
If you want to use custom authentication and create a custom security principal object for your
service, you need to configure WCF to use your authentication components, instead of the defaults.
You can do this in a couple of different ways:
1. Create custom ASP.NET membership and role providers and configure WCF to use your
custom providers.
2. Create a custom WCF username/password validator and authorization policy object.
Both techniques are valid, but the second one is an entirely WCF solution, and it turns out to be
relatively simple to implement. As a result, it is the second option Ill discuss in this section.
Using a custom principal object requires changes to the service, both in configuration and code.
WCF allows customization for both authentication and authorization. In each case, a class must be
created with a custom implementation of the required behavior. These two classes are invoked at
different times during the WCF initialization process on every service call.
Using CSLA .NET 3.0
Copyright 2007 Rockford Lhotka

Page 35

Unfortunately, the strong separation of these two concepts in WCF complicates matters a little.
After the credential validation step, WCF clears the principal object. Later in the process, the
principal can be set for the service, but at that point in time the credentials are no longer available.
This means that the credential validation step must somehow cache the results of the Login()
method in memory so that the principal object can be made current later in the process as shown in
Figure 7. This is the purpose behind the PrincipalCache object in Csla.Security.

Figure 7. Authentication and authorization during WCF initialization.


The PrincipalCache object caches principal objects at the AppDomain level. Since WCF
guarantees that a consistent AppDomain is used throughout the life of a service call, this is a safe
location for such a cache.
To minimize memory consumption, the PrincipalCache object is implemented as a circular list
with a default size of 10 items. Keep in mind that the principal object only needs to be cached for the
fraction of a second it takes WCF to go through its initialization process prior to invoking the service
itself, so the size of the cache should be roughly the same as the number of service requests you
expect to occur at any instant in time.
You can control the size of the cache by using the CslaPrincipalCacheSize key in the
web.config file:
<appSettings>
<add key="CslaPrincipalCacheSize" value="20" />
</appSettings>

Using CSLA .NET 3.0


Copyright 2007 Rockford Lhotka

Page 36

This would override the default, expanding the cache to keep the most recent 20 principal objects
in memory.
Without the PrincipalCache object, the authorization step would need to go back to the security
database to reload the principal object. This results in two hits to the security database for every
service request.
Even with the PrincipalCache object, a second hit to the security database may be required. If the
cache is configured to be too small, theres a chance that the principal object could be gone from the
cache in the fraction of a second it takes to get from authentication to authorization. To prevent this
unlikely occurrence from causing a failure, your authorization code should have the option of
reloading the principal from the database if needed, and thats the approach Ill illustrate in this
chapter.

Modifying PTPrincipal
Before I get into the WCF authorization and authentication code, it is necessary to modify
PTPrincipal and PTIdentity to accommodate the separation between authentication and
authorization.

LoadPrincipal Method
WCF validates the users credentials early in the process. Later in the process, WCF creates an
authorization policy object that defines the principal and identity objects to be used as the service is
executed. This authorization policy object is only provided with the username value, not the
password, and so PTPrincipal needs a way to load the principal and identity objects based purely on
the username.
This requirement isnt unique to WCF. When creating a totally stateless web application, the
custom principal cant be held in Session on the web server. Instead, it must be reloaded on each
page request, based purely on the username. While the password isnt maintained by ASP.NET forms
authentication, the username is provided on every page request. This LoadPrincipal() method can
also be used to reload the principal from the database on every page request.
The LoadPrincipal() method is very similar to the Login() method, except that it only requires a
username, not a password. In fact, Ive altered the Login() method to share some code:
public static bool Login(string username, string password)
{
return SetPrincipal(PTIdentity.GetIdentity(username, password));
}
public static void LoadPrincipal(string username)
{
SetPrincipal(PTIdentity.GetIdentity(username));
}
private static bool SetPrincipal(PTIdentity identity)
{
if (identity.IsAuthenticated)
{
PTPrincipal principal = new PTPrincipal(identity);
Csla.ApplicationContext.User = principal;
}
return identity.IsAuthenticated;
}

Using CSLA .NET 3.0


Copyright 2007 Rockford Lhotka

Page 37

The highlighted line of code invokes a new overload of the GetIdentity() factory method on
PTIdentity. This overload uses a different criteria object, that contains only the username, to call an
overload of DataPortal_Fetch() in PTIdentity:
private void DataPortal_Fetch(LoadOnlyCriteria criteria)
{
using (SqlConnection cn =
new SqlConnection(Database.SecurityConnection))
{
cn.Open();
using (SqlCommand cm = cn.CreateCommand())
{
cm.CommandText = "GetUser";
cm.CommandType = CommandType.StoredProcedure;
cm.Parameters.AddWithValue("@user", criteria.Username);
using (SqlDataReader dr = cm.ExecuteReader())
{
Fetch(dr);
}
}
}
}

Because the loading of the objects fields is the same for both the login and load process, Ive
consolidated that code into a Fetch() method that is called by both overloads of
DataPortal_Fetch():
private void Fetch(SqlDataReader dr)
{
if (dr.Read())
{
_name = dr.GetString(0);
_isAuthenticated = true;
if (dr.NextResult())
{
while (dr.Read())
{
_roles.Add(dr.GetString(0));
}
}
}
else
{
_name = string.Empty;
_isAuthenticated = false;
_roles.Clear();
}
}

The original login process is effectively unchanged. But this new load process calls a different
stored procedure that doesnt require or verify the password:

Using CSLA .NET 3.0


Copyright 2007 Rockford Lhotka

Page 38

CREATE PROCEDURE GetUser


(
@user varchar(20)
)
AS
SELECT Username
FROM Users
WHERE Username=@user;
SELECT R.Role
FROM Users AS U INNER JOIN Roles AS R ON
R.UserName = U.UserName
WHERE U.Username = @user
RETURN

The end result is that the UI can either call Login(username, password) or
and the result is the same. Of course, only the Login() method actually
validates the users credentials. Ill use the new LoadPrincipal() method in the implementation of
the custom WCF authorization behavior.
LoadPrincipal(username)

Custom UserNamePasswordValidator
In WCF, custom username authorization is handled by subclassing UserNamePasswordValidator
from the System.IdentityModel.Selectors namespace. The implementation of this class is not
complex. If the users credentials are valid, it returns without an exception. If the credentials are
invalid, the method must throw an exception. Only users with valid username/password
combinations get past this point in the process.
It is important to realize that you cannot set the current principal at this point. WCF makes no
guarantee that this code will run on the same thread as the actual service instance, and it explicitly
resets the principal object at a point after this method completes. However, using the approach shown
in Figure 7, it is possible to cache the principal in memory for use later by using the PrincipalCache
object.
You can find the CredentialValidator class in the PTWcfServiceAuth project in
ProjectTracker:
public class CredentialValidator : UserNamePasswordValidator
{
public override void Validate(string userName, string password)
{
if (userName != "anonymous")
{
PTPrincipal.Logout();
if (!PTPrincipal.Login(userName, password))
throw new FaultException("Unknown username or password");
// add current principal to rolling cache
Csla.Security.PrincipalCache.AddPrincipal(Csla.ApplicationContext.User);
}
}
}

Notice the call to the Logout() method. This is required because the data portal must have a valid,
even if unauthenticated, principal to work. Without the call to Logout() , the Login() call would
throw a data portal exception due to having an invalid principal on the thread.
If the Login() call succeeds, the resulting principal (which is temporarily the current principal
until this method completes) is stored in the PrincipalCache object. This makes the principal object
available for use later in the service initialization process.
Using CSLA .NET 3.0
Copyright 2007 Rockford Lhotka

Page 39

Finally, notice that special consideration is made for the username anonymous . Remember that
ProjectTracker allows unauthenticated users to access some information and so the application must
allow anonymous users to at least make some service calls. This special username will get extra
attention in the custom authorization policy class later.
Ill discuss how to configure WCF to use the CredentialValidator class later in the chapter. For
now, it is enough to know that this code will be invoked early in the process as WCF initializes itself
for every service call.

Custom Authorization Policy


During the initialization process for every service call, WCF creates an authorization policy object.
This object is responsible for providing the principal and identity objects to be used during the
service call itself, so this is the point at which the custom principal and identity objects must be set.
To implement a custom authorization policy, a class must implement the IAuthorizationPolicy
interface from the System.IdentityModel.Policy namespace:
public class PrincipalPolicy : IAuthorizationPolicy
{
private string _id = Guid.NewGuid().ToString();
public string Id
{
get { return _id; }
}
public ClaimSet Issuer
{
get { return ClaimSet.System; }
}
public bool Evaluate(EvaluationContext context, ref object state)
{
// get the identities list from the context
object obj;
if (!context.Properties.TryGetValue("Identities", out obj))
return false;
IList<IIdentity> identities = obj as IList<IIdentity>;
// make sure there is already a default identity
if (identities == null || identities.Count <= 0)
return false;
// try to get principal from rolling cache
string username = identities[0].Name;
IPrincipal principal = Csla.Security.PrincipalCache.GetPrincipal(username);
if (principal == null)
{
PTPrincipal.Logout();
if (username != "anonymous")
{
// load principal based on username authenticated in CredentialValidator
PTPrincipal.LoadPrincipal(username);
// add current principal to rolling cache
Csla.Security.PrincipalCache.AddPrincipal(Csla.ApplicationContext.User);
}
principal = Csla.ApplicationContext.User;
}
// tell WCF to use the custom principal
context.Properties["Principal"] = principal;
// tell WCF to use the custom identity
Using CSLA .NET 3.0
Copyright 2007 Rockford Lhotka

Page 40

identities[0] = principal.Identity;
return true;
}
}

The Evaluate() method is called to evaluate the current context and set the principal and identity.
The highlighted lines of code show how the principal is retrieved from the PrincipalCache object
based on the username value available from the identity object retrieved from the
context.Properties dictionary:
string username = identities[0].Name;
IPrincipal principal = Csla.Security.PrincipalCache.GetPrincipal(username);

The identity object from context.Properties is typically a GenericIdentity that merely


contains the name of the user, based on the credentials originally provided to CredentialValidator .
This identity object was created by WCF, and should be replaced by the custom identity associated
with our custom principal object.
It is possible that there is no cached principal object matching the username. This can happen for
two reasons: because the anonymous username was used, or because a valid users principal was
flushed from the cache since CredentialValidator was called (meaning the cache size is too small).
Either way, some valid custom principal object must be created and made current.
Notice that the Logout() method is called before LoadPrincipal(). Remember that WCF clears
the principal object between authentication and setting the authorization policy, so when Evaluate()
is called by WCF the principal is not a valid principal from the data portals perspective. In fact, it is
just an authenticated GenericPrincipal at this point. The Logout() method makes sure theres a
valid PTPrincipal on the thread before LoadPrincipal() tries to use the data portal.
If the anonymous username was used, then that unauthenticated custom principal is left as the
current principal.
If the anonymous username wasnt used, however, that indicates that the users principal was
prematurely flushed from the cache. The solution is to call the LoadPrincipal() method to reload the
principal and identity objects from the security database.
Note: You may also want to log that this occurred, as it indicates that your cache size
is too small to handle the number of service requests hitting your server.
The result is a valid custom principal that is current on the thread. You can use
to get at the principal like you would in any other environment.

Csla.ApplicationContext.User

Both the principal and identity must also be made available to WCF by setting values in
context.Properties.

The custom principal is provided to WCF through context.Properties:


context.Properties["Principal"] = principal;

The custom identity replaces the old GenericIdentity created by WCF:


identities[0] = principal.Identity;

Using CSLA .NET 3.0


Copyright 2007 Rockford Lhotka

Page 41

This object is then used as the primary identity for the ServiceSecurityContext provided by
WCF.
With these two classes defined in the
use them.

PTWcfServiceAuth

project, all that remains is to configure

PTWcfService to

Server Configuration
WCF allows the use of custom username validation and authorization policy objects through
configuration. Before changing web.config however, it is important to reference the
PTWcfServiceAuth project so that assembly is available to the service.
The highlighted lines of code show the changes to web.config necessary to use these new classes:
<services>
<service behaviorConfiguration="PTServiceBehavior" name="PTService">
<endpoint address=""
binding="wsHttpBinding" bindingConfiguration="UserNameWS"
contract="IPTService" />
<endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange" />
</service>
</services>
<bindings>
<wsHttpBinding>
<binding name="UserNameWS">
<security mode="Message">
<message clientCredentialType="UserName" />
</security>
</binding>
</wsHttpBinding>
</bindings>
<behaviors>
<serviceBehaviors>
<behavior name="PTServiceBehavior">
<serviceAuthorization principalPermissionMode="Custom">
<authorizationPolicies>
<add policyType="PTWcfServiceAuth.PrincipalPolicy, PTWcfServiceAuth"/>
</authorizationPolicies>
</serviceAuthorization>
<serviceCredentials>
<serviceCertificate
findValue="localhost"
storeLocation="LocalMachine"
storeName="My"
x509FindType="FindBySubjectName" />
<userNameAuthentication
userNamePasswordValidationMode="Custom"
customUserNamePasswordValidatorType=
"PTWcfServiceAuth.CredentialValidator, PTWcfServiceAuth" />
</serviceCredentials>
<serviceMetadata httpGetEnabled="true" />
<serviceDebug includeExceptionDetailInFaults="true" />
</behavior>
</serviceBehaviors>
</behaviors>

The endpoint is configured to use a special bindingConfiguration named UserNameWS. That


defines a custom version of wsHttpBinding:

Using CSLA .NET 3.0


Copyright 2007 Rockford Lhotka

Page 42

<bindings>
<wsHttpBinding>
<binding name="UserNameWS">
<security mode="Message">
<message clientCredentialType="UserName" />
</security>
</binding>
</wsHttpBinding>
</bindings>

It explicitly sets the security mode to Message, which is the default. More importantly, it specifies
that the clientCredentialType is UserName, so the service will require that callers provide a
username and password when calling the service.
Also, notice that the PTServiceBehavior behavior
serviceAuthorization element has been added:

has been altered. A new

<serviceAuthorization principalPermissionMode="Custom">
<authorizationPolicies>
<add policyType="PTWcfServiceAuth.PrincipalPolicy, PTWcfServiceAuth"/>
</authorizationPolicies>
</serviceAuthorization>

This specifies that the principalPermissionMode is Custom, and that means that a custom
authorization policy must be defined. It is set to use a policyType that references the
PrincipalPolicy class discussed earlier in the chapter.
The behaviors serviceCredentials element has also been enhanced to define the
customUserNamePasswordValidatorType that refers to the CredentialValidator class discussed
earlier in the chapter:
<userNameAuthentication
userNamePasswordValidationMode="Custom"
customUserNamePasswordValidatorType=
"PTWcfServiceAuth.CredentialValidator, PTWcfServiceAuth" />

With these changes, WCF will now invoke the CredentialValidator to authenticate the callers
username and password. Assuming that method doesnt throw an exception, WCF will invoke the
PrincipalPolicy to set up the authorization policy, later in the process. This will includ the custom
principal and identity to be used by the service.

Providing a Username from the Client


The client application that calls the service must now provide a username with every call. It may also
provide a password, but WCF will require that a username value be provided that is not empty or
null.
It is also the case that the client configuration needs to change in order to reflect the changes made
to the services configuration.

Client Implementation
In the clients code, setting the username and password values is relatively straightforward:

Using CSLA .NET 3.0


Copyright 2007 Rockford Lhotka

Page 43

PTWcfService.ProjectData[] list = null;


PTWcfService.PTServiceClient svc = new PTWcfClient.PTWcfService.PTServiceClient();
try
{
svc.ClientCredentials.UserName.UserName = "pm";
svc.ClientCredentials.UserName.Password = "pm";
list = svc.GetProjectList();
}
finally
{
svc.Close();
}
this.projectDataBindingSource.DataSource = list;

Because the service supports the special anonymous username, this will also work (for methods
that dont require special roles):
PTWcfService.ProjectData[] list = null;
PTWcfService.PTServiceClient svc = new PTWcfClient.PTWcfService.PTServiceClient();
try
{
svc.ClientCredentials.UserName.UserName = "anonymous";
list = svc.GetProjectList();
}
finally
{
svc.Close();
}
this.projectDataBindingSource.DataSource = list;

You might also use a channel factory to create the service proxy. In that case, you set the
credentials on the factory:
PTWcfService.ProjectData[] list = null;
ChannelFactory<PTWcfService.IPTService> factory =
new ChannelFactory<PTWcfService.IPTService>("WSHttpBinding_IPTService");
try
{
factory.Credentials.UserName.UserName = "pm";
factory.Credentials.UserName.Password = "pm";
PTWcfService.IPTService proxy = factory.CreateChannel();
using (proxy as IDisposable)
{
list = proxy.GetProjectList();
}
}
finally
{
factory.Close();
}
this.projectDataBindingSource.DataSource = list;

In this case, you can dispose the proxy, but you should call Close() on the factory object.
Disposing the factory may result in an exception from WCF.
Either way, the supplied values are passed securely to the service where they are authenticated
and then used to load the custom principal and identity objects.

Client Configuration
There is one change required to the callers WCF configuration, and it is highlighted here:

Using CSLA .NET 3.0


Copyright 2007 Rockford Lhotka

Page 44

<bindings>
<wsHttpBinding>
<binding name="WSHttpBinding_IPTService" closeTimeout="00:01:00"
openTimeout="00:01:00" receiveTimeout="00:10:00" sendTimeout="00:01:00"
bypassProxyOnLocal="false" transactionFlow="false"
hostNameComparisonMode="StrongWildcard"
maxBufferPoolSize="524288" maxReceivedMessageSize="65536"
messageEncoding="Text" textEncoding="utf-8" useDefaultWebProxy="true"
allowCookies="false">
<readerQuotas maxDepth="32" maxStringContentLength="8192" maxArrayLength="16384"
maxBytesPerRead="4096" maxNameTableCharCount="16384" />
<reliableSession ordered="true" inactivityTimeout="00:10:00"
enabled="false" />
<security mode="Message">
<transpor t clientCredentialType="Windows" proxyCredentialType="None"
realm="" />
<message clientCredentialType="UserName" negotiateServiceCredential="true"
algorithmSuite="Default" establishSecurityContext="true" />
</security>
</binding>
</wsHttpBinding>
</bindings>
<behaviors>
<endpointBehaviors>
<behavior name="ServiceCertificate">
<clientCredentials>
<serviceCertificate>
<authentication certificateValidationMode="PeerTrust"/>
</serviceCertificate>
</clientCredentials>
</behavior>
</endpointBehaviors>
</behaviors>
<client>
<endpoint
behaviorConfiguration="ServiceCertificate"
address="http://localhost/PTWcfServicecs/PTService.svc"
binding="wsHttpBinding" bindingConfiguration="WSHttpBinding_IPTService"
contract="PTWcfService.IPTService" name="WSHttpBinding_IPTService">
</endpoint>
</client>

If you hand-craft your client configuration, much of what you see here is optional. However, if
you allow Visual Studio to create the configuration for you, youll see something similar to this. The
highlighted lines of code merely specify that, at the message level, the service expects a UserName
type credential:
<message clientCredentialType="UserName" negotiateServiceCredential="true"
algorithmSuite="Default" establishSecurityContext="true" />

With this change, the client will now properly pass the credentials through to the service.
At this point, you should understand how to use WCF with CSLA .NET. You can use WCF
through the data portal as a replacement for previous network transports. You can configure CSLA
.NET to use WCF serialization with the NetDataContractSerializer. You can also create and
consume WCF services using CSLA .NET objects.

Using CSLA .NET 3.0


Copyright 2007 Rockford Lhotka

Page 45

Chapter 2:
Windows Presentation Foundation
Perhaps the part of Microsoft .NET 3.0 that will have the biggest long-term impact, is the Windows
Presentation Foundation (WPF). WPF is the next generation UI technology for the development of
Windows applications.
Note: In its Silverlight incarnation, WPF is also the next generation technology for
the development of Web applications.
In this chapter, I assume you have a basic understanding of WPF. This includes the concepts of
XAML programming, how code behind works in a WPF application and XML namespaces.
WPF provides many of the best features of both Windows and web development. It is a rich,
interactive, event-driven environment similar to Windows Forms. Yet it uses an XML-based, stylable
tag markup language to describe the presentation much like the web.
The challenge with WPF is that it has aspects that are very similar to both Windows Forms and
Web Forms development, and yet it is fundamentally different from both of those technologies. This
leads to a learning curve that must be overcome before you can be productive in the new
environment.
Like Windows Forms, WPF provides powerful data binding support. But it is important to
remember that WPF is a version 1.0 technology, and so there are features that a Windows Forms
developer would expect that simply dont exist (yet) in WPF.
Like Web Forms, WPF uses a data control model (called a data provider control) to load data into
a form. And like its ASP.NET equivalent, the ObjectDataProvider control is too limited to work
with CSLA .NET style business objects. To address this, CSLA .NET now includes a
CslaDataProvider control for WPF.
A Windows Forms developer might expect to find an ErrorProvider control, but WPF does not
yet have such a control. CSLA .NET provides a Validator control to provide similar functionality.
CSLA .NET also provides an Authorizer control, which is roughly equivalent to the
ReadWriteAuthorization control CSLA .NET includes for Windows Forms development.
Like Windows Forms, WPF has an issue where data changed in a propertys set block wont be
immediately shown in the UI. CSLA .NET includes an IdentityConverter control that can be used
to address this issue.
Note: You may see some differences between the code in this chapter and the code in
the PTWpf project from the ProjectTracker download. The differences are because
PTWpf relies on a custom UI base class that Ill discuss in Chapter 7.
Before getting into the details of each control, it is important to discuss one higher-level issue
regarding custom authentication.

Using CSLA .NET 3.0


Copyright 2007 Rockford Lhotka

Page 46

Custom Authentication in WPF


WPF is not threadsafe. This means that all UI work must be done on the single UI thread.
Nevertheless, WPF does use background threads behind the scenes. The security infrastructure in
.NET is driven off the current principal object, which is associated with a specific thread. This means
that you can run into cases where your code is running on a thread that doesnt have the right
principal.
This issue only applies if you are using custom authentication. If you are using Windows
authentication, then all threads will use the WindowsPrincipal for the user logged into the
workstation and theres no issue.
The common case where youll encounter this issue is when doing data access. To overcome this
issue, the CSLA .NET client-side data portal now raises a new static event: DataPortalInitInvoke.
The DataPortalInitInvoke event is raised before every data portal call. More importantly, it
occurs before the data portal initializes the context objects that are passed to the application server
along with your actual data portal request to create, fetch, update or execute an object.
Note: This issue applies to both local and remote data portal configurations. Even if
there is no remote data portal server, WPF may run some of your code on a
background thread. Therefore, you need to implement the code discussed in this
section to get a consistent principal object at all times.

Managing the Principal Object


You can handle the DataPortalInitInvoke event in your code to ensure that all threads have a valid
and correct principal object before any data access is attempted.
You must store a reference to the current principal object in a static field so it is available to all
code in your AppDomain. Then you can use the DataPortalInitInvoke event to ensure that all
threads use that common principal object.
I typically implement this code in the main form of my application. You can find an example in
the PTWpf project in ProjectTracker . Look at the code in MainForm.

Storing the Principal Reference


The first step is to store a reference to the current principal object in a static field so it is available
to all code in your AppDomain, regardless of thread. Remember that a remote data portal requires a
valid (even if unauthenticated) principal at all times, and so you need to make sure the principal is
valid as your application starts.
Usually, this is done by calling a Logout() method to set the principal to a valid, unauthenticated
principal:
private static ProjectTracker.Library.Security.PTPrincipal _principal;
void MainForm_Loaded(object sender, RoutedEventArgs e)
{
ProjectTracker.Library.Security.PTPrincipal.Logout();
_principal = (ProjectTracker.Library.Security.PTPrincipal)
Csla.ApplicationContext.User;
this.Title = "Project Tracker";
}
Using CSLA .NET 3.0
Copyright 2007 Rockford Lhotka

Page 47

As the main form is loaded, a Logout() method is called. Then the current principal object is
stored in the static field.
This static reference must be updated if the current principal changes. In your UI code, after
youve logged a user in or out, refresh the field. For example:
void LogInOut(object sender, EventArgs e)
{
if (Csla.ApplicationContext.User.Identity.IsAuthenticated)
{
ProjectTracker.Library.Security.PTPrincipal.Logout();
CurrentUser.Text = "Not logged in";
LoginButtonText.Text = "Log in";
}
else
{
Login frm = new Login();
frm.ShowDialog();
if (frm.Result)
{
string username = frm.UsernameTextBox.Text;
string password = frm.PasswordTextBox.Password;
ProjectTracker.Library.Security.PTPrincipal.Login(username, password);
}
if (!Csla.ApplicationContext.User.Identity.IsAuthenticated)
{
ProjectTracker.Library.Security.PTPrincipal.Logout();
CurrentUser.Text = "Not logged in";
LoginButtonText.Text = "Log in";
}
else
{
CurrentUser.Text =
string.Format("Logged in as {0}",
Csla.ApplicationContext.User.Identity.Name);
LoginButtonText.Text = "Log out";
}
}
_principal = (ProjectTracker.Library.Security.PTPrincipal)
Csla.ApplicationContext.User;
// ...
}

This way the static field can always be used, on any thread, to get a reference to the current
principal.

Using the DataPortalInitInvoke Event


Any time the data portal is invoked to perform a create, fetch, update or execute operation, it first
raises a static event called DataPortalInitInvoke. Handling this event allows you to ensure that
the thread running the data portal code has the current principal from the static field.
The first thing you need to do is set up an event handler for this event. You may do this in the
main forms constructor or when it is first loaded. For example:

Using CSLA .NET 3.0


Copyright 2007 Rockford Lhotka

Page 48

public MainForm()
{
InitializeComponent();
_mainForm = this;
this.Loaded += new RoutedEventHandler(MainForm_Loaded);
Csla.DataPortal.DataPortalInitInvoke +=
new Action<object>(DataPortal_DataPortalInitInvoke);
}

Then, in the event handler itself, all you need to do is make sure the current principal for the
thread matches the current principal from the static field:
void DataPortal_DataPortalInitInvoke(object obj)
{
if (!ReferenceEquals(Csla.ApplicationContext.User, _principal))
Csla.ApplicationContext.User = _principal;
}

When the application first starts up and WPF initializes the threads, it will use this code to set the
principal for those threads. Once the application is running, WPF tends to reuse the same threads and
so this code basically does nothing at that point.
You should now understand that WPF provides a slightly more complex environment, in some
ways, than Windows Forms or Web Forms. To use custom authentication, you need to implement
some code to deal with WPFs use of multiple threads.

CslaDataProvider Control
WPF has a data control concept similar to the data source control concept from ASP.NET 2.0. In
WPF these data controls are called data providers, and they allow declarative data access from your
XAML code, or your code-behind.
Data provider controls are powerful, because they abstract the concept of data access within a
WPF form, and they can support additional behaviors such as providing asynchronous access to data.
As with ASP.NET, WPF provides an ObjectDataProvider control that might, at first glance,
appear to be a good way to work with CSLA .NET style business objects. Unfortunately, the
ObjectDataProvider has some of the same limitations as the ASP.NET ObjectDataSource control:
It requires a public constructor
It has no way to call a static (or any other type of) factory method
CSLA .NET style business objects have non-public constructors and factory methods are used to
create or retrieve the object.
Additionally, CSLA .NET objects intrinsically support n-level undo and persistence, and the
has no knowledge of those capabilities either.

ObjectDataProvider

Whats needed is a data provider control that understands how to call static factory methods and
how to manage the objects lifetime: interacting with n-level undo and CSLA .NET style object
persistence.
The CslaDataProvider is a WPF data provider control that understands how to interact with
CSLA .NET business objects. This control cannot only create or retrieve a business object, but it can

Using CSLA .NET 3.0


Copyright 2007 Rockford Lhotka

Page 49

manage the objects entire lifetime through to saving (inserting, updating or deleting) the object into
the database, or cancelling any changes made to the object by the user.
Like many other data provider controls, CslaDataProvider supports asynchronous loading of the
object, which can be a very powerful feature in some cases.
Table 7 lists the properties of the CslaDataProvider control available for use in your XAML
code.

Property
x:Key

Description
A standard WPF property that defines the name of this instance of
CslaDataProvider .

ObjectType

Defines the type of the business object to be created or retrieved by


the data provider. Typically this is set to an x:Type expression
referencing a CSLA .NET business object type from your business
library.

FactoryMethod

Specifies the name of the static factory method called to create or


retrieve an instance of the business object at runtime.

ManageLifetime

Indicates whether the data provider should use n-level undo to


manage the lifetime of the business object by calling BeginEdit(),
ApplyEdit() and CancelEdit() at appropriate times.
Typically you will set this property to
default is False.

True for

editable objects, the

IsAsynchronous

Indicates whether the data provider should load the object on a


background thread and provide the object to data binding once
loading is complete. The default is False.

IsInitialLoadEnabled

Indicates whether the data provider should create an instance of the


business object when the control is first initialized. If you are passing
dynamic parameter values to your factory method, you will typically
set this property to False. The default is True .

FactoryParameters

Provides a list of parameters that will be passed to the static factory


method when it is invoked.

Table 7. CslaDataSource control properties


The x:Key, ObjectType and FactoryMethod properties are the minimum required properties that
must be set for CslaDataProvider to return a valid business object. The other properties are useful in
many cases, but are not always required.
Lets start by discussing the basic use of CslaDataProvider , and then move on to see how to use
it in other common scenarios.

Using CSLA .NET 3.0


Copyright 2007 Rockford Lhotka

Page 50

Basic Use
The basic use of the CslaDataProvider control is relatively straightforward:
1. Define the Csla.Wpf namespace in XAML
2. Define your business library namespace in XAML
3. Define CslaDataProvider as a resource
4. Use the resource as a data source

Define the Csla.Wpf Namespace


Because CslaDataProvider comes from the Csla.Wpf namespace in Csla.dll, you must first tell
WPF how to find the type. This is done by adding a new XML namespace in your XAML:
<Page x:Class="PTWpf.ProjectEdit"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:csla="clr-namespace:Csla.Wpf;assembly=Csla"
Title="Project Edit"
>

You can now use the csla: prefix to use the controls from the Csla.Wpf namespace in your form.

Define Your Business Namespace


Similarly, your business objects type comes from a namespace in your business library (assembly).
Before you can use or reference your business object type, that namespace must also be added to
your XAML. Heres an example of adding the ProjectTracker.Library namespace from the
ProjectTracker.Library.dll assembly:
<Page x:Class="PTWpf.ResourceList"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:csla="clr-namespace:Csla.Wpf;assembly=Csla"
xmlns:lib="clr-namespace:ProjectTracker.Library;assembly=ProjectTracker.Library"
Title="Resource List">

You can now use the lib: prefix to access the types from your business library.

Define CslaDataProvider as a Resource


With the namespaces defined, the next step is to define an instance of CslaDataProvider as a
resource in your form.

Using CSLA .NET 3.0


Copyright 2007 Rockford Lhotka

Page 51

<Page x:Class="PTWpf.ResourceList"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:csla="clr-namespace:Csla.Wpf;assembly=Csla"
xmlns:lib="clr-namespace:ProjectTracker.Library;assembly=ProjectTracker.Library"
Title="Resource List">
<Page.Resources>
<csla:CslaDataProvider x:Key="ResourceList"
ObjectType="{x:Type lib:ResourceList}"
FactoryMethod="GetResourceList">
</csla:CslaDataProvider>
</Page.Resources>

Notice how the <csla:CslaDataProvider> tag is contained within a <Page.Resources> tag. You
can define CslaDataProvider as a resource anywhere you would use any other type of data provider
as a resource. However, data providers are typically declared at the top level of a form as shown here.
Note: Instead of Page, you can use Window or UserControl as well.
Remember that the x:Key, ObjectType and FactoryMethod properties are the minimum required
properties that must be set for the CslaDataProvider control to return a valid business object.
At this point, you have defined a data resource that you can use as a data source in your form.

Using the Data Provider Resource


To use a data provider resource with data binding, you simply specify the resource as the
DataContext for a control:
<Page x:Class="PTWpf.ResourceList"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:csla="clr-namespace:Csla.Wpf;assembly=Csla"
xmlns:lib="clr-namespace:ProjectTracker.Library;assembly=ProjectTracker.Library"
Title="Resource List">
<Page.Resources>
<csla:CslaDataProvider x:Key="ResourceList"
ObjectType="{x:Type lib:ResourceList}"
FactoryMethod="GetResourceList">
</csla:CslaDataProvider>
</Page.Resources>

<Grid DataContext="{Binding Source={StaticResource ResourceList}}">

</Grid>

Notice that in the Binding expression, the resource name,


property used to define the resource.

ResourceList,

matches the

x:Key

Note: You may define many data provider resources for a form, as long as each one
has a unique x:Key so it can be referenced later in the XAML.
At this point, the Grid control will bind to the business object returned by the CslaDataProvider .
Thanks to the way WPF data binding works, all controls contained within the Grid will also default
to binding to this same business object.

Using CSLA .NET 3.0


Copyright 2007 Rockford Lhotka

Page 52

Passing Parameters to the Factory Method


Though some objects have static factory methods that accept no parameters like the ResourceList
example earlier, many factory methods require parameters when they are created or retrieved.
If your parameters are static, they can be placed directly in the XAML. If they are dynamic, you
can set them using data binding or in the code behind your form.
Additionally, using code behind the form, you can change the name of the factory method at
runtime. This may be required when you need to change a CslaDataSource from creating a new
object through one factory to retrieving an existing object by calling a different factory.

Setting Parameters in XAML


The simplest, but most limiting, approach is to set the parameter values directly in the XAML. This
is useful for forms that always load an object that is loaded based on the same parameter values.
In most cases, parameter values are of .NET types like string , int and so forth. Like any other
type, before you can use it in XAML you must import the appropriate namespace:
<Page x:Class="PTWpf.ProjectList"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:csla="clr-namespace:Csla.Wpf;assembly=Csla"
xmlns:lib="clr-namespace:ProjectTracker.Library;assembly=ProjectTracker.Library"
xmlns:system="clr-namespace:System;assembly=mscorlib"
Title="Project List">

In this case, Im referencing the System namespace from the .NET mscorlib.dll assembly, and
so all the basic .NET types like String, Object and Int32 are now available for use in the XAML.
Note: While C# has language-specific keywords for most primitive .NET types, you
must use the .NET type name from the System namespace when using these types in
XAML.
At this point, you can specify factory parameter values directly in the XAML:
<Page x:Class="PTWpf.ProjectList"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:csla="clr-namespace:Csla.Wpf;assembly=Csla"
xmlns:lib="clr-namespace:ProjectTracker.Library;assembly=ProjectTracker.Library"
xmlns:system="clr-namespace:System;assembly=mscorlib"
Title="Project List">
<Page.Resources>
<csla:CslaDataProvider x:Key="ProjectList"
ObjectType="{x:Type lib:ProjectList}"
FactoryMethod="GetProjectList">
<csla:CslaDataProvider.FactoryParameters>
<system:String>Expert</system:String>
</csla:CslaDataProvider.FactoryParameters>
</csla:CslaDataProvider>
</Page.Resources>

The highlighted lines of XAML illustrate how to pass a single String parameter value to the
factory method. The value of the parameter is Expert, and that value will be

GetProjectList()

Using CSLA .NET 3.0


Copyright 2007 Rockford Lhotka

Page 53

passed as a parameter when GetProjectList() is called. In the case of the ProjectList business
object, this means the list will contain all projects with the word Expert in the project name.
Notice the use of the system: prefix when defining the <system:String> XAML element. You
can specify any type in the System namespace, as long as that type has a public constructor that can
accept the value provided in the element. This means you can specify most common types, including
numeric values, Boolean values, text values, GUID values and so forth.
The advantage of this approach is that it is simple, but obviously very limited since the parameter
value must be coded directly into the XAML.

Setting Parameters Through Data Binding


WPF data binding is used to do more than bind controls to objects. It can also be used to bind
controls to other controls, and this capability can be used to allow the user to dynamically supply
factory method parameter values to the CslaDataProvider control.
In the previous example, the factory parameter value was hard-coded in the XAML. Rather than
hard-coding the value, you can set the value using data binding from another control, such as a
TextBox. This can be done purely through XAML, with no need for code behind the form:
<Page x:Class="PTWpf.ProjectList"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:csla="clr-namespace:Csla.Wpf;assembly=Csla"
xmlns:system="clr-namespace:System;assembly=mscorlib"
xmlns:lib="clr-namespace:ProjectTracker.Library;assembly=ProjectTracker.Library"
Title="Project List">
<Page.Resources>
<csla:CslaDataProvider x:Key="ProjectList"
ObjectType="{x:Type lib:ProjectList}"
FactoryMethod="GetProjectList"
IsInitialLoadEnabled="False">
<csla:CslaDataProvider.FactoryParameters>
<system:String>&lt;enter name&gt;</system:String>
</csla:CslaDataProvider.FactoryParameters>
</csla:CslaDataProvider>
</Page.Resources>
<Grid DataContext="{Binding Source={StaticResource ProjectList}}">
<StackPanel FlowDirection="LeftToRight">
<StackPanel.Resources>
<Style TargetType="{x:Type TextBlock}">
<Setter Property="Margin" Value="3,5"/>
</Style>
<Style TargetType="{x:Type ListBox}">
<Setter Property="Margin" Value="3,5"/>
</Style>
</StackPanel.Resources>
<TextBlock>Projects:</TextBlock>
<DockPanel>
<TextBlock>Name:</TextBlock>
<TextBox Name="NameTextBox" AutoWordSelection="True">
<TextBox.Text>
<Binding Source="{StaticResource ProjectList}"
Path="FactoryParameters[0]"
BindsDirectlyToSource="true"
UpdateSourceTrigger="PropertyChanged">
</Binding>
</TextBox.Text>
</TextBox>
</DockPanel>
<ListBox Name="listBox1"
ItemsSource="{Binding}"
MouseDoubleClick="ShowProject"/>
Using CSLA .NET 3.0
Copyright 2007 Rockford Lhotka

Page 54

</StackPanel>
</Grid>
</Page>

Notice how the factory parameter is set to a temporary value. This value will be displayed to the
user, and must be non-empty. The reason it cant be empty is that the System.String type has no
public default constructor, which is exactly what WPF tries to invoke if the parameter value is empty.
However, the IsInitialLoadEnabled property of the CslaDataProvider control is set to False.
This prevents the control from attempting to create a business object when the control is first
initialized, so the temporary value in the factory parameter is essentially ignored. No business object
will actually be created until some property of the data provider control is changed, which will
trigger the control to refresh itself and to actually create a business object.
Later in the XAML, notice the highlighted TextBox control. This control is using data binding to
bind its Text property to the first factory parameter value of the ProjectList data provider control:
<TextBox.Text>
<Binding Source="{StaticResource ProjectList}"
Path="FactoryParameters[0]"
BindsDirectlyToSource="true"
UpdateSourceTrigger="PropertyChanged">
</Binding>
</TextBox.Text>

The Source property of the binding is set to the ProjectList resource: the data provider control
itself. The Path property of the binding is set to the first element of the FactoryParameters
collection.
The BindsDirectToSource property is required, and tells data binding to bind to the controls
properties rather than to the properties of the data returned by the control.
Finally, the UpdateSourceTrigger property indicates that the binding should be refreshed when
the property value is changed. Essentially, this means that the refresh will occur when the user tabs
off the TextBox control.
Using this technique, the value entered by the user is placed into the factory parameter by data
binding, which triggers a refresh of the data provider. The data provider then executes the factory
method, passing in the new parameter value and returning the resulting business object as the new
data source.

Setting Parameters in the Code Behind


Sometimes you want to provide parameters to the factory method programmatically. Perhaps you
even wish to change the name of the factory method to be invoked. To do this you need to write
some code behind the form.

Using Data Binding Without a Data Provider


You should know that it is possible to directly load a business object in code and to provide it as the
data source to a form. For example, consider the following XAML:
<Page x:Class="PTWpf.ProjectList"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Project List">
<Grid x:Key=mainGrid>
Using CSLA .NET 3.0
Copyright 2007 Rockford Lhotka

Page 55

<StackPanel FlowDirection="LeftToRight">
<StackPanel.Resources>
<Style TargetType="{x:Type TextBlock}">
<Setter Property="Margin" Value="3,5"/>
</Style>
<Style TargetType="{x:Type ListBox}">
<Setter Property="Margin" Value="3,5"/>
</Style>
</StackPanel.Resources>
<TextBlock>Projects:</TextBlock>
<ListBox Name="listBox1"
ItemsSource="{Binding}"
MouseDoubleClick="ShowProject"/>
</StackPanel>
</Grid>
</Page>

Notice that there is no data provider control, but the XAML is set up to use data binding. Behind
this form you can write code like this:
public partial class ProjectList : EditForm
{
public ProjectList()
{
InitializeComponent();
ProjectList list = ProjectList.GetList();
this.mainGrid.DataContext = list;
}
}

The highlighted lines of code show how to create a business object and set it as the data source for
the Grid control (and all controls it contains). The result of this code is basically the same as the
previous example where the business object was created by the CslaDataProvider control.
Since the result is the same, you might wonder why you would ever programmatically set the
factory parameters, or any other properties, on a data provider control.
I think the primary motivation to use the CslaDataProvider control in code is that the control has
an IsAsynchronous property that causes the business object to be created and loaded on a
background thread, and then provided to data binding once the object has been loaded.
Clearly, you can write similar code yourself, using the BackgroundWorker component, or
manually interacting with System.Threading objects. But why would you go to the work of writing
and debugging complex threading code when the work is already done for you by the data provider
control?

Setting CslaDataProvider Properties from Code-Behind


To programmatically set the factory method name and parameters, you must first define the data
provider control in your XAML. For example:
<csla:CslaDataProvider x:Key="Project"
ObjectType="{x:Type PTracker:Project}"
FactoryMethod="GetProject"
IsInitialLoadEnabled="False">
</csla:CslaDataProvider>

Notice that the XAML does not define any factory parameters at all. The code-behind will handle
that entirely on its own. However, since no parameters are provided, the IsInitialLoadEnabled
Using CSLA .NET 3.0
Copyright 2007 Rockford Lhotka

Page 56

property is set to False to prevent the control from trying to invoke the factory method without any
meaningful parameters.
The code-behind includes logic to determine whether a new object should be created, or an
existing object retrieved. Since creating an object and retrieving an object require calling two
different factory methods, the method name is set in the code as well:
void ProjectEdit_Loaded(object sender, RoutedEventArgs e)
{
Csla.Wpf.CslaDataProvider dp =
this.FindResource("Project") as Csla.Wpf.CslaDataProvider;
using (dp.DeferRefresh())
{
dp.FactoryParameters.Clear();
if (_projectId.Equals(Guid.Empty))
{
dp.FactoryMethod = "NewProject";
}
else
{
dp.FactoryMethod = "GetProject";
dp.FactoryParameters.Add(_projectId);
}
}
}

Lets look at this code piece by piece.


First, the code gets a reference to the CslaDataProvider instance defined by the XAML:
Csla.Wpf.CslaDataProvider dp =
this.FindResource("Project") as Csla.Wpf.CslaDataProvider;

Because the control is a resource of the form, the FindResource() method can be used to retrieve
it by name. Notice that the name used here matches the x:Key property set on the control in the
XAML.
The rest of the code is contained in a using block:
using (dp.DeferRefresh())
{
// ...
}

Data provider controls expose a DeferRefresh() method, that returns an object used to control
when the data provider refreshes its data. By default, every time you change a property on a data
provider control, it will refresh its data. However, if you are setting a series of properties on the
control, you probably dont want it to refresh until you are done setting all of them.
The DeferRefresh() method takes care of this, blocking any refreshes from occurring until the
DeferRefresh object is disposed at the end of the using block. Only when this object is disposed
does the data provider control refresh itself to create a new business object.
Inside the using block, the code clears the data provider controls factory parameter list:
dp.FactoryParameters.Clear();

The CslaDataProvider maintains the factory parameters as an ObservableCollection<object> ,


so you can manipulate the contents of that list as you would any other standard
Using CSLA .NET 3.0
Copyright 2007 Rockford Lhotka

Page 57

ObservableCollection<T>. By clearing

the list to start with, the code can add appropriate items as
needed to create or retrieve the business object.
In this case, a _projectId value of Guid.Empty is used to indicate that a new business object
should be created, a process that requires no parameters be passed to the factory method:
if (_projectId.Equals(Guid.Empty))
{
dp.FactoryMethod = "NewProject";
}

The FactoryMethod property is set to the name of the factory method that should be invoked to
create a new business object. Since no parameters are required for this method, the
FactoryParameters collection is left empty. This is equivalent to the following XAML:
<csla:CslaDataProvider x:Key="Project"
ObjectType="{x:Type PTracker:Project}"
FactoryMethod="GetProject">
</csla:CslaDataProvider>

If _projectId is not an empty GUID value, then the code changes the factory method name and
sets a parameter to retrieve an existing business object:
else
{
dp.FactoryMethod = "GetProject";
dp.FactoryParameters.Add(_projectId);
}

Notice that not only is the FactoryMethod property set to a method name, but an item is added to
the FactoryParameters collection. That value is passed to the static factory method as a parameter.
If your factory method requires multiple parameters, you would add them to the collection as
well. They are simply passed as parameters to the factory method in the order they are found in the
collection. The collection stores values as type object, so it is up to you to make sure that each value
has the correct type as expected by the method.
At this point, you should understand how to set the properties of a CslaDataProvider control, so
you can dynamically change the factory method name and parameters using code behind the form.

Managing the Object Lifetime


CSLA .NET business objects have a self-contained lifetime. In general terms an object is created or
retrieved, the user interacts with the object and then either cancels any changes, or saves their
changes.
The WPF data provider model includes the concepts necessary to create or retrieve an object, but
by default theres no concept of canceling or saving any changes. The CslaDataProvider control,
however, does implement those concepts.
This is important because it moves functionality from the code behind into XAML, making data
entry and edit forms more declarative.
To be clear, the CslaDataProvider control allows you to use XAML to create or retrieve an
object, bind it to controls for use by a user, and then cancel or save those changes. All of that can be
done without the need for anything but XAML.
Using CSLA .NET 3.0
Copyright 2007 Rockford Lhotka

Page 58

Enabling Lifetime Management


To enable this capability, you must set the ManageObjectLifetime property to True on your
CslaDataProvider resource declaration. For example:
<csla:CslaDataProvider x:Key="Project"
ObjectType="{x:Type PTracker:Project}"
FactoryMethod="GetProject"
IsInitialLoadEnabled="False"
ManageObjectLifetime="True">
</csla:CslaDataProvider>

This property tells the data provider control to do several things:


When a business object is created or retrieved, call BeginEdit() on that object
Support the ApplicationCommands.Undo command, and if that command is received, call
CancelEdit() on the business object
Support the ApplicationCommands.Save command, and if that command is received, call
ApplyEdit() and Save() on the business object, then refresh data bindings
Support the ApplicationCommands.New command, and if that command is received, and if
the business object implements IBindingList, and if IBindingList.AllowNew is true ,
then add a new item to the list
It is important to realize that when ManageObjectLifetime is True, the CslaDataProvider control
will automatically call BeginEdit() when it creates or retrieves a business object. This BeginEdit()
call occurs before the business object is provided to WPF data binding, so the object is already at edit
level one (or higher) by the time the user can interact with the object.
Because BeginEdit() has been called, if you decide to manually save or cancel any changes you
must call ApplyEdit() or CancelEdit() as appropriate.
However, in most cases you wont manually call those methods at all. Instead, you can use
standard WPF commanding to associate other controls, such as buttons, to those actions.
WPF provides a commanding model that allows controls to send commands to other controls,
either directly or indirectly. Normally a data provider control cannot receive commands, but the
CslaDataProvider control has a special property, CommandManager , that can receive commands on its
behalf.
When a command is routed to the data provider controls CommandManager, the command is
actually handled by the data provider control itself.

Declaring a Save Button


Heres how to declare a Save button, assuming the use of the Project data provider resource
discussed earlier:
<Button
Command="ApplicationCommands.Save"
CommandTarget="{Binding Source={StaticResource Project},
Path=CommandManager,
BindsDirectlyToSource=True}"
HorizontalAlignment="Left"
IsDefault="True">Save</Button>

Using CSLA .NET 3.0


Copyright 2007 Rockford Lhotka

Page 59

This control will automatically enable and disable based on the business objects IsSavable
property value. In other words, the Button control will only be enabled if the business object
provided by the CslaDataProvider control is both valid and dirty (changed).
When the Button control is clicked, it triggers the ApplicationCommands.Save command. The
target of that command is specified by the CommandTarget property, which is defined by a Binding
expression.
The CommandTarget propertys Binding expression specifies that the command should be routed to
the Project resource (our CslaDataProvider control), and to the CommandManager property of the
data provider. The BindsDirectlyToSource property indicates that the binding should connect to the
data provider control itself, rather than to the data object returned by the control.
When this command is received by the data provider control, the control does the following:
1. Call ApplyEdit() on the business object to commit any changes to the object in memory
2. Call the business objects Save() method to save the objects data to the database
3. Call BeginEdit() on the new business object returned from the

Save() method

4. Refresh data binding to use the new business object returned from the Save() method
In short, any changes to the business object are safely saved to the database, and data binding
automatically reflects any changes made to the object during the save process.

Declaring a Cancel Button


Heres how to declare a Cancel button, again assuming the use of the Project data provider
resource discussed earlier:
<Button
Command="ApplicationCommands.Undo"
CommandTarget="{Binding Source={StaticResource Project},
Path=CommandManager,
BindsDirectlyToSource=True}"
HorizontalAlignment="Left"
IsCancel="True">Cancel</Button>

This control will automatically enable and disable based on the business objects IsDirty
property value. In other words, the Button control will only be enabled if the business object
provided by the CslaDataProvider control is dirty (changed).
In this case, it is an ApplicationCommands.Undo command that is routed to the Project data
provider controls CommandManager property.
When this command is received by the data provider control, the control does the following:
1. Call CancelEdit() on the business object to roll back any changes to the object
2. Call BeginEdit() on the business object to prepare for future editing of the objects
properties
Any changes to the business object are undone, in memory, and the object remains ready for
editing.

Using CSLA .NET 3.0


Copyright 2007 Rockford Lhotka

Page 60

Declaring an Add New Button


If the business object is a list or collection that implements System.ComponentModel.IBindingList,
then it has the option of supporting automatic addition of new child objects. The IBindingList
interface defines an AllowNew property. If that property returns true, it indicates that the collection
has an override for AddNewCore() that will add a new child item to the collection on request.
Within the context of CSLA .NET, BusinessListBase implements IBindingList, and you can
define a business collection that adds new child items like this:
[Serializable]
public class Roles : BusinessListBase<Roles, Role>
{
protected override object AddNewCore()
{
Role item = Role.NewRole();
Add(item);
return item;
}
// ...
private Roles()
{
this.AllowNew = true;
}
// ...
}

The highlighted lines of code show the AddNewCore() method override and the setting of the
property to true .

AllowNew

With a collection that implements IBindingList , where AllowNew returns true , you can define an
Add new button like this:
<Button Name="AddItemButton"
Command="ApplicationCommands.New"
CommandTarget="{Binding Source={StaticResource RoleList},
Path=CommandManager,
BindsDirectlyToSource=True}"
HorizontalAlignment="Left">Add new</Button>

This control will automatically enable and disable based on whether the business object
implements IBindingList and whether the AddNew property returns true . The Button control will
only be enabled if the business object provided by the CslaDataProvider control supports adding
new items.
The XAML declaration is no different from the Save or Cancel buttons, except that the
command is ApplicationCommands.New .
When this command is received by CslaDataProvider, the control does the following:
1. Call IBindingList.AddNew() on the business object
This adds a new item to the collection, which is immediately displayed in the UI through data
binding thanks to the automatic ListChanged event raised when a new item is added.

Using CSLA .NET 3.0


Copyright 2007 Rockford Lhotka

Page 61

Handling Exceptions
Using declarative XAML programming is a powerful tool, but it is also abstract. The
CslaDataProvider control abstracts the creation, retrieval, canceling and saving of business objects
into compact XAML notation.
However, any of those operations could potentially throw an exception. This is especially true for
retrieval of existing data, and saving of data into the database. But if you arent in control of the
retrieval or saving of the object, how can you get access to any exception information?
There are two parts to the answer. First is the DataChanged event raised by the data provider
whenever the data object is created, retrieved or saved. Second is the Error property exposed by the
data provider.
In your code behind a form, you can declare a DataChanged event handler:
protected virtual void DataChanged(object sender, EventArgs e)
{
Csla.Wpf.CslaDataProvider dp = sender as Csla.Wpf.CslaDataProvider;
if (dp.Error != null)
MessageBox.Show(dp.Error.ToString(),
"Data error",
MessageBoxButton.OK,
MessageBoxImage.Exclamation);
}

The highlighted lines of code show the use of the Error property. This property will return null if
the last data provider operation was successful, otherwise it will return the Exception object
representing the failure.
In this example, I am simply displaying the exception detail to the end user. You may choose to
log the exception or take other steps as appropriate for your particular application.
Of course, you must hook the DataChanged event so it is handled by your DataChanged method as
shown by the highlighted lines of code:
public ProjectEdit()
{
InitializeComponent();
Csla.Wpf.CslaDataProvider dp =
this.FindResource("Project") as Csla.Wpf.CslaDataProvider;
dp.DataChanged += new EventHandler(DataChanged);
}

I add this code to the constructors of my forms, ensuring that any DataChanged events are properly
handled.

Binding a ComboBox to a Name/Value List


There are many different data binding scenarios, and ways to handle binding both through XAML
and through code-behind. Comprehensive coverage of WPF data binding is outside the scope of this
book, but binding a ComboBox (or similar) control to a business object that subclasses
NameValueListBase is so common that I want to address it here.
A ComboBox control typically binds to two data providers rather than just one. This complicates the
process a little.
Using CSLA .NET 3.0
Copyright 2007 Rockford Lhotka

Page 62

First, it means that you must define two data provider resources. One provides the data to populate
the ComboBox controls list of items, the other typically provides the business object that has a
property corresponding to that list of options.
In the ProjectTracker sample application, the resources assigned to a project have a role. So
there is a list of roles in an object called RoleList, and the ProjectResource object has a Role
property to specify which role that particular resource fills on the project.
The data providers are declared like this:
<csla:CslaDataProvider x:Key="RoleList"
ObjectType="{x:Type PTracker:RoleList}"
FactoryMethod="GetList"
IsAsynchronous="False" />
<csla:CslaDataProvider x:Key="Project"
ObjectType="{x:Type PTracker:Project}"
FactoryMethod="GetProject"
IsAsynchronous="False"
IsInitialLoadEnabled="False"
ManageObjectLifetime="True" />

The RoleList data provider returns a RoleList business object containing a name/value list of the
roles defined in the application. The Project data provider returns a Project business object that has
a child list of ProjectResource objects representing the resources assigned to the project.
The data template controlling the display of that collection of ProjectResource objects looks like
this:
<DataTemplate x:Key="lbTemplate">
<Grid>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Path=FullName}" Width="200" />
<TextBlock Text="{Binding Path=Assigned}" Width="100" />
<ComboBox
ItemsSource="{Binding Source={StaticResource RoleList}}"
DisplayMemberPath="Value"
SelectedValuePath="Key"
SelectedValue="{Binding Path=Role}"
Width="150" />
<Button Click="Unassign"
HorizontalAlignment="Left"
Tag="{Binding Path=ResourceId}">Unassign</Button>
</StackPanel>
</Grid>
</DataTemplate>

The highlighted lines show the ComboBox control.


Remember that the overall form is data bound to the Project object, and the ListBox control
using this data template is bound to the collection of ProjectResource objects. Each time this
template is applied, it is applied to a single ProjectResource object.
The ItemsSource property of the ComboBox control overrides the overall binding context, and
specifies that the list of items in the ComboBox should come from the RoleList resource.
The DisplayMemberPath property specifies that the user should actually see the contents of the
Value property of each item in the RoleList collection.

Using CSLA .NET 3.0


Copyright 2007 Rockford Lhotka

Page 63

The SelectedValuePath property tells the ComboBox control to privately keep track of the Key
property value for each item in the RolesList collection. The Key value is automatically matched
against the SelectedValue property to control which item is selected in the ComboBox.
The SelectedValue property indicates that the
property from the ProjectResource object.

ComboBox should

be data bound to the Role

What happens is that the ProjectResource objects Role property value is indexed into the Key
property of the items in RoleList to find the matching item. The Value property of that matching
item is displayed to the user in the ComboBox as the currently selected item.
If the user opens the ComboBox, theyll see all the Value values from the RoleList items. When
they click on an item, the Key value from the RoleList for that selected item is used to set the Role
property of the ProjectResource object.
You should now understand how to use the CslaDataProvider control to create, retrieve, cancel
and save business objects through XAML and code behind. Generally speaking, this control can be
used like any other data provider control; and you can use all the normal data binding techniques
available in WPF when interacting with the properties of your business objects.

Validator Control
Windows Forms provides an ErrorProvider control that interacts with data binding to automatically
display validation error information to the user on a per-property basis. This control drives off the
System.ComponentModel.IDataErrorInfo interface, and is very easy to use.
Web Forms in ASP.NET provides a set of validation controls, but they dont drive off the
business object at all. Instead, they end up replicating the validation business logic into the UI and
Presentation layers of the application. In my view, this is an unfortunate step backward.
WPF has a validation scheme somewhat similar to Web Forms, in that you can create validation
components that run within the UI, sitting between the UI controls and the data source and
automatically invoked by data binding. This model is not ideal, because it shifts the validation
business logic into the UI, meaning that it must be a duplicate of the logic in your business objects.
Fortunately, WPF has the infrastructure support needed to create something similar to the
Windows Forms ErrorProvider control. The Validator control in CSLA .NET provides this
functionality, interacting with the object through the same IDataErrorInfo interface, and providing a
standard, stylable visual effect to the UI.
For comparison purposes, Figure 8 shows the default Windows Forms ErrorProvider control,
with a tool tip that displays a human-readable reason the field is in error.

Figure 8. Default display of a Windows Forms ErrorProvider control.


Figure 9 shows the default

Using CSLA .NET 3.0


Copyright 2007 Rockford Lhotka

Validator

control, which surrounds the invalid field with a red border.

Page 64

Figure 9. Default display of a CSLA .NET Validator control in WPF.


Simply surrounding the control with a red border and no tooltip or other detail is the default style
provided by WPF. However, like most aspects of WPF, the visual appearance of the Validator
showing a field as invalid is highly customizable through styles, a topic Ill discuss later. First
though, lets discuss how to use the Validator control.

Using the Validator Control


As with all controls that come from an assembly, you must first define the Csla.Wpf namespace in
your XAML at the top of your form:
xmlns:csla="clr-namespace:Csla.Wpf;assembly=Csla"

The Validator control is a decorator, which means that it alters the visual appearance of the
control it contains. It can contain only one control, but that control can be a panel, allowing
Validator to effectively contain many controls.
The Validator control not only interacts with the control it contains, but it also interacts with its
data context: the business object to which the controls are bound. This means that the DataContext
must be set on the Validator control itself, or on a control that contains the Validator.
For example:
<csla:Validator DataContext="{Binding Source={StaticResource Project}}">
<StackPanel FlowDirection="LeftToRight">
<TextBlock>Id:</TextBlock>
<TextBlock Text="{Binding Id, Mode=OneWay}"></TextBlock>
<TextBlock>Name:</TextBlock>
<TextBox Name="NameTextBox"
Text="{Binding Name,
Converter={StaticResource IdentityConverter}}"></TextBox>
<TextBlock>Started:</TextBlock>
<TextBox Text="{Binding Started,
Converter={StaticResource IdentityConverter}}"></TextBox>
<TextBlock>Ended:</TextBlock>
<TextBox Text="{Binding Ended,
Converter={StaticResource IdentityConverter}}"></TextBox>
<TextBlock>Description:</TextBlock>
<TextBox Text="{Binding Description,
Converter={StaticResource IdentityConverter}}"
TextWrapping="Wrap"></TextBox>
</StackPanel>
</csla:Validator>

Notice that the DataContext is set directly on the Validator , which makes the business object the
data source for the Validator and all the controls it contains. More commonly, youll define the data
context at a higher level:

Using CSLA .NET 3.0


Copyright 2007 Rockford Lhotka

Page 65

<Grid DataContext="{Binding Source={StaticResource Project}}">


...
<csla:Validator>
<StackPanel FlowDirection="LeftToRight">
...

In this case the Validator will automatically use the data context provided by its container, the
Grid.
Simply wrapping a panel or other control with a Validator automatically causes the Validator to
change the visual style of any data bound control when the underlying business object reports a
validation error through the IDataErrorInfo interface. The CSLA .NET BusinessBase<T> class
implements this interface, and it is integrated with the validation rules concept, so no extra work is
required.
It is important to remember that the Validator control applies to the current data context. In the
example above, it applies to the Project business object to which the controls are data bound.
However, you may also use Validator in a template so it applies to each individual item in a
ListBox or other list control. For example:
...
<Grid.Resources>
<DataTemplate x:Key="lbTemplate">
<csla:Validator>
<StackPanel Orientation="Horizontal">
<TextBox
Text="{Binding FirstName,
Converter={StaticResource IdentityConverter}}"></TextBox>
<TextBox
Text="{Binding LastName,
Converter={StaticResource IdentityConverter}}"></TextBox>
</StackPanel>
</csla:Validator>
</DataTemplate>
...

Later in the XAML you would use this data template in a list control:
<ListBox DataContext="{Binding Source={StaticResource WorkGroup}}"
ItemsSource="{Binding Employees}"
ItemTemplate="{StaticResource lbTemplate}">
</ListBox>

Here, the DataContext is set explicitly on the ListBox, but it could come from a parent control.
The important thing to note is that the ItemsSource is set to a property representing a collection of
items from the WorkGroup object.
Each item in that Employees collection will be displayed according to the data template defined by
That means the data context each time the template is applied is a single child item from
the collection.
lbTemplate.

In other words, each item displayed in the ListBox has its own data context, and so the Validator
control operates against that child object.

Using a Custom Style for Controls in Error


The default visual effect applied for a property that has a validation error is to simply surround the
control with a red border as shown in Figure 9. This effect flows from the standard
Using CSLA .NET 3.0
Copyright 2007 Rockford Lhotka

Page 66

provided by WPF. While this is sufficient to indicate an issue, you will


probably want to provide a more interesting visual effect.
Validation.ErrorTemplate

To provide your own visual effect for a control with a validation error, you need to create your
own error template and set the controls Validation.ErrorTemplate property. In some cases, youll
also interact with the Validation.HasErrors property to implement a trigger to display a tooltip with
the validation error message.
Like virtually everything in WPF, this can be done through either XAML or code, but is typically
implemented purely in XAML.
Note: The ErrorTemplate is set on the visual controls, like TextBox, not on the
Validator control itself.
You can get information about the ErrorTemplate and the HasError properties from this MSDN
article:
http://msdn2.microsoft.com/en-us/library/system.windows.controls.validation.errortemplate.aspx.
There are several ways you can set the ErrorTemplate property, but perhaps the best approach is
to use an application-level style. To do this, open your projects Application.xaml file and add a
custom style within the <Application.Resources> element. For example:
<Style TargetType="{x:Type TextBox}">
<Setter Property="Validation.ErrorTemplate">
<Setter.Value>
<ControlTemplate>
<DockPanel>
<Border BorderBrush="Red" BorderThickness="3">
<AdornedElementPlaceholder />
</Border>
</DockPanel>
</ControlTemplate>
</Setter.Value>
</Setter>
<Style.Triggers>
<Trigger Property="Validation.HasError" Value="true">
<Setter Property="ToolTip"
Value="{Binding RelativeSource={x:Static RelativeSource.Self},
Path=(Validation.Errors)[0].ErrorContent}"/>
</Trigger>
</Style.Triggers>
</Style>

This style affects all TextBox controls, as noted by the TargetType property on the
The style sets the controls ErrorTemplate property:

Style element.

<Setter Property="Validation.ErrorTemplate">

The property is set to a ControlTemplate value:


<ControlTemplate>
<DockPanel>
<Border BorderBrush="Red" BorderThickness="3">
<AdornedElementPlaceholder />
</Border>
</DockPanel>
</ControlTemplate>

Using CSLA .NET 3.0


Copyright 2007 Rockford Lhotka

Page 67

The AdornedElementPlaceHolder element represents the original control within the template (in
this case the TextBox). It is important to realize that the template doesnt redisplay the original
control, but the placeholder allows the template to manipulate the space around the original control,
by adding a red border in this example.
The style also has a trigger, which displays a tool tip with the validation error text:
<Style.Triggers>
<Trigger Property="Validation.HasError" Value="true">
<Setter Property="ToolTip"
Value="{Binding RelativeSource={x:Static Relative Source.Self},
Path=(Validation.Errors)[0].ErrorContent}"/>
</Trigger>
</Style.Triggers>

The result is a display like that in Figure 10.

Figure 10. Validation error with custom styling.


You can see the thicker border and the new tool tip. The important thing to realize is that this type
of styling is a standard technique within WPF. The Validator control is simply tying into the existing
WPF validation infrastructure, so you can design your XAML like you would with any other type of
WPF validation technology.
Heres another, somewhat more complex, style that is more similar to the Windows Forms
in appearance:

ErrorProvider

<Style TargetType="{x:Type TextBox}">


<Setter Property="Validation.ErrorTemplate">
<Setter.Value>
<ControlTemplate>
<DockPanel LastChildFill="True">
<Grid DockPanel.Dock="Right" Margin="5,0,0,0" VerticalAlignment="Center">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="20"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="20"/>
</Grid.RowDefinitions>
<Ellipse
ToolTip="{Binding
Path=AdornedElement.ToolTip,
RelativeSource={RelativeSource
Mode=FindAncestor,
AncestorType={x:Type Adorner}}}">
<Ellipse.Fill>
<LinearGradientBrush
StartPoint="0,0"
EndPoint="1,1">
<GradientStop Color="White"
Offset="0" />
<GradientStop Color="Red"
Offset="1" />
</LinearGradientBrush>
Using CSLA .NET 3.0
Copyright 2007 Rockford Lhotka

Page 68

</Ellipse.Fill>
</Ellipse>
<TextBlock Text="!"
Foreground="White"
FontSize="16"
FontWeight="Bold"
HorizontalAlignment="Center"
ToolTip="{Binding
Path=AdornedElement.ToolTip,
RelativeSource={RelativeSource
Mode=FindAncestor,
AncestorType={x:Type Adorner}}}"/>
</Grid>
<AdornedElementPlaceholder />
</DockPanel>
</ControlTemplate>
</Setter.Value>
</Setter>
<Style.Triggers>
<Trigger Property="Validation.HasError" Value="true">
<Setter Property="ToolTip"
Value="{Binding RelativeSource={x:Static RelativeSource.Self},
Path=(Validation.Errors)[0].ErrorContent}"/>
</Trigger>
</Style.Triggers>
</Style>

The result of using this style is shown in Figure 11.

Figure 11. Validation error style similar to Windows Forms ErrorProvider.


Notice how the tool tip is associated with the original control, the ellipse and the ! character, so
the user will have many opportunities to see the reason the field is in error.
You should now understand how to use the Validator control to provide similar functionality to
the Windows Forms ErrorProvider control. This control allows you to completely encapsulate your
validation business logic in your business objects, and still achieve a very high level of interactivity
for the user.

Authorizer Control
The ReadWriteAuthorization control from the Csla.Windows namespace helps Windows Forms
developers build interfaces where the controls on the form alter their appearance based on whether
the user is authorized to read or write to the underlying business object property.
The Authorizer control in the Csla.Wpf namespace provides similar functionality for WPF. Like
the Validator control, Authorizer is a decorator, and it affects the appearance of the control it
contains. If you want it to affect the appearance of multiple controls, you can nest those controls
within a panel or other container control, inside the Authorizer control itself.
uses the Csla.Security.IAuthorizeReadWrite interface to interact with the business
object. It uses this interface to determine whether the user is authorized to read or write to each
business object property that is data bound to a control contained within the Authorizer control.
Authorizer

Using CSLA .NET 3.0


Copyright 2007 Rockford Lhotka

Page 69

Using the Authorizer Control


As with the Validator control, you must first reference the Csla.Wpf namespace in your XAML
before you can use the control:
xmlns:csla="clr-namespace:Csla.Wpf;assembly=Csla"

You can then use Authorizer to control read and write access to the detail controls on your form.
The control is used to wrap, or decorate, another control:
<csla:Authorizer Name="AuthPanel">
<csla:Validator>
<StackPanel FlowDirection="LeftToRight" Margin="0 0 30 0">
<TextBlock>First name:</TextBlock>
<TextBox Text="{Binding FirstName,
Converter={StaticResource IdentityConverter}}"></TextBox>
<TextBlock>Last name:</TextBlock>
<TextBox Text="{Binding LastName,
Converter={StaticResource IdentityConverter}}"></TextBox>
</StackPanel>
</csla:Validator>
</csla:Authorizer>

In this case, it decorates a Validator control. The Validator control ,in turn, decorates a
contains the UI controls actually displayed to the user.

StackPanel that

Like the Validator control, the Authorizer control can be used at a form level, or within a
template that is applied to each item in a ListBox or other list control.

Forcing a Manual Refresh


In the previous example, notice that the Authorizer controls Name property is set to a value. This
allows the code behind the form to interact with the Authorizer control by name. For example, this
code forces a refresh of the authorization rules:
this.AuthPanel.Refresh();

The Authorizer control automatically refreshes authorization rules when the form loads or when
its data context changes. However, you may need to manually force a refresh at times, most notably
when the currently logged in user changes while the form is open.
I typically implement a method called ApplyAuthorization() in each form, that looks similar to
this:
private void ApplyAuthorization()
{
this.AuthPanel.Refresh();
if (Resource.CanEditObject())
{
this.ProjectListBox.ItemTemplate =
(DataTemplate)this.MainGrid.Resources["lbTemplate"];
this.AssignButton.IsEnabled = true;
}
else
{
this.ProjectListBox.ItemTemplate =
(DataTemplate)this.MainGrid.Resources["lbroTemplate"];
((Csla.Wpf.CslaDataProvider)this.FindResource("Resource")).Cancel();
this.AssignButton.IsEnabled = false;
}
Using CSLA .NET 3.0
Copyright 2007 Rockford Lhotka

Page 70

The Authorizer is refreshed, which refreshes the display of all detail controls in the form.
The code also checks to the see if the current user is authorized to edit the business object, and if
so, it changes some other aspects of the UI. It does this by using an editable template for a ListBox
control and enabling a button.
If the user is not authorized to edit the business object, the code changes the ListBox to use a
read-only template for display and disables a button. But more importantly, as indicated by the
highlighted line of code, the CslaDataProvider controls Cancel() method is invoked to force a
CancelEdit() on the business object. This causes the business object to undo any changes made
while the user was able to edit, so the user now sees the original object in its unedited state.

Read Authorization
If the user is not authorized to read the property bound to a UI control, the UI control is collapsed,
hidden or left unchanged. The NotVisibleMode attached dependency property is used to determine
what happens, but the default is to collapse the UI control. Table 8 lists the options for
NotVisibleMode.

Option
Collapsed

Description
If the user is not authorized to read a property bound to this control,
the control is collapsed. This means the control is not visible, and
also consumes no space in the UI.

Hidden

If the user is not authorized to read a property bound to this control,


the control is hidden. This means the control is not visible, but still
consumes the space it would need were it visible.

Ignore

No change is made to the appearance of the control, even if the user


is not authorized to read the property value. Remember that the
business object will refuse to provide a valid value, so even though
the user can see the control, they wont normally see the property
value in the display.

Table 8. Options for the NotVisibleMode property.


The following shows an example of how to set the NotVisibleMode property:
<csla:Authorizer Name="AuthPanel">
<csla:Validator>
<StackPanel FlowDirection="LeftToRight" Margin="0 0 30 0">
<TextBlock>First name:</TextBlock>
<TextBox csla:Authorizer.NotVisibleMode=Hidden
Text="{Binding FirstName,
Converter={StaticResource IdentityConverter}}"></TextBox>
<TextBlock>Last name:</TextBlock>
<TextBox csla:Authorizer.NotVisibleMode=Ignore
Text="{Binding LastName,
Converter={StaticResource IdentityConverter}}"></TextBox>
</StackPanel>
</csla:Validator>
</csla:Authorizer>

Using CSLA .NET 3.0


Copyright 2007 Rockford Lhotka

Page 71

The first TextBox control is changed so it is hidden if the user is not authorized to read the value.
The second TextBox control will be ignored, so no change will be made to its appearance regardless
of whether the user is authorized to read the property or not. Remember that the default is to collapse
the control, both hiding it and making it consume no space in the UI layout.
It is important to remember that the way the CanReadProperty() method is used in Expert C#
2005 Business Objects, when implementing a property get block ,will throw an exception if the user
is not authorized to read the property. Unfortunately, there is no way to prevent WPF data binding
from attempting to read the property value from the object, even if the UI control is collapsed or
hidden. Due to this, you should implement your property get block to return a dummy value if read
access is not authorized:
public string FirstName
{
get
{
if (CanReadProperty("FirstName"))
return _firstName;
else
return "n/a";
}
}

This way, no exception is thrown. If you are using the Authorizer control the user wont actually
see the dummy value, because the control will be collapsed or hidden.

Write Authorization
If the user is not authorized to write to a property that is data bound to a UI control, the UI control is
set to read-only mode, or entirely disabled.
If the control has an IsReadOnly property then that property is set to true . If the control has no
then IsEnabled is set to false; which always works because the base control
type in WPF implements IsEnabled , so it is available on all UI controls.
IsReadOnly property,

The end result is that the user will be unable to enter data into the UI control if they arent
authorized to write to the underlying business object property.
You should now understand how to use the Authorizer control to automatically alter the
appearance of UI controls based on whether the user is authorized to read or write to the business
object property to which each UI control is data bound.

ObjectStatus Control
Editable CSLA .NET business objects that subclass BusinessBase<T> have a set of valuable status
properties. These properties are not available for data binding because they are marked with the
[Browsable(false)] attribute, and because they dont raise the PropertyChanged event when they
change.

Using CSLA .NET 3.0


Copyright 2007 Rockford Lhotka

Page 72

Property
IsNew

Description
Indicates whether the object corresponds to an existing primary key
value in the database or not.

IsDirty

Indicates whether the objects properties have been changed.

IsDeleted

Indicates whether the object has been marked for deletion.

IsValid

Indicates whether the object has any broken validation rules.

IsSavable

Indicates whether the object is both valid and dirty.

Table 9. Status properties from BusinessBase<T>.


Sometimes, you may need access to these properties within your XAML code. You might, for
example, want to enable or disable certain controls on the form based on whether the objects
IsSavable property returns true or false.
The ObjectStatus control from the Csla.Wpf namespace exposes these properties as bindable
properties from a WPF control. The ObjectStatus control takes the properties from its current
DataContext and exposes them as dependency properties so they can be used in control-to-control
data binding. Additionally, the ObjectStatus control includes code to detect when each of the status
properties has changed, so it can raise appropriate PropertyChanged events for them.

Using the ObjectStatus Control


Using the ObjectStatus control is much like using Validator or Authorizer. First you need to
define the Csla.Wpf namespace in your XAML:
xmlns:csla="clr-namespace:Csla.Wpf;assembly=Csla"

Then you can use the control in your form. The ObjectStatus control is a decorator, and so it
contains another control. For example:
<csla:ObjectStatus>
<StackPanel>
...
</StackPanel>
</csla:ObjectStatus>

Remember that the ObjectStatus control requires a DataContext to work. You can set the
DataContext directly on the ObjectStatus control, or on a control that contains the ObjectStatus
control. This is no different than how you use Validator or Authorizer.
Controls inside the ObjectStatus control can use the properties it provides. These are the same
properties as listed in Table 9. Here is an example of using these properties to display the status of
the business object. You can see the resulting output near the bottom of the form shown in Figure 12:

Using CSLA .NET 3.0


Copyright 2007 Rockford Lhotka

Page 73

<csla:ObjectStatus>
<StackPanel Orientation="Horizontal">
<CheckBox IsEnabled="False"
IsChecked="{Binding RelativeSource={RelativeSource FindAncestor,
AncestorType=csla:ObjectStatus, AncestorLevel=1},
Path=IsSavable}">IsSavable</CheckBox>
<CheckBox IsEnabled="False"
IsChecked="{Binding RelativeSource={RelativeSource FindAncestor,
AncestorType=csla:ObjectStatus, AncestorLevel=1},
Path=IsValid}">IsValid</CheckBox>
<CheckBox IsEnabled="False"
IsChecked="{Binding RelativeSource={RelativeSource FindAncestor,
AncestorType=csla:ObjectStatus, AncestorLevel=1},
Path=IsDirty}">IsDirty</CheckBox>
<CheckBox IsEnabled="False"
IsChecked="{Binding RelativeSource={RelativeSource FindAncestor,
AncestorType=csla:ObjectStatus, AncestorLevel=1},
Path=IsNew}">IsNew</CheckBox>
<CheckBox IsEnabled="False"
IsChecked="{Binding RelativeSource={RelativeSource FindAncestor,
AncestorType=csla:ObjectStatus, AncestorLevel=1},
Path=IsDeleted}">IsDeleted</CheckBox>
</StackPanel>
</csla:ObjectStatus>

Figure 12. ObjectStatus data bound to CheckBox controls.


The important thing to notice about this code is the way the IsChecked propertys Binding
expression is defined:
IsChecked="{Binding RelativeSource={RelativeSource FindAncestor,
AncestorType=csla:ObjectStatus, AncestorLevel=1},
Path=IsDeleted}">IsDeleted</CheckBox>

This is a RelativeSource binding, where the binding looks for an ancestor of the current control
(a control that ultimately contains the current control). The ancestor must be of type
csla:ObjectStatus, and it will stop at the first instance of ObjectStatus it finds.
Once data binding has found the first instance of an ObjectStatus control, it binds to the
IsDeleted property, based on the Path property value.
You can use this technique to bind the properties to any other controls Boolean property.

Using CSLA .NET 3.0


Copyright 2007 Rockford Lhotka

Page 74

IdentityConverter Control
WPF data binding has one obscure issue that can cause trouble in certain cases. If your property set
block alters the value of the inbound data, any changes you make will not be shown in the UI until
some other property is changed. This can lead to some very inconsistent UI behaviors that can
confuse your users.
For example, consider this property set block:
public string ProductNumber
{
get
{
if (CanReadProperty("ProductNumber"))
return _productNumber;
else
return string.Empty;
}
set
{
CanWriteProperty("ProductNumber", true);
if (string.IsNullOrEmpty(value)) value = string.Empty;
if (_productNumber != value)
{
_productNumber = value.ToUpper();
PropertyHasChanged("ProductNumber");
}
}
}

The highlighted line of code is important, because it changes the incoming value to be all upper
case. This is entirely valid code, but it can cause problems with WPF data binding.
When the user types a value into a data bound control, such as a TextBox, their new value is
placed into the business objects property. The property, in this example, changes the value and
keeps the changed value. It also raises the PropertyChanged event (due to the PropertyHasChanged()
method call). This event tells data binding that the property has changed.
However, as an optimization, data binding does not refresh the current control. It refreshes all
other controls on the form, but not the current control. The erroneous assumption is that property set
blocks never change values.
Fortunately, theres a solution to this problem.
WPF has a concept called a value converter that can be used to format values as data binding
moves them from the data source to the UI control and from the UI control to the data source. Value
converters are very useful, especially when dealing with date values, because they allow the UI
developer to control how the value appears to the user, or to parse complex user input into something
the business object can understand.
It turns out that when a value converter is associated with a UI control, data binding always
refreshes the display. It has to refresh the display, because data binding has no way of knowing what
the value converter might do to the value as it moves from the data source to the UI control.
In other words, as long as you attached a value converter to your UI control, the control will
refresh its display as part of the data binding process. In most cases, however, you dont actually
want to change the value from the business object property, you just want to refresh the display.

Using CSLA .NET 3.0


Copyright 2007 Rockford Lhotka

Page 75

The IdentityConverter value converter from the Csla.Wpf namespace can be used to force UI
controls to update their display without changing the value from the business object.
Note: The word identity in this case comes from standard arithmetic where 0 is the
identity for addition (anything plus 0 is the original number), and 1 is the identity for
multiplication (anything times 1 is the original number).

Using the IdentityConverter Control


As with all other CSLA .NET WPF controls, you must first define the Csla.Wpf namespace in your
XAML:
xmlns:csla="clr-namespace:Csla.Wpf;assembly=Csla"

You must also define the value converter as a resource in your form:
<Page.Resources>
<csla:IdentityConverter x:Key="IdentityConverter" />
<Page.Resources>

Then you can use the IdentityConverter value converter in your XAML. For example:
<csla:ObjectStatus>
<csla:Authorizer Name="AuthPanel">
<csla:Validator>
<StackPanel FlowDirection="LeftToRight" Margin="0 0 30 0">
<TextBlock>First name:</TextBlock>
<TextBox csla:Authorizer.NotVisibleMode=Hidden
Text="{Binding FirstName,
Converter={StaticResource IdentityConverter}}"></TextBox>
</StackPanel>
</csla:Validator>
</csla:Authorizer>
</csla:ObjectStatus>

The Converter property is used in a Binding expression to specify the value converter that should
be used by data binding as the data value moves between the UI control and the data source. The
highlighted line of code specifies that the IdentityConverter should be used.
The result is that the value is not changed as it moves between the UI control and the data source,
but data binding will always refresh the UI controls contents when the underlying business object
property changes.
If you do have a value converter that changes the value, thats fine. You would just use that value
converter instead of IdentityConverter. The IdentityConverter control is intended to be used in
cases where you dont otherwise need a value converter, but want the UI controls to refresh their
display as users would expect.
Note: If you know that a property set wont change the value, there is no need to use
an IdentityConverter when binding to that property.
At this point, you should have a good understanding of how CSLA .NET supports WPF
development, through the CslaDataProvider , Validator , Authorizer, ObjectStatus and
IdentityConverter controls.
Using CSLA .NET 3.0
Copyright 2007 Rockford Lhotka

Page 76

Chapter 3:
Workflow
Another pillar of Microsoft .NET 3.0 is the Windows Workflow Foundation (WF). WF is a basic
workflow engine, but it also comes with a designer experience that integrates into Visual Studio.
In this chapter, I assume you have a basic understanding of WF, and know how to use the
workflow designer in Visual Studio to lay out a workflow. I also assume you are familiar with the
concept of creating a workflow in an external assembly that may also include custom activities.
It is important to realize that workflow, in general, is a procedural programming model. You
might think that theres little need for object-oriented concepts in the workflow world, since
procedural programming and object-oriented programming are quite different. That isnt entirely the
case.
Certainly, the workflow itself is procedural: workflows are basically anima ted flowcharts after all.
But an object-oriented application can invoke a workflow, and workflow activities can be
constructed using objects behind the scenes.
A workflow activity is, by definition, a self-contained unit of functionality with defined inputs and
outputs. This is also the basic definition of a use case. As a result, you can view a workflow activity
as a use case! This is exciting, because creating workflow activities using object-oriented design
concepts is as close as most of us will ever get to doing pure object design, without all the
complexities of user interaction and so forth.
Figure 13 illustrates, at a high level, one architectural view of using both objects and workflows.

Using CSLA .NET 3.0


Copyright 2007 Rockford Lhotka

Page 77

Figure 13. High level architecture using objects and workflows.


At the top of the diagram is your normal application, with its presentation, UI, business data
access and data storage layers. In this case however, the business layer invokes a workflow through a
command object. The workflow itself (in the lower left of the diagram) executes a series of activities.
Each activity is implemented using business objects, which in turn invoke data access and data
storage layers.
The important thing to keep in mind with Figure 13 is the level of encapsulation involved. To the
applications business layer, the workflow is a black box that performs some useful operation. To the
workflow, each activity is a black box and performs a specific task. Each activity relies on business
objects to implement its task, and the activity should view these objects as black boxes.
Maintaining this level of encapsulation is important, as it provides maximum flexibility and reuse.
It allows you to change the workflow without impacting (directly at least) the calling application. It
allows you to change the implementation of an activity without impacting (directly) the workflow or
the calling application. Finally, it allows you to change the implementation of the business objects
used by an activity without impacting the activity, the workflow or the calling application.
Pragmatically, most applications have two general types of functionality. Theres the part that
interacts with the user, display and collecting data. Then theres the part that performs noninteractive back-end processing, typically once the user is done entering their data.
Generally speaking, workflow can be very useful in implementing that non-interactive back-end
processing. You can use WF to run a workflow on a background thread, and sometimes it is easier to
visualize complex processing using the flowchart-style designer than directly through code.

Using CSLA .NET 3.0


Copyright 2007 Rockford Lhotka

Page 78

Building Activities using Objects


A workflow activity is, at its core, simply a procedure that performs some pre-defined action. When
you implement an activity you are really just implementing an Execute() method of some sort.
Within that method you can design your code however you choose: using linear, procedural,
modular or object-oriented concepts. For activities of any complexity, using object-oriented concepts
can be a good idea, because you gain the benefits of object-oriented design and programming.
Additionally, when using CSLA .NET you can use CSLA .NET objects within your activity
implementation. The value of doing this is that you are able to leverage all the benefits of CSLA
.NET, like encapsulated validation, authorization and persistence.
In fact, from the perspective of your business objects, a workflow activity is merely another type
of interface. Its not much different from a Windows, Web, WPF, web service or WCF service
interface. As shown in Figure 14, the workflow activity is, architecturally, merely another UI layer.

Figure 14. A workflow activity as a UI relative to the business layer.


Looking at it this way, an activity is very similar to a web service or WCF service and the type of
code youd write in an asmx page or in the code behind a svc file.
Each activity has a clearly defined set of inputs and outputs, and should have a clearly defined
task to fulfill. To get any sort of reuse of activities, your activities must be atomic units of
functionality.
The presentation layer in this architecture is the interface exposed by the activity to the
workflow. This interface exists as:
1. The Execute() method of the activity (invoked by the workflow).
2. The dependency properties exposed by the activity, allowing data to flow into and out of the
activity.
3. The dependency events exposed by the activity, allowing the activity to provide notification of
its actions.
The code in the activity itself, in the Execute() method, is the UI layer. The role of this code is
not unlike the role of the code in an asmx or behind a svc file in a WCF service: it exists to invoke the
business objects that do the real work, and to act as intermediary between the interface (dependency
Using CSLA .NET 3.0
Copyright 2007 Rockford Lhotka

Page 79

properties and events) and the business objects. You should think of the code in the activity as an
encapsulation layer that hides the business objects used to implement the activity.

Implementing a Code Activity


Figure 15 shows a simple sequential workflow for closing a project in the ProjectTracker
application.

Figure 15. Sequential workflow to close a project.


The project is closed, and then the resources associated with both the project and the projects
sponsor are notified in parallel. Each of these activities are code activities, meaning that in the codebehind the workflow designer each of them has an ExecuteCode() method to provide the activitys
implementation.
The closeProject activity is implemented like this:
private void closeProject_ExecuteCode(object sender, EventArgs e)
{
Project project = Project.GetProject(this.ProjectId);
project.Ended = DateTime.Today.ToString();
project.Save();
}

Notice how the highlighted lines of code are simply using the Project class from the
ProjectTracker.Library project. This is very similar to the code youd expect to find in a WCF

Using CSLA .NET 3.0


Copyright 2007 Rockford Lhotka

Page 80

service to close a project, reinforcing how a workflow activity is really just another type of interface
to your business layer.
Again, each activity should be viewed as an atomic task, and so it should have little or no
dependencies on prior activities, and should leave no state behind that could be used or misused by
subsequent activities.
The input for this activity is the ProjectId dependency property, which is global to the workflow:
private static DependencyProperty ProjectIdProperty = DependencyProperty.Register(
"ProjectId", typeof(Guid), typeof(ProjectWorkflow), null);
public Guid ProjectId
{
get { return (Guid)base.GetValue(ProjectIdProperty); }
set { base.SetValue(ProjectIdProperty, value); }
}

As youll see later in this chapter, the ProjectId value is set by the code that invokes the
workflow itself, so it is considered a required input to the workflow.
Due to the use of the dependency property, the closeProject implementation has no requirements
on any prior activities, and leaves no state behind for subsequent activities. The result is that this
activity can be used anywhere in the workflow, as long as ProjectId contains a valid value.

State and Performance


We dont live in an ideal world. The fact is that there is a potentially large performance cost to
making every activity atomic and self-contained, especially if several activities in the workflow
require the same information.
For example, the workflow in Figure 15 closes the project, and then notifies people about the
closure. The activities to notify people may very well need access to the project information
contained in the Project object used in the closeProject activity.
If closeProject is implemented as shown above, the Project object instance is gone before those
other activities execute, and so each of them must independently reload that same information from
the database.
To achieve maximum reuse, maintainability and flexibility, thats exactly what you should do. But
the performance cost of reloading the same information over and over may be prohibitive. You must
decide whether flexibility or performance is more important for your workflow.
If you decide on performance, then you must keep some or all of the state from one activity for
use by later activities. The impact of this is that the activities become dependent on each other, and
you must document which activities rely on previous ones so someone in the future doesnt rearrange
the workflows activities into an invalid configuration.
To help make the workflow self-documenting, you might choose to break some aspects of an
activity into multiple activities. For example, Figure 16 shows a revised workflow that provides some
clarity of intent.

Using CSLA .NET 3.0


Copyright 2007 Rockford Lhotka

Page 81

Figure 16. Revised workflow to close a project.


This workflow has an extra getProject activity that clearly gets the Project object from the
database:
private Project _project;
private void getProject_ExecuteCode(object sender, EventArgs e)
{
_project = Project.GetProject(this.ProjectId);
}

Notice that _project is a private field of the workflow itself, so when the object is loaded it is
available to subsequent activities within the workflow.
You could choose, instead, to create a dependency property. This makes the Project object
available for data binding to activity dependency properties through the designer. In that case, the
code looks like this:
private static DependencyProperty ProjectProperty = DependencyProperty.Register(
"Project", typeof(Project), typeof(ProjectWorkflow), null);
public Project Project
{
get { return (Project)base.GetValue(ProjectProperty); }
Using CSLA .NET 3.0
Copyright 2007 Rockford Lhotka

Page 82

set { base.SetValue(ProjectProperty, value); }


}
private void getProject_ExecuteCode(object sender, EventArgs e)
{
this.Project = Project.GetProject(this.ProjectId);
}

The closeProject and other activities can then make use of this pre-loaded object. Heres the
new closeProject activity code:
private void closeProject_ExecuteCode(object sender, EventArgs e)
{
this.Project.Ended = DateTime.Today.ToString();
this.Project = this.Project.Save();
}

A private field is simpler and incurs less overhead, and is perfectly adequate when all your
activities are code activities implemented directly in the workflow. However, if you use activities
from external assemblies, you may want the more advanced data binding features enabled by storing
the value in a dependency property.
Pay special attention to the Save() method call:
this.Project = this.Project.Save();

Notice how the result of this method is used to replace the value of the workflow-level Project
value. Remember that the CSLA .NET Save() method often returns an updated version of the object,
and since this object may be used by later activities in the workflow, it is important to update the
global value.

Implementing an External Activity


The previous example shows how to create a simple code activity by implementing the
ExecuteCode() method within the workflows code behind. In some cases youll want to create
activities that can be reused across multiple workflows, and to do that the activity must be
implemented in a separate assembly that is referenced by each workflow.
There are many ways to implement external activities, and a discussion of all of them is outside
the scope of this book. The most common implementation uses a graphical designer to define the
external activity as though it were a sequential workflow. This approach allows you to implement
sub-activities within the activity, using standard workflow development techniques; including the use
of code activities.
However, when building external activities you need to deal with state management issues, most
notably getting data between your external activity and the workflow itself. That is the issue Ill
focus on in this section as I show how to implement getProject and closeProject as external
activities.
The primary method you can use to communicate values into and out of an external activity is
through dependency properties. Dependency properties declared on an activity are available for data
binding within the workflow, and are available to the code in your ExecuteCode() methods as well.
The following code is located in the PTWfActivities project in the ProjectTracker application.
The PTWfActivities project references both ProjectTracker.Library and Csla , and so it has access
Using CSLA .NET 3.0
Copyright 2007 Rockford Lhotka

Page 83

to both the CSLA .NET framework and the business objects common to all ProjectTracker
applications.

Implementing a SequentialActivity
By default, when you add an activity to a workflow project or a workflow activity library, youll get
a SequentialActivity. This type of activity is like a mini-workflow, and you can use the normal
workflow designer to add sub-activities within the activity to create its implementation.
In other words, the activity designer in Visual Studio helps you implement activities using
procedural design. And sometimes thats fine, but you may want to implement your activity using
business objects instead. The simplest way to do this is to drag a single Code Activity onto the
designer as shown in Figure 17.

Figure 17. Using a code activity within a SequentialActivity.


The code behind the CloseProject activity looks like this:
public partial class CloseProject : SequenceActivity
{
public CloseProject()
{
InitializeComponent();
}
private static DependencyProperty ProjectProperty = DependencyProperty.Register(
"Project", typeof(Project), typeof(CloseProject), null);
public Project Project
{
get { return (Project)base.GetValue(ProjectProperty); }
set { base.SetValue(ProjectProperty, value); }
}
private void doClose_ExecuteCode(object sender, EventArgs e)
{
this.Project.Ended = DateTime.Today.ToString();
this.Project = this.Project.Save();
}
}

The activity defines a dependency property, Project, so the workflow can provide the activity
with an instance of a Project object to close. The doClose code activitys ExecuteCode() method
then uses this value to do its work:
this.Project.Ended = DateTime.Today.ToString();
this.Project = this.Project.Save();

Using CSLA .NET 3.0


Copyright 2007 Rockford Lhotka

Page 84

This is the same code as in the earlier implementation, but now it is encapsulated in an activity
that is implemented entirely outside the workflow. The result is that this activity can be used by any
workflow that can set the Project dependency property to a valid value.

Implementing an Activity
Another way you can implement an external activity is by adding a SequentialActivity and then
changing the base class from which it inherits. You must change the base class before doing anything
else to the activity or you can confuse the Visual Studio activity designer:
public partial class GetProject: Activity

Figure 18 shows how the designer looks once youve changed this base class.

Figure 18. Visual Studio activity designer showing a simple Activity.


With this change, you can no longer drag and drop sub-activities onto the designer surface. This is
a simpler and more direct type of activity, and is better suited to an object-oriented implementation.
To implement the code within the activity, you override the activitys Execute() method:
protected override ActivityExecutionStatus Execute(
ActivityExecutionContext executionContext)
{
this.Project = Project.GetProject(this.ProjectId);
return base.Execute(executionContext);
}

As with the CloseProject activity, this activity also implements dependency properties:
private static DependencyProperty ProjectIdProperty = DependencyProperty.Register(
"ProjectId", typeof(Guid), typeof(GetProject), null);
public Guid ProjectId
{
get { return (Guid)base.GetValue(ProjectIdProperty); }
set { base.SetValue(ProjectIdProperty, value); }
}
private static DependencyProperty ProjectProperty = DependencyProperty.Register(
"Project", typeof(Project), typeof(GetProject), null);
public Project Project
{
get { return (Project)base.GetValue(ProjectProperty); }
set { base.SetValue(ProjectProperty, value); }
}

The ProjectId property is an input, and provides the activity with the Guid value that identifies
the project to get. The Project property exposes the resulting Project object to the workflow and
any other activities within the workflow.
Again, notice how the actual implementation is the same as before:
Using CSLA .NET 3.0
Copyright 2007 Rockford Lhotka

Page 85

this.Project = Project.GetProject(this.ProjectId);

But the activitys implementation is now encapsulated into a separate location from the workflow.
Any workflow can use this activity to load a Project, as long as it can provide a valid ProjectId .

Using External Activities in a Workflow


Returning to the PTWorkflow application, which now references the PTWfActivities project, the
workflow looks like Figure 19.

Figure 19. Workflow using external activities.


Notice the difference in appearance between the getProject and CloseProject activities.
The getProject activity appears as a single, simple shape on the diagram, clearly indicating that it
is a standalone activity. It is a black box.
The closeProject activity can be expanded or collapsed, but when it is expanded the workflow
designer shows its internal implementation. The developer building the workflow cant change
closeProject, but they can see how it works, and so this activity offers a different level of
transparency. It is more of a gray box.

Using CSLA .NET 3.0


Copyright 2007 Rockford Lhotka

Page 86

Binding the Dependency Properties


If you select the getProject1 activity, you can see both the Project and ProjectId dependency
properties displayed in the activitys Properties dialog as shown in Figure 20.

Figure 20. Properties for the getProject1 activity.


You can see that a value has been specified for ProjectId . When you select a dependency
property in the Properties dialog, you can click a button on the right of the property value to
bring up a dialog where you can bind the property to another dependency property from the
workflow or other activities within the workflow. Figure 21 shows how this dialog was used to bind
getProject1.ProjectId to the workflows ProjectId property.

Figure 21. Binding getProject1.ProjectId to the workflows ProjectId.


Remember that the getProject1.Project property is an output property. Once the activity has
executed, that property will return the Project object that was retrieved.

Using CSLA .NET 3.0


Copyright 2007 Rockford Lhotka

Page 87

As shown in Figure 22, the Project property is bound to getProject1.Project for the
closeProject1 activity.

Figure 22. Binding closeProject1.Project to getProject1.Project.


Because the Project object is now globally available from the getProject1 activity, the
workflow itself no longer needs to implement a Project dependency property. All activities needing
to use a Project object can get that value from the getproject1.Project dependency property,
either through data binding, or directly through code.
For example, the notifyResources code activity uses the value through code:
private void notifyResources_ExecuteCode(object sender, EventArgs e)
{
foreach (ProjectResource resource in this.getProject1.Project.Resources)
{
// notify each resource
}
}

Directly using the properties of activities simplifies the code in the workflow itself.
You should now understand how to use business objects when implementing simple code
activities, SequentialActivity objects or Activity objects. You should also understand how to
declare and use dependency properties to pass values and objects into and out of activities, through
data binding or directly through code.

Business Objects as Workflow or Activity State


In the previous examples, the workflow or activities had a dependency property called Project. It is
important to realize that any dependency property values are serialized and persisted (typically in a
database) if the workflow is unloaded before it completes. This means that the Project object
instance exposed by these Project properties may be serialized!
Thats OK, because all CSLA .NET style business objects must be Serializable, and so the
serialization will work fine. The fields in your object, and any child objects, will be serialized into a
byte stream, which is then stored as part of the workflows state.
When the workflow is later loaded back into memory, its state is deserialized. Your business
object is part of this state, so it is deserialized as well. This deserialization occurs before the
workflow resumes running.
Using CSLA .NET 3.0
Copyright 2007 Rockford Lhotka

Page 88

There are two issues you need to consider. First, the WF engine uses the BinaryFormatter to
serialize and deserialize the data. This means that you cant use the DataContract attribute on your
business objects if you intend on exposing them through dependency properties. Instead, you must
continue to use the Serializable attribute.
Second, and more importantly, what you need to worry about here is versioning. If you update
your business assembly (DLL) while a workflow is unloaded, and you change the fields in the
business objects that were serialized (adding or removing fields), you may run into some versioning
issues when the data is deserialized.
The WF engine uses the BinaryFormatter in a mode where it is not version sensitive. This means
that the deserialization will not fail due to a version mismatch even if the business assembly has been
updated since the object was serialized. However, Table 10 lists some specific issues you need to
consider.

Issue
Add new field to
business object

Description
If you add a new field to a business object, the serialized byte stream
clearly has no value corresponding to that field. When the byte stream
is deserialized, that field is left uninitialized.

Remove field
from business
object

If you remove a field from a business object, the serialized byte


stream has a value with no corresponding field. When the byte stream
is deserialized, that value is simply and silently discarded.

Mark a field as

If you mark a field as NotSerialized, the BinaryFormatter ignores


that field, so it is like the field was removed from the object. The
serialized byte stream then has a value with no corresponding field.
When the byte stream is deserialized, that value is simply and silently
discarded.

NotSerialized

Table 10. Versioning issues when reloading a workflow.


Microsoft provides some good information about versioning issues and the BinaryFormatter in
this MSDN article:
http://msdn2.microsoft.com/en-us/library/ms229752.aspx
The article includes some important recommendations:
1. Never remove a field from a Serializable object.
2. Never add the NotSerialized attribute to any field in a Serializable object.
3. Use the OptionalField attribute from the System.Runtime.Serialization namespace when
adding a new field to a Serializable object.
Item number 3 is worthy of some further discussion.

Using the OptionalField Attribute


The OptionalField attribute indicates that a field is optional, so the BinaryFormatter knows it can
safely ignore the field if there is no corresponding value in the byte stream.
[OptionalField]
private int _newField;
Using CSLA .NET 3.0
Copyright 2007 Rockford Lhotka

Page 89

What youll find, though, is that you dont need to use this attribute to avoid getting an exception
when the WF engine reloads a workflow instance. In other words, the OptionalField attribute is
optional.
However, you can use the OptionalField attribute in conjunction with the
attribute to set default values for optional fields:

OnDeserializing

[OptionalField]
private int _newField;
[OnDeserializing]
private void OnDeserializingHandler(StreamingContext context)
{
_newField = 123; // default value
}

The method marked with OnDeserializing is invoked by the BinaryFormatter as the objects
fields are deserialized from the byte stream, and it is intended to be used as shown here: loading
default values into optional fields.
You can also specify the version of your object at which the field was added:
[OptionalField(VersionAdded = 3)]
private int _newField;
[OnDeserializing]
private void OnDeserializingHandler(StreamingContext context)
{
_newField = 123; // default value
}

The BinaryFormatter then knows to ignore the value when deserializing any byte streams from
older versions of the object.
These techniques allow you to construct your business objects in a way that is safe for versioning,
even if the business object is serialized and deserialized as part of a workflows state.

Starting a Workflow from an Object


Executing a workflow from a business object is not much different from executing a workflow in any
other context. If you add a workflow console application in Visual Studio, the code will include the
basic template for executing your workflow. Any time you execute a workflow youll follow this
basic pattern:
1. Create a thread synchronization object.
2. Create a workflow runtime instance.
3. Set up some event handlers (at least for Completed and Terminated) where you Set() the
synchronization object.
4. Create the workflow instance.
5. Start the workflow instance.
6. Wait on the thread synchronization object until it is Set() by one of the event handlers.
However, there is one important difference when using business objects. Remember that the
workflow project references the business layer. To execute a workflow you need access to a Type
Using CSLA .NET 3.0
Copyright 2007 Rockford Lhotka

Page 90

object for that workflow, which implies that the business layer would have a reference to the
workflow project. This sort of circular reference between assemblies is not allowed.
If you look back at Figure 14, the workflow is architecturally treated as just another interface to
the business layer. And thats a good way to think about the workflow. That means that having the
workflow reference the assembly containing the business objects is a good thing, so I dont
recommend changing that.
To avoid a circular reference, the business layer must not reference the workflow assembly. You
can avoid the circular reference by dynamically loading the workflow type. This is done with code
like this:
Type workflowType = Type.GetType("Namespace.WorkflowClass, Assembly");
WorkflowInstance instance =
workflowRuntime.CreateWorkflow(workflowType);

The Namespace.WorkflowClass part of the text is the fully qualified type name of the workflow
class, including namespace. The Assembly part of the text is the name of the assembly containing the
workflow class. Notice how the CreateWorkflow() method then accepts the Type object. This allows
the workflow runtime to properly create an instance of the workflow, without this code needing a
reference to the assembly that contains the workflow.

WorkflowManager Class
CSLA .NET provides a WorkflowManager class in the Csla.Workflow namespace to help abstract the
process of executing a workflow. The primary purpose of this class is to help manage the use of
thread synchronization, while maintaining the flexibility provided by the workflow runtime model.
Table 11 lists the methods provided by the

Method
ExecuteWorkflow

WorkflowManager

object.

Description
Synchronously executes a workflow, blocking the calling
thread until the workflow stops.

BeginWorkflow

Asynchronously starts executing a workflow. The calling


thread is not blocked, but must call EndWorkflow() at
some point.

WaitForEnd

Synchronously waits for the workflow to stop. Returns


immediately if the workflow has already stopped. This
method can optionally dispose the WF runtime instance.

ResumeWorkflow

Synchronously resumes execution of a workflow,


blocking the calling thread until the workflow stops.

BeginResumeWorkflow

Asynchronously resumes execution of a workflow. The


calling thread is not blocked, but must call EndWorkflow()
at some point.

InitializeRuntime

Synchronously initializes the WF runtime, allowing the


calling code to manipulate the runtime before calling one

Using CSLA .NET 3.0


Copyright 2007 Rockford Lhotka

Page 91

of the methods to execute or resume a workflow.


DisposeRuntime

Synchronously disposes the workflow runtime.

Table 11. Methods provided by WorkflowManager.


Several of these methods allow you to execute a workflow, or to resume a suspended workflow.
In both cases, control wont return to your code until the workflow is completed, terminated or
suspended.
Other methods allow you to start execution of a workflow on a background thread, or to resume
execution of a workflow on a background thread. Your thread remains active and can be used to
perform other tasks while the workflow is running.

Synchronous Execution of a Workflow


In its simplest usage, the WorkflowManager can execute a workflow, by Type or type name, in two
lines of code:
WorkflowManager mgr = ne w WorkflowManager();
mgr.ExecuteWorkflow("PTWorkflow.ProjectWorkflow, PTWorkflow");

In this case, the ExecuteWorkflow() method creates a Type object for the workflow based on the
assembly qualified name of the workflow, such as PTWorkflow.ProjectWorkflow, PTWorkflow.
Alternately, you can just pass a Type object as a parameter, rather than the type name:
WorkflowManager mgr = new WorkflowManager();
mgr.ExecuteWorkflow(typeof(ProjectWorkflow));

Either way, this synchronously executes the workflow. When ExecuteWorkflow() returns, the
workflow is complete or terminated, and the workflow runtime has been disposed.
You can examine mgr.Status to determine the final state of the workflow. The possible values are
listed in Table 12.

Using CSLA .NET 3.0


Copyright 2007 Rockford Lhotka

Page 92

Status
Initializing

Description
The workflow is being initialized and has not yet started
executing.

Executing

The workflow is being executed.

Completed

The workflow completed properly.

Terminated

The workflow was abnormally terminated. Use the Error


property to get the exception.

Suspended

The workflow was suspended.

Idled

The workflow is idled.

Aborted

The workflow was aborted.

Table 12. WorkflowManager Status property values.


For simple workflows, you can expect Completed or Terminated results once the
has returned.

ExecuteWorkflow() method

Dealing with Idled or Suspended Workflows


More complex workflows might use a persistence service to store an idled or suspended workflow to
a database so it can be resumed later. That complicates the code slightly, because you need to
associate a persistence service with the workflow runtime, and be more detailed in checking the
status of the workflow when ExecuteWorkflow() returns.

Starting a Workflow
This code shows the basic structure required to execute a workflow that might be idled or suspended,
and which will be persisted to a database in both those cases:
Csla.Workflow.WorkflowManager mgr = new Csla.Workflow.WorkflowManager();
mgr.InitializeRuntime();
// associate your persistence service with mgr.RuntimeInstance here
mgr.ExecuteWorkflow("WorkflowApp.Workflow1, WorkflowApp", false);
if (mgr.Status == Csla.Workflow.WorkflowStatus.Suspended)
{
Guid instanceId = mgr.WorkflowInstance.InstanceId;
mgr.WorkflowInstance.Unload();
// store instanceId so you can resume the workflow later
}
mgr.DisposeRuntime();

If you expect your workflow might become idled or suspended, and you want to store that
workflow instance in a database to resume later, you need to provide a persistence service to the
workflow runtime.

Using CSLA .NET 3.0


Copyright 2007 Rockford Lhotka

Page 93

Associating a Persistence Service with the Runtime


By explicitly calling InitializeRuntime() , the code above creates and initializes the workflow
runtime, allowing you to then associate a persistence service (or other services) with that runtime.
This would be done with code similar to:
Csla.Workflow.WorkflowManager mgr = new Csla.Workflow.WorkflowManager();
mgr.InitializeRuntime();
string connectionString =
@"Data Source=ineroth;Initial Catalog=WorkflowPersistenceStore;
Persist Security Info=True;User ID=test;Password=test";
bool unloadOnIdle = true;
TimeSpan instanceOwnershipDuration = TimeSpan.MaxValue;
TimeSpan loadingInterval = new TimeSpan(0, 2, 0);
// Add the SqlWorkflowPersistenceService to the runtime engine.
SqlWorkflowPersistenceService persistService =
new SqlWorkflowPersistenceService(
connectionString, unloadOnIdle, instanceOwnershipDuration, loadingInterval);
mgr.RuntimeInstance.AddService(persistService);

This code creates and configures an instance of SqlWorkflowPersistenceService , a built-in


persistence service provided with WF. This persistence service stores the serialized workflow state in
the SQL Server database specified by the connection string. Before using that database, you need to
run a pre-built SQL script to set up the required tables and do some other administrative tasks. This
MSDN article describes the required steps:
http://msdn2.microsoft.com/en-US/library/aa349366.aspx.
The unloadOnIdle parameter also specifies that an Idled workflow should be automatically
persisted.
Notice the last line of code:
mgr.RuntimeInstance.AddService(persistService);

This line of code adds the service to the workflow runtime instance being used by the
WorkflowManager.

Unloading a Suspended Workflow


Returning to the original code that executes the workflow, notice that ExecuteWorkflow() is called
with an extra parameter of false. This parameter indicates that the workflow runtime should not be
disposed before ExecuteWorkflow() returns. This is important, because if the workflow was
suspended, the code then uses the runtime to persist the workflow by calling the workflows
Unload() method:
if (mgr.Status == Csla.Workflow.WorkflowStatus.Suspended)
{
Guid instanceId = mgr.WorkflowInstance.InstanceId;
mgr.WorkflowInstance.Unload();
// store instanceId so you can resume the workflow later
}

Once this is done, the DisposeRuntime() method of the WorkflowManager is called to dispose the
workflow runtime.

Using CSLA .NET 3.0


Copyright 2007 Rockford Lhotka

Page 94

Make special note of the comment in the code above. If you unload a workflow, you need the
workflows InstanceId value, a Guid , to reload and resume the workflow later. Without this value
the workflow cant be reloaded.

Loading and Resuming a Workflow


The following code reloads and resumes the workflow, assuming the instanceId field contains a
valid workflow id:
Csla.Workflow.WorkflowManager mgr = new Csla.Workflow.WorkflowManager();
mgr.InitializeRuntime();
// associate your persistence service with mgr.RuntimeInstance here
mgr.ResumeWorkflow(instanceId);

The ResumeWorkflow() method uses the instanceId value to load the workflow instance from the
persistence service and then resumes the workflow execution.
At this point, you should understand how to execute, unload and resume a workflow that could
become idle or suspended as it runs.

Resuming a Suspended Workflow without Unloading


You dont have to unload a suspended workflow. You could keep the workflow instance in memory
and simply resume it. This is often valuable if the workflow requires that the application or user do
something immediately to allow the workflow to continue.
The following code shows how to resume a suspended workflow without unloading it:
Csla.Workflow.WorkflowManager mgr = new Csla.Workflow.WorkflowManager();
mgr.ExecuteWorkflow("WorkflowApp.Workflow1, WorkflowApp", false);
if (mgr.Status == Csla.Workflow.WorkflowStatus.Suspended)
{
// perform any actions required before resuming the workflow
mgr.ResumeWorkflow(false);
}
mgr.DisposeRuntime();

Notice that no persistence service is required, because the workflow is never unloaded. However,
the ExecuteWorkflow() method is still called such that the workflow runtime is not disposed when it
completes.
The Status property is then checked, and if the workflow was suspended then the application
needs to do any work that is required before the workflow is resumed. This is represented by the first
highlighted line of code.
The second highlighted line of code shows how to resume the current workflow. Since the
workflow wasnt unloaded, it is still in memory, just suspended. The ResumeWorkflow() method call
resumes the workflow and allows it to run to completion.

Executing a Workflow from a Business Object


You can use the WorkflowManager class to simplify execution of a workflow from within a business
object, or you can directly invoke the WF objects yourself. Either way, a workflow is typically
executed from one of the DataPortal_XYZ methods in a business object.
Using CSLA .NET 3.0
Copyright 2007 Rockford Lhotka

Page 95

Execution of a workflow should fit naturally into your object model. Remember that the UI layer
always interacts with your business objects, never with the database or any other services directly. If
your application makes use of a workflow to perform some back-end processing, that processing
should be represented somehow within your object model.

Using a Command Object


In many cases, this means that the workflow will be represented by a command object, which is
subclass of CommandBase . For example, the ProjectWorkflow that closes a project could be
represented in the object model through a ProjectCloser object:
[Serializable]
public class ProjectCloser : CommandBase
{
public static void CloseProject(Guid id)
{
ProjectCloser cmd = new ProjectCloser(id);
cmd = DataPortal.Execute<ProjectCloser>(cmd);
}
private Guid _projectId;
private ProjectCloser()
{ /* require use of factory methods */ }
private ProjectCloser(Guid projectId)
{
_projectId = projectId;
}
protected override void DataPortal_Execute()
{
Dictionary<string, object> parameters = new Dictionary<string, object>();
parameters.Add("ProjectId", _projectId);
WorkflowManager mgr = new WorkflowManager();
mgr.ExecuteWorkflow("PTWorkflow.ProjectWorkflow, PTWorkflow", parameters);
if (mgr.Status == WorkflowStatus.Terminated)
throw mgr.Error;
}
}

The code in DataPortal_Execute() is highlighted, as this is the code that executes the workflow.
Notice the use of a Dictionary to pass in name/value parameters to the workflow. The parameter
sets the ProjectId dependency property of the ProjectWorkflow.
It is also important to note that ExecuteWorkflow() is called using the type name of
reference PTWorkflow,

ProjectWorkflow. This is required because ProjectTracker.Library cannot


because PTWorkflow already references ProjectTracker.Library!

When ExecuteWorkflow() completes, the Status property is checked to see if the workflow
terminated abnormally. The exception from the workflow is then thrown, indicating to the original
caller of the object that the command failed.
With ProjectCloser implemented like this, any code using the business layer can close a project
like this:
ProjectCloser.CloseProject(projectId);

Using CSLA .NET 3.0


Copyright 2007 Rockford Lhotka

Page 96

The calling code doesnt need to worry about workflows, threading or any of the details.
Everything is abstracted behind the business object.
Better yet, ProjectCloser is a standard CSLA .NET business object, which means that its call to
DataPortal.Execute() uses the data portal. If the application is configured properly, this means that
the workflow would run on the application server, or if the application is configured to use a local
data portal the workflow would run on the client machine.

Executing a Workflow as Part of Data Access


You may encounter cases where it is appropriate to execute a workflow as part of the normal process
of data access. This can be a powerful technique to allow extra processing to occur before or after the
data access itself.
For example, as shown in Figure 23, the ProjectWorkflow workflow could be changed to assume
that the Project objects data has already been saved.

Figure 23. ProjectWorkflow that doesnt save the Project object.


In the example above, the workflow is focused purely on what happens due to a project being
inserted or updated. Given the powerful and high level design tools available for designing
workflows, this can be an excellent way to allow for post-processing after a data operation.

Using CSLA .NET 3.0


Copyright 2007 Rockford Lhotka

Page 97

The workflow now includes a check to see if the project is closed, and if it is closed then the
notification processes are invoked. If the project isnt closed nothing happens. However, the
workflow can be changed independently from the rest of the application, and so theres a great deal
of flexibility offered because additional processing can be added without affecting the business or UI
layers.
What you cant see from the designer surface is that this new version of ProjectWorkflow has a
dependency property called Project, and the ProjectId property has been removed:
private static DependencyProperty ProjectProperty = DependencyProperty.Register(
"Project", typeof(Project), typeof(ProjectWorkflow), null);
public Project Project
{
get { return (Project)base.GetValue(ProjectProperty); }
set { base.SetValue(ProjectProperty, value); }
}

The reason for this change is that workflow is designed to be invoked as the Project object
inserts or updates itself. Executing the workflow is handled in a new method in the Project class:
private void ExecuteWorkflow()
{
Dictionary<string, object> parameters = new Dictionary<string, object>();
parameters.Add("Project", this);
Csla.Workflow.WorkflowManager mgr = new Csla.Workflow.WorkflowManager();
mgr.ExecuteWorkflow("PTWorkflow.ProjectWorkflow, PTWorkflow", parameters);
}

Notice how the actual instance of the Project object, this , is passed into the workflow as a
parameter, so it can be used by the activities in the workflow.
Note: Because the PTWorkflow assembly is being loaded dynamically by name
without being referenced by ProjectTracker.Library, you must ensure that the
PTWorkflow assembly is physically located in the same directory as your applications
EXE or in your data portal servers \bin directory.
The easiest way to do that is typically to reference the PTWorkflow project from your
main UI project, such as PTWpf or PTWin.
The final step is to call ExecuteWorkflow() from the DataPortal_Insert() and
DataPortal_Update() methods. For example, heres the call in DataPortal_Update():
[Transactional(TransactionalTypes.TransactionScope)]
protected override void DataPortal_Update()
{
if (base.IsDirty)
{
using (SqlConnection cn = new SqlConnection(Database.PTrackerConnection))
{
cn.Open();
using (SqlCommand cm = cn.CreateCommand())
{
cm.CommandText = "updateProject";
cm.Parameters.AddWithValue("@lastChanged", _timestamp);
DoInsertUpdate(cm);
}
}
Using CSLA .NET 3.0
Copyright 2007 Rockford Lhotka

Page 98

}
// update child objects
_resources.Update(this);
MarkOld();
ExecuteWorkflow();
}

Notice how the workflow is executed after both the Project object and all its child objects have
been updated. The Project object in memory should exactly match the data in the database.
Also notice the explicit call to MarkOld() , to mark the object as both not new and not changed.
The data portal will make this call too, but after DataPortal_Update() completes, and it is best if the
call happens before the workflow executes so the workflow activities are working against accurate
data.
Remember that this code is still running within a TransactionScope due to the Transactional
attribute on the method. If the workflow terminates abnormally, the exception from the workflow
will be thrown, which will automatically roll back the transaction.
If you want the workflow to execute outside of the transaction, youll need to switch to manual
transactions and create your own TransactionScope. Heres what that code would look like:
protected override void DataPortal_Update()
{
using (TransactionScope tr = new TransactionScope())
{
if (base.IsDirty)
{
using (SqlConnection cn = new SqlConnection(Database.PTrackerConnection))
{
cn.Open();
using (SqlCommand cm = cn.CreateCommand())
{
cm.CommandText = "updateProject";
cm.Parameters.AddWithValue("@lastChanged", _timestamp);
DoInsertUpdate(cm);
}
}
}
// update child objects
_resources.Update(this);
tr.Complete();
}
ExecuteWorkflow();
}

The highlighted lines of code are the extra lines needed to implement the transactional behavior
manually. The Transactional attribute has been removed from the method, which means that the
data portal wont wrap this method in a transaction automatically. Manual transactions are the default
behavior by the data portal.
The ExecuteWorkflow() method is called after the transaction is complete, meaning that it will
execute outside your transaction. Now if the workflow terminates abnormally, the exception from the
workflow will be thrown, but wont roll back the database transaction.
You can use either technique, depending on whether you want a workflow exception to roll back
the data operation or not.

Using CSLA .NET 3.0


Copyright 2007 Rockford Lhotka

Page 99

At this point, you should understand how to use WF and business objects together. You can build
workflow activities using business objects within the activity, and you can execute workflows from
within your business objects.

Using CSLA .NET 3.0


Copyright 2007 Rockford Lhotka

Page 100

Chapter 4:
Validation
CSLA .NET includes framework support for implementing validation logic within each of your
business objects. This functionality was introduced in version 2.0 and is described in Expert C# 2005
Business Objects. CSLA .NET version 2.1 introduced several major enhancements, and they are
described in the CSLA .NET Version 2.1 Handbook.
CSLA .NET version 3.0 includes more enhancements to the validation rule functionality. Some of
these enhancements are relatively straightforward, but a couple are more complex and are designed
either for advanced scenarios or to simplify code generation of business objects. Table 13 provides a
summary of the new features.

Feature
StringMinLength

rule

Description
This is a new rule method in
Csla.Validation.CommonRules.

It allows you to require a


minimum length for a string property.
RegExMatch

rule null

handling

The RegExMatch rule method has been enhanced to allow


you to specify how null input values should be treated.

Friendly property names

The RuleArgs object has been enhanced to allow you to


specify a friendly name for a property. Your rule methods
can use this friendly name to provide nicer output to the
user, and the CommonRules rule methods now do this as
well.

Format mask

The rule methods in CommonRules that output numeric


values to the user now accept a format mask that the rule
will use to format the value in the output.

Short-circuiting bug fix

A bug relating to short-circuiting of rule processing was


fixed, enabling an important scenario.

DecoratedRuleArgs

A subclass of RuleArgs named DecoratedRuleArgs was


introduced to better support code generation scenarios.

Table 13. Summary of new validation features and enhancements.


Lets discuss each of these enhancements.

Using CSLA .NET 3.0


Copyright 2007 Rockford Lhotka

Page 101

StringMinLength Rule
CSLA .NET 2.0 included the StringRequired and StringMaxLength rule methods. The
StringMinLength method completes the set, making it possible to mandate a minimum length for a
string value. You use the rule as follows:
ValidationRules.AddRule(
Csla.Validation.CommonRules.StringMinLength,
new Csla.Validation.CommonRules.MinLengthRuleArgs("Name", 5));

This example ensures that the Name property has a string with length 5 or greater, or the object will
be considered invalid.

RegExMatch Rule
The RegExMatch rule method returns true if the property value matches the supplied regular
expression. However, in version 2.0 there were no explicit options for dealing with null values, and
that shortcoming has been addressed in version 3.0 with the addition of the NullResultOptions
enumerated list. The possible values are listed in Table 14.

Value
ConvertToEmptyString

Description
Converts any null value to an empty string before
checking the regular expression.

ReturnFalse

A null value always causes the rule to return false .

ReturnTrue

A null value always causes the rule to return true.

Table 14. NullResultOptions values.


The default value is ReturnFalse, meaning that a null value always causes the rule to fail by
returning false. In other words, by default a null value never matches any regular expression.
This change to the framework means that RegExMatch now handles null values without throwing
a NullReference exception, and you are in control of what a null value means for each property.
You can provide one of these values in the RegExRuleArgs parameter to control how the rule
processes null values:
ValidationRules.AddRule(
Csla.Validation.CommonRules.RegExMatch,
new Csla.Validation.CommonRules.RegExRuleArgs(
"Name",
Csla.Validation.CommonRules.RegExPatterns.Email,
Csla.Validation.CommonRules.RegExRuleArgs.NullResultOptions.ConvertToEmptyString));

The highlighted line of code specifies that the rule should convert null values to an empty string
before checking the regular expression.

Friendly Property Names


Rule methods generate a human-readable description in the case of failure, and most of these
descriptions include the property name as part of the text. For some property names this works fine,

Using CSLA .NET 3.0


Copyright 2007 Rockford Lhotka

Page 102

but sometimes the property name isnt what youd like to display to the user. For example, a property
called ProjectName might be better represented by the text Project name.
CSLA .NET 3.0 includes support for friendly names for properties. If a friendly name is
supplied, it is used in the rule description text, otherwise the property name is used. The friendly
name is passed into the rule method as part of the RuleArgs parameter.

RuleArgs Property
The RuleArgs class has been enhanced to include a PropertyFriendlyName property. You can set this
property directly, or through an overloaded constructor. For example:
Csla.Validation.RuleArgs args = new Csla.Validation.RuleArgs("Name");
args.PropertyFriendlyName = "Project name";
ValidationRules.AddRule(
Csla.Validation.CommonRules.StringRequired, args);

Or:
ValidationRules.AddRule(
Csla.Validation.CommonRules.StringRequired,
new Csla.Validation.RuleArgs("Name", "Project name"));

If you set the PropertyFriendlyName property, this value is provided to the rule method
implementation, which can use this value to generate the broken rule description.

GetPropertyName Method
When you implement a custom rule method you should honor any value provided through
PropertyFriendlyName, and if no value is provided then you should use the PropertyName value:
string name;
if (string.IsEmptyOrNull(e.PropertyFriendlyName))
name = e.PropertyName;
else
name = e.PropertyFriendlyName;

Writing that same code in each rule method is redundant however, and so the RuleArgs class
includes a helper method to do the work for you:
string name = Csla.Validation.RuleArgs.GetPropertyName(e);

You can use this GetPropertyName() method to generate your description text. For example, a
Customer class might include a rule like this:
private static bool CreditLimit<T>(
T target, Csla.Validation.RuleArgs e) where T : Customer
{
if (target._creditLimit > Customer.MaxCreditAllowed)
{
e.Description = Csla.Validation.RuleArgs.GetPropertyName(e) + " limit exceeded;
return false;
}
else
return true;
}

Using CSLA .NET 3.0


Copyright 2007 Rockford Lhotka

Page 103

The rule methods in Csla.Validation.CommonRules make use of this pattern and you can use
them as examples for using the new functionality.

Format Mask Support


Several of the rule methods in Csla.Validation.CommonRules display numeric values in their
human-readable broken rule descriptions. In CSLA .NET 3.0 you can now specify the .NET format
string used to display these values. For example:
ValidationRules.AddRule(
Csla.Validation.CommonRules.MaxValue<int>,
new Csla.Validation.CommonRules.MaxValueRuleArgs<int>(
"Price", "Item price", 200, "$#,##0.00"));

In this example, Im passing in a custom format mask $#,##0.00 .


The rule methods that support the

StringMinLength

StringMaxLength

MaxValue

MinValue

IntegerMaxValue

IntegerMinValue

RegExMatch

format parameter

are:

The constructors for the RuleArgs subclasses for each of these rules include a format parameter
allowing you to specify the value.

Short-Circuiting Bug Fix


CSLA .NET 2.0 implemented a concept called short-circuiting, where a broken rule could prevent
the processing of subsequent rule methods for a property. This is a useful capability, because it
allows you to execute inexpensive rules first, and only execute expensive or complex rules if the
previous rules all return true .
This can be done through the use of rule priorities and the ProcessThroughPriority property as
discussed in the CSLA .NET Version 2.1 Handbook, or it can be done explicitly in a rule method by
setting e.StopProcessing to true :
private static bool MyRule(object target, RuleArgs e)
{
if (<rule fails>)
{
e.StopProcessing = true;
e.Description = "Bad value";
return false;
}
else
return true;
}

Using CSLA .NET 3.0


Copyright 2007 Rockford Lhotka

Page 104

In CSLA .NET 2.0 there was a bug where the StopProcessing property was never reset to false,
and so rule processing always short-circuited for a property after the rules were first checked. In
CSLA .NET 3.0, StopProcessing is set back to false after the rule has been processed.
CSLA .NET 3.0 also supports the use of the StopProcessing property even if the rule method
returns true :
private static bool AlwaysStop(object target, RuleArgs e)
{
e.StopProcessing = true;
return true;
}

This variation allows you to stop processing subsequent rule methods for the property, without
making the property or business object invalid. This technique can be useful if there are some rules
that you only want to process under specific circumstances, such as when the object is new. For
example:
private static bool StopIfNotNew<T>(T target, RuleArgs e) where T : IEditableBusinessObject
{
if (!target.IsNew)
e.StopProcessing = true;
return true;
}

By using priorities you can have some rules that run at all times, and other rules that only run for
new objects:
protected override void AddBusinessRules()
{
ValidationRules.ProcessThroughPriority = 2;
ValidationRules.AddRule(
Csla.Validation.CommonRules.StringRequired, "Name");
ValidationRules.AddRule<Customer>(
StopIfNotNew<Customer>, "Name", 1);
ValidationRules.AddRule(
SomeOtherRule, "Name", 2);
}

The ProcessThroughPriority property indicates that all rules priorities 0 to 2 should be


processed without automatic short-circuiting. However, at priority 1 the StopIfNotNew rule is
executed, and it will explicitly short-circuit rule processing if the object is not new, so the priority 2
rules will only run for new objects.
You can use this concept to organize your rules in many ways depending on the business
requirements for your rules and objects.

Improved Code Generation Support


Code generation is a powerful concept, and I recommend the use of code generation to build as much
business object code as possible. Code generators or the templates for such generators are easier to
write when consistent coding patterns are used in the generated code.
Unfortunately, the use of strongly typed subclasses of RuleArgs makes it difficult for code
generators to create the code in AddBusinessRules(). Since each rule method typically requires a
specific RuleArgs subclass, which has a unique constructor, it is difficult to generalize this code.
Using CSLA .NET 3.0
Copyright 2007 Rockford Lhotka

Page 105

For example:
protected override void AddBusinessRules()
{
ValidationRules.AddRule(
Csla.Validation.CommonRules.StringRequired,
new Csla.Validation.RuleArgs("Name", "Project name"));
ValidationRules.AddRule(
Csla.Validation.CommonRules.StringMinLength,
new Csla.Validation.CommonRules.MinLengthRuleArgs("Name", 5));
ValidationRules.AddRule(
Csla.Validation.CommonRules.StringMaxLength,
new Csla.Validation.CommonRules.MaxLengthRuleArgs("Name", 50, "d"));
ValidationRules.AddRule(
Csla.Validation.CommonRules.MaxValue<int>,
new Csla.Validation.CommonRules.MaxValueRuleArgs<int>(
"Price", "Item price", 200, "$#,##0.00"));
}

Notice how each argument object is a different type, and accepts different parameters in its
constructor. While it is possible to create code generation to handle this, the generator or template
ends up being complex and fragile.

DecoratedRuleArgs
The DecoratedRuleArgs is a new subclass of RuleArgs in the Csla.Validation namespace. This
class builds on RuleArgs by using the Decorator design pattern; which is a fancy way of saying that
it provides a Dictionary of name/value pairs that are passed into the rule method.
The previous AddBusinessRules() implementation can be written this way instead:
protected override void AddBusinessRules()
{
Csla.Validation.DecoratedRuleArgs args;
args = new Csla.Validation.DecoratedRuleArgs("Name", "Project name");
ValidationRules.AddRule(
Csla.Validation.CommonRules.StringRequired, args);
args = new Csla.Validation.DecoratedRuleArgs("Name");
args["MinLength"] = 50;
args["Format"] = "d";
ValidationRules.AddRule(
Csla.Validation.CommonRules.StringMinLength, args);
args = new Csla.Validation.DecoratedRuleArgs("Name");
args["MaxLength"] = 50;
args["Format"] = "d";
ValidationRules.AddRule(
Csla.Validation.CommonRules.StringMaxLength, args);
args = new Csla.Validation.DecoratedRuleArgs("Price", "Item price");
args["MaxValue"] = 200;
args["Format"] = "$#,##0.00";
ValidationRules.AddRule(
Csla.Validation.CommonRules.MaxValue<int>, args);
}

The result is more code, but the code is more consistent. Every call to AddRule() is the same, and
theres no need to know about custom argument types or different parameters to the constructor.
Since the parameters are added as name/value pairs, they can be easily generated.
This is possible, because all the custom RuleArgs subclasses used by the rule methods in
actually subclass from DecoratedRuleArgs, and the rule
methods themselves use the Dictionary to get all parameter values.
Csla.Validation.CommonRules now

Using CSLA .NET 3.0


Copyright 2007 Rockford Lhotka

Page 106

Note: This technique can only be used with rule methods that accept
DecoratedRuleArgs or a subclass of DecoratedRuleArgs as a parameter. You must
change any existing rule methods before using this technique!
The only remaining variation that a code generator has to deal with is the use of generics. Some
rule methods are generic, others are not. But the use of generics (or not) when creating rule methods
is under your control, and so you can either make your code generator accommodate their use, or you
can mandate that all (or none) of your rule methods use generics to standardize the generated code as
you choose.

Creating Rule Methods


If you want your custom rule method to support the use of DecoratedRuleArgs as shown in the
previous example, your rule method must accept a DecoratedRuleArgs parameter, or a subclass of
DecoratedRuleArgs.
You can look at the rule methods in Csla.Validation.CommonRules for inspiration, but they are
more complex than they need to be. The reason is that I wanted to preserve backward compatibility
and so I had to do more work in those rule methods than you should have to do when creating a
custom rule method of your own.
Creating a rule method that uses DecoratedRuleArgs is relatively straightforward. For example,
heres a simple date rule:
private static bool DateBefore<T>(
T target, Csla.Validation.RuleArgs e) where T : SalesOrder
{
Csla.Validation.DecoratedRuleArgs args = (Csla.Validation.DecoratedRuleArgs)e;
if (target._shipDate > (DateTime)args["MaxDate"])
{
e.Description = "Invalid date";
return false;
}
else
return true;
}

Because the delegate signature for a rule method requires a RuleArgs parameter, the parameter
must be cast to DecoratedRuleArgs:
Csla.Validation.DecoratedRuleArgs args = (Csla.Validation.DecoratedRuleArgs)e;

Once thats done, the args field is a Dictionary containing the parameter values, so they can be
used in the rule implementation:
if (target._shipDate > (DateTime)args["MaxDate"])

This rule can now be associated with a property in the AddBusinessRules() method:

Using CSLA .NET 3.0


Copyright 2007 Rockford Lhotka

Page 107

protected override void AddBusinessRules()


{
Csla.Validation.DecoratedRuleArgs args;
args = new Csla.Validation.DecoratedRuleArgs("ShipDate", "Ship date");
args["MaxDate"] = DateTime.Now;
ValidationRules.AddRule<SalesOrder>(
DateBefore<SalesOrder>, args);
}

Again, the call to AddRule() is very standardized and can easily be generated by a tool or code
generation template.
One subtle issue to note about this example code, is that AddBusinessRules() is run exactly once
during the lifetime of your application. It is run when the first instance of this business object is
created, and that is the point at which the MaxDate parameter is set:
args["MaxDate"] = DateTime.Now;

If the application is not closed until the next day, this date could become invalid. If that is a
concern, youd need to use a different technique to set the MaxDate value so it could be changed as
the application continues to run.

CommonRules Parameters
The one primary downside to using DecoratedRuleArgs is that the parameters used by your rule
method are no longer strongly typed, and so Intellisense is not available. This means that the author
of a rule method must clearly document the arguments expected.
The rules in Csla.Validation.CommonRules still have their strongly typed RuleArgs subclasses,
and so Intellisense still works if you use those subclasses. However, as youve seen, these rule
methods also accept a simple DecoratedRuleArgs parameter, in which case the parameter names and
meanings must be documented. Table 15 lists the values for each rule method.

Using CSLA .NET 3.0


Copyright 2007 Rockford Lhotka

Page 108

Rule

Parameter
MaxLength

StringMaxLength

StringMinLength

MaxValue

MinValue

IntegerMaxValue

IntegerMinValue

RegExMatch

Description
Maximum length allowed

Format

Format string for output of


MaxLength value

MinLength

Minimum length allowed

Format

Format string for output of


MinLength value

MaxValue

Maximum value allowed

Format

Format string for output of


MaxValue value

MinValue

Minimum value allowed

Format

Format string for output of


MinValue value

MaxValue

Maximum value allowed

Format

Format string for output of


MaxValue value

MinValue

Minimum value allowed

Format

Format string for output of


MinValue value

RegEx

Regular expression

NullOption

Value from the NullResultOptions


enumerated list in string form

Table 15. Parameters for the rule methods in CommonRules.


If you choose to directly pass a DecoratedRuleArgs parameter to one of these rule methods, Table
15 lists the parameter values you need to provide to each rule.

RuleDescription
CSLA .NET 2.1 introduced the use of a URI to describe the rules associated with the properties of an
object. A rule URI looks like this:
rule://methodName/propertyName?arg1=value&arg2=value

Using CSLA .NET 3.0


Copyright 2007 Rockford Lhotka

Page 109

For a complete discussion of the rule:// URI format please refer to the CSLA .NET Version 2.1
Handbook.
CSLA .NET 3.0 includes a new RuleDescription class that understands how to parse a rule://
URI for easier use. Rather than manually writing code to parse the URI, the RuleDescription class
can be used to easily get at the parts of the URI:
Csla.Validation.RuleDescription desc = new Csla.Validation.RuleDescription(
"rule://methodName/propertyName?arg1=value&arg2=value");
string scheme = desc.Scheme;
string methodName = desc.MethodName;
string propertyName = desc.PropertyName;
List<string> args = new List<string>();
foreach (System.Collections.Generic.KeyValuePair<string, string> item in desc.Arguments)
args.Add(item.Key + ", " + item.Value);

Table 16 lists the properties of the

Property
Scheme

RuleDescription

object.

Description
Returns the URI scheme, which will always be rule://.

MethodName

Returns the rule method name specified in the URI.

PropertyName

Returns the name of the business object property with which this
rule is associated.

Arguments

Returns a Dictionary of name/value pairs representing the


arguments passed to the rule method.

Table 16. Properties of the RuleDescription object.


These properties provide easy access to the information in a

rule:// URI.

You should now have a good understanding of the new features and capabilities available for
creating and using rule methods in CSLA .NET 3.0.

Using CSLA .NET 3.0


Copyright 2007 Rockford Lhotka

Page 110

Chapter 5:
Authorization
CSLA .NET 2.0 introduced support for property level authorization in business objects. By calling
CanReadProperty() in your property get code and CanWriteProperty() in your property set code,
you can ensure that only authorized users are able to read and write each property value.
By overriding the AddAuthorizationRules() method, you can specify the roles that are allowed
or denied read and write access to all the properties in an object.
CSLA .NET 3.0 extends this capability by adding a CanExecuteMethod() method, and
corresponding AllowExecute() and DenyExecute() methods for use in AddAuthorizationRules() .

CanExecuteMethod Method
The CanExecuteMethod() method is similar to CanReadProperty() and CanWriteProperty(). You
can use it at the top of a method in your business object to determine whether the current user is
authorized to execute that method.
The CanExecuteMethod() method has several overloads you can use. You may choose to
explicitly provide the method name:
public void DoSomething()
{
CanExecuteMethod("DoSomething", true);
// do something
}

Alternatively it can dynamically determine the method name:


[System.Runtime.CompilerServices.MethodImpl(
System.Runtime.CompilerServices.MethodImplOptions.NoInlining)]
public void DoSomething()
{
CanExecuteMethod(true);
// do something
}

Notice that you must use the NoInlining attribute to tell the .NET just-in-time compiler not to
apply the inlining optimization to this method. The inlining optimization literally copies your code
from inside the method directly into the code that is calling your method. The result is that your
method doesnt really exist in the code at runtime, and so CanExecuteMethod() is then unable to
dynamically determine the name of your method.
This second option allows for more maintainable code, because you dont have a string literal in
your code. However, dynamically determining the method name does incur a performance penalty.
You can decide which overload to use based on whether maintainability or performance is the
highest priority for your application.
In both the previous examples, a Boolean parameter was passed into the method. This parameter
specifies whether CanExecuteMethod() should throw an exception if the user is not authorized to
execute the method. Passing true indicates that an exception should be thrown. You can also pass
false:
Using CSLA .NET 3.0
Copyright 2007 Rockford Lhotka

Page 111

public void DoSomething()


{
if (CanExecuteMethod("DoSomething", false))
{
// do something
}
else
{
// do nothing
}
}

The method executes whether the user is authorized or not, but if the user isnt authorized, the
method does no work. You can use this overload to throw your own exception, perform no action or
perform an alternate action if the user is not authorized.

AllowExecute and DenyExecute Methods


When using authorization, your business class must override AddAuthorizationRules() to establish
which roles are allowed or denied read and write access to the objects properties, and execute access
to the objects methods.
The AllowExecute() and DenyExecute() methods are used to specify the roles for executing
methods:
protected override void AddAuthorizationRules()
{
AuthorizationRules.AllowExecute("DoSomething", "Administrator");
AuthorizationRules.DenyExecute("DoSomethingElse", "Guest");
}

By default, all users are allowed to execute methods.


If you specify one or more roles using AllowExecute() , then all users are denied access except
users in those roles. In this example, the DoSomething method can only be executed by users in the
Administrator role.
If you specify one or more roles using DenyExecute(), then all users are allowed access except
users in those roles. Here, the DoSomethingElse method can be executed by all users except those in
the Guest role.
This approach is identical to the way AllowRead(), DenyRead() and the other property-based role
methods work from CSLA .NET 2.0.
You should now understand how to use the CanExecuteMethod() functionality added in CSLA
.NET 3.0.

Using CSLA .NET 3.0


Copyright 2007 Rockford Lhotka

Page 112

Chapter 6:
Data Binding
Data binding is an important technology that provides a clear, abstract interface between business
objects and the user interface. Whether the interface is Web Forms, Windows Forms or WPF, data
binding is very useful.
Windows Forms data binding is far richer and more demanding of the business objects than Web
Forms. At the present time Windows Forms data binding is more demanding than WPF as well,
though I expect that WPF will rapidly mature to be comparable to Windows Forms.
Because of the demanding nature of Windows Forms data binding, I am constantly on the lookout
for bugs and issues with the implementation of the data binding support in CSLA .NET. For business
objects to fully support data binding, they must implement some relatively complex and very
nuanced interfaces defined by Microsoft. You can find a full discussion of these interfaces and their
original implementations in Expert C# 2005 Business Objects.
In this chapter Ill discuss the bug fixes and changes made in CSLA .NET 3.0. These changes
address issues that have been discovered and addressed between CSLA .NET version 2.1 and 3.0.

Edit Level Mismatch Exception


The most important and visible change is that CSLA .NET now throws an UndoException when the
edit levels of parent and child objects get out of sync. Table 17 lists when this exception will be
thrown.

Method
CopyState()

Condition
If the new EditLevel would exceed the parents
EditLevel

UndoChanges()

If the new EditLevel would be less than the parents


EditLevel

AcceptChanges()

If the new EditLevel would be less than the parents


EditLevel

Table 17. Conditions when UndoException is thrown.


It is important to realize that these rules only apply when the methods are called by the parent
object. If you explicitly call BeginEdit() on an object, for example, no exception will be thrown,
even if that makes the objects EditLevel exceed the edit level of its parent. This is necessary,
because n-level undo can be nested through such explicit calls. For example:
parent.BeginEdit();
parent.children[0].BeginEdit();
parent.children[0].CancelEdit();
parent.ApplyEdit();

//
//
//
//

parent and
child 0 at
child 0 at
parent and

all children at EL 1
EL 2
EL 1
all children at EL 0

You can cause the exception using the following invalid code:

Using CSLA .NET 3.0


Copyright 2007 Rockford Lhotka

Page 113

parent.BeginEdit();
parent.children[0].BeginEdit();
parent.children[0].CancelEdit();
parent.children[0].CancelEdit();
parent.ApplyEdit();

//
//
//
//
//

parent and all children at EL 1


child 0 at EL 2
child 0 at EL 1
child 0 at EL 0
parent at EL 0, child at EL -1 (exception thrown)

Notice the duplicate CancelEdit() call on the child object that puts its EditLevel value to 0.
The highlighted line of code will throw an UndoException , because the parent will attempt to
cascade the ApplyEdit() call to its children through their AcceptChanges() method. At that point,
the children[0] objects new EditLevel value of -1 will be lower than the parents new value of 0.
The behavior of edit levels in n-level undo can be relatively complex, but when you call
a parent object, that object and all its child objects should end up at
exactly the same edit level. If that is not the case, then the code calling BeginEdit(), CancelEdit()
and ApplyEdit() is incorrect.
CancelEdit() or ApplyEdit() on

If you are using Windows Forms data binding, it is the data binding infrastructure that calls these
methods. So if the edit levels get out of sync in that environment, the resulting exception indicates a
flaw in the UI code or in how the UI is using data binding.
If you are using WPF, youll either be allowing CslaDataProvider to make the n-level undo calls
on your behalf, or youll be calling the methods manually. Either way, if the edit levels get out of
sync it is an indication of a flaw in the UI code.
Typically, n-level undo is not used in Web Forms. If you do use it youll need to make the method
calls manually. Again, an exception indicates a flaw in the UI code that calls those methods.
The most common area where youll encounter these exceptions is in Windows Forms with data
binding. The next section discusses the correct way to implement Apply, Save, Cancel and
Close buttons in that environment.

Windows Forms Data Binding


The most common data binding questions and issues occur with Windows Forms data binding. It is
important to realize that Windows Forms data binding uses a BindingSource control as a buffer
between the business object and the UI controls as shown in Figure 24.

Figure 24. Windows Forms data binding and the BindingSource.


Using CSLA .NET 3.0
Copyright 2007 Rockford Lhotka

Page 114

The BindingSource control sits between the UI and the business layer. Code in the UI should
interact with the BindingSource, not with the business objects. The BindingSource assumes that it
has sole control over the interaction with the business layer, and it can get very confused if the UI
code directly interacts with the business layer. This is especially true when it comes to n-level undo.
What this means for a Windows Forms developer, is that they should never call BeginEdit(),
CancelEdit() or ApplyEdit() on a business object when that object is connected to a BindingSource
control. For most applications this means the UI code will not call these methods on the business
object at all.
Instead, the UI code should call the comparable methods on the BindingSource control. That way
the control can make appropriate calls to the underlying business object.
If you dislike this behavior, you can tell CSLA .NET to ignore data bindings calls to invoke nlevel undo. Ill discuss this option later in the chapter.

Binding to an Object
When binding a Windows Forms UI to a business object, the binding is done through a
BindingSource control. If you drag and drop the object onto the Windows Forms design surface,
Visual Studio will create and configure the BindingSource control for you. For example, if you drag
and drop a Project business class onto a form, the result is a projectBindingSource control.
You can then write code behind the form, often as the form is created or loaded, to set the
of projectBindingSource:

DataSource property

projectBindingSource.DataSource = Project.NewProject();

This line of code not only provides the business object as a data source for the form, but it causes
the BindingSource control to immediately call BeginEdit() on the object.
Working with a list of objects is slightly more complex:
projectListBindingSource.DataSource = ProjectList.GetList();

When the data source is a list, the concept of currency becomes important. Only one item in the
list is the current item, and this is tracked by the BindingSource control. As soon as the DataSource
property is set, the current items BeginEdit() method is called.
Lets assume that this list is displayed in a grid control. If the user moves to another row in the
grid, the BindingSource automatically calls EndEdit() (which calls ApplyEdit()) on the old child
object, and calls BeginEdit() on the new object.
If the user presses the ESC key while in a grid, the BindingSource calls CancelEdit() on the
current child object, and immediately calls BeginEdit() on that same child object.
What you should observe from this is that the current object (whether a single object, or the
current object in a list) is always in edit mode. That is to say that the current object has always had
BeginEdit() called on it as long as the object is bound to a BindingSource control.

Unbinding from an Object


It might seem that unbinding an object from a BindingSource control would be as simple as this:

Using CSLA .NET 3.0


Copyright 2007 Rockford Lhotka

Page 115

projectBindingSource.DataSource = null;

Unfortunately thats not the case. The reason it is more complex, is that setting the DataSource to
null doesnt cause the BindingSource to call either EndEdit() or CancelEdit() on the object. This
means that the current object is left in edit mode. The following helper method addresses this issue:
protected void UnbindBindingSource(BindingSource source, bool apply, bool isRoot)
{
System.ComponentModel.IEditableObject current =
source.Current as System.ComponentModel.IEditableObject;
if (isRoot)
source.DataSource = null;
if (current != null)
if (apply)
current.EndEdit();
else
current.CancelEdit();
}

You can find this method in the WinPart class in the PTWin project.
The source parameter is the BindingSource to unbind. The cancel parameter indicates whether to
accept or cancel edits to the current object, and the isRoot parameter indicates whether this is a root
or child BindingSource control.
The UnbindBindingSource() method finds the current object and calls CancelEdit() or
EndEdit() as appropriate. The result is that the objects edit level is reduced by one as it is unbound
from the BindingSource control.
Only a root BindingSource should be set back to null. If the BindingSource is a child
BindingSource, then its DataSource property must be reset to point to its parent BindingSource
control. This comes into play if your UI uses parent-child displays of data.
In the ProjectEdit form, for example, projectBindingSource is a root control, while
a child control, because it gets its data from the projectBindingSource.
To unbind these controls, use code like the following:
resourcesBindingSource is

UnbindBindingSource(this.resourcesBindingSource, true, false);


UnbindBindingSource(this.projectBindingSource, true, true);
this.resourcesBindingSource.DataSource = this.projectBindingSource;

The first two lines unbind the objects from the BindingSource controls by calling the
UnbindBindingSource() helper method.
Note: You must unbind the child first, then the parent.
The third line finishes the unbinding process by resetting the child
now empty, parent BindingSource .

BindingSource

to point to the,

The result of this process is that the objects are no longer connected to the BindingSource
controls, and thanks to the code in UnbindBindingSource(), the edit levels of the objects have been
set to appropriate values as well.

Using CSLA .NET 3.0


Copyright 2007 Rockford Lhotka

Page 116

Implementing an Apply Button


An Apply button saves any changes made by the user, but leaves the user on the same form, ready
to continue editing the same object. This is more complex than the Save button Ill discuss next,
because the UI must be unbound from the business object, the object is then saved, and the resulting
new business object is then rebound to the UI:
private void ApplyButton()
{
using (StatusBusy busy = new StatusBusy("Saving..."))
{
// stop the flow of events
this.projectBindingSource.RaiseListChangedE vents = false;
this.resourcesBindingSource.RaiseListChangedEvents = false;
// commit edits in memory
UnbindBindingSource(this.resourcesBindingSource, true, false);
UnbindBindingSource(this.projectBindingSource, true, true);
try
{
// clone object and save clone
Project temp = _project.Clone();
_project = temp.Save();
// rebind the UI
this.projectBindingSource.DataSource = null;
this.resourcesBindingSource.DataSource = this.projectBindingSource;
this.projectBindingSource.DataSource = _project;
ApplyAuthorizationRules();
}
catch (Csla.DataPortalException ex)
{
MessageBox.Show(ex.BusinessException.ToString(),
"Error saving", MessageBoxButtons.OK,
MessageBoxIcon.Exclamation);
}
catch (Exception ex)
{
MessageBox.Show(ex.ToString(),
"Error Saving", MessageBoxButtons.OK,
MessageBoxIcon.Exclamation);
}
finally
{
this.projectBindingSource.RaiseListChangedEvents = true;
this.resourcesBindingSource.RaiseListChangedEvents = true;
this.projectBindingSource.ResetBindings(false);
this.resourcesBindingSource.ResetBindings(false);
}
}
}

The highlighted lines of code deal with data binding.


First, event processing is disabled, so any events raised by the objects during this process dont
trigger events in the UI itself:
this.projectBindingSource.RaiseListChangedEvents = false;
this.resourcesBindingSource.RaiseListChangedEvents = false;

Then the objects are unbound from the UI, committing any edits made to the current objects:
UnbindBindingSource(this.resourcesBindingSource, true, false);
UnbindBindingSource(this.projectBindingSource, true, true);

Using CSLA .NET 3.0


Copyright 2007 Rockford Lhotka

Page 117

Once the object has been saved, the new object returned from the Save() call is then bound to the
UI. This is actually a two-step process, the first step being to restore the BindingSource controls to
their original state:
this.projectBindingSource.DataSource = null;
this.resourcesBindingSource.DataSource = this.projectBindingSource;

And then the new binding is established:


this.projectBindingSource.DataSource = _project;

Finally, event processing is turned back on, and a data refresh is forced so the UI displays the data
from the newly saved object:
this.projectBindingSource.RaiseListChangedEvents = true;
this.resourcesBindingSource.RaiseListChangedEvents = true;
this.projectBindingSource.ResetBindings(false);
this.resourcesBindingSource.ResetBindings(false);

The user is then left on the same form, able to edit the object further if they choose.

Implementing a Save Button


Implementing a Save button is a little simpler than implementing the Apply button, because no
rebinding is required. Typically a Save button will save the data, and close the form:
private void SaveButton()
{
using (StatusBusy busy = new StatusBusy("Saving..."))
{
// stop the flow of events
this.projectBindingSource.RaiseListChangedEvents = false;
this.resourcesBindingSource.RaiseListChangedEvents = false;
// commit edits in memory
UnbindBindingSource(this.resourcesBindingSource, true, false);
UnbindBindingSource(this.projectBindingSource, true, true);
try
{
// clone object and save clone
Project temp = _project.Clone();
_project = temp.Save();
}
catch (Csla.DataPortalException ex)
{
MessageBox.Show(ex.BusinessException.ToString(),
"Error saving", MessageBoxButtons.OK,
MessageBoxIcon.Exclamation);
}
catch (Exception ex)
{
MessageBox.Show(ex.ToString(),
"Error Saving", MessageBoxButtons.OK,
MessageBoxIcon.Exclamation);
}
}
this.Close();
}

Using CSLA .NET 3.0


Copyright 2007 Rockford Lhotka

Page 118

As you can see, the event processing is disabled and the objects are unbound from the
BindingSource controls. Then the object is saved and the form is closed. So this is the same basic
process as the Apply button, but without the work involved for rebinding the UI.

Implementing a Cancel Button


A Cancel button works much like an Apply button, but in reverse. The edits made by the user are
undone, and the user is left on the form so they can continue to edit the object:
private void CancelButton()
{
// disable events
this.projectBindingSource.RaiseListChangedEvents = false;
this.resourcesBindingSource.RaiseListChangedEvents = false;
// unbind the UI
UnbindBindingSource(this.resourcesBindingSource, false, false);
UnbindBindingSource(this.projectBindingSource, false, true);
this.resourcesBindingSource.DataSource = this.projectBindingSource;
// rebind the UI
this.projectBindingSource.DataSource = _project;
// restore events
this.projectBindingSource.RaiseListChangedEvents = true;
this.resourcesBindingSource.RaiseListChangedEvents = true;
// refresh the UI
this.projectBindingSource.ResetBindings(false);
this.resourcesBindingSource.ResetBindings(false);
}

First, the event processing is turned off so the UI doesnt try to refresh during the rest of the
process:
this.projectBindingSource.RaiseListChangedEvents = false;
this.resourcesBindingSource.RaiseListChangedEvents = false;

Next, the objects are unbound from the BindingSource controls:


UnbindBindingSource(this.resourcesBindingSource, false, false);
UnbindBindingSource(this.projectBindingSource, fakse, true);
this.resourcesBindingSource.DataSource = this.projectBindingSource;

Again, it is critically important that the child BindingSource be unbound before the parent or root
unbound, the child BindingSource should be reset to point to

BindingSource. Once the controls are


the now-null parent BindingSource.

Notice that the second parameter to UnbindBindingSource() method is true, indicating that
CancelEdit() should be called to undo any changes to the objects. At this point the objects have not
only been unbound, but theyve been restored to their original states.
Now the objects can be rebound to the UI:
this.projectBindingSource.DataSource = _project;

Finally, event handling can be turned back on and a data refresh is forced to ensure that the UI
displays the data from the objects:
Using CSLA .NET 3.0
Copyright 2007 Rockford Lhotka

Page 119

// restore events
this.projectBindingSource.RaiseListChangedEvents = true;
this.resourcesBindingSource.RaiseListChangedEvents = true;
// refresh the UI
this.projectBindingSource.ResetBindings(false);
this.resourcesBindingSource.ResetBindings(false);

The result is that the users changes are undone, and the user is left on the same form, ready to
continue to edit the objects data.

Implementing a Close Button


A Close button is similar to Cancel, but it closes the form. This means that no rebinding is
necessary in this case:
private void CloseButton()
{
// disable events
this.projectBindingSource.RaiseListChangedEvents = false;
this.resourcesBindingSource.RaiseListChangedEvents = false;
// unbind the UI
UnbindBindingSource(this.resourcesBindingSource, false, false);
UnbindBindingSource(this.projectBindingSource, false, true);
this.resourcesBindingSource.DataSource = this.projectBindingSource;
this.Close();
}

The code here is the same as the start of the Cancel button. The event handling is turned off and
the objects are unbound from the BindingSource controls. The UnbindBindingSource() method calls
CancelEdit() on the current objects, ensuring that any changes to those objects are undone.
The result is that all edits to the objects are undone, and the form is closed.
You might consider just directly closing the form:
private void CloseButton()
{
this.Close();
}

If you take this approach, remember that the current business object for each BindingSource
control will still be at edit level 1. If your business object is only used by this one form you can get
away with this simpler technique, because the business object cant be used by any other code.
However, if any other code in your application has a reference to the business object, simply
closing the form will leave the business objects edit levels in a confusing state and the result will be
a bug that is very difficult to track down and fix.

Disabling IEditableObject
Data binding relies on the IEditableObject interface from System.ComponentModel to do much of
the n-level undo work. Some people want to use elements of data binding, but dont want data
binding to automatically call the n-level undo methods.

Using CSLA .NET 3.0


Copyright 2007 Rockford Lhotka

Page 120

BusinessBase implements IEditableObject, and in CSLA .NET 3.0 it now includes a protected
property called DisableIEditableObject that you can set to true to disable that interface. If this

property is set to true , all method calls through this interface are simply ignored. For example:
private Project()
{
this.DisableIEditableObject = true;
}

If you disable IEditableObject, your users will not get the expected behaviors from in-place
editing in a grid control. Due to this, I strongly recommend against using any grid controls if you
disable IEditableObject in your business objects.
There may be other undesired side-effects from disabling IEditableObject, depending on exactly
how you use data binding. If you think you want to use this property, I strongly urge extensive
testing in your scenarios to ensure that you dont encounter such issues. It is important to realize that
data binding expects the interface to work, and so disabling it means you are entering uncharted and
unsupported territory.

BusinessListBase Bug Fixes


The BusinessListBase class contains quite a lot of functionality to support data binding. Several
areas of functionality have been fixed in CSLA .NET 3.0:
1. When removing or setting an item value in the list, the ListChanged event is now raised
after the item has been removed or replaced. This way any code handling the event can
interact with the list and new item appropriately.
2. When setting an item value in the list, the EditLevel and EditLevelAdded values are
properly set for the new item, ensuring that those values are in sync with the list.
3. When removing an item from the list, the items EditLevel is lowered to match the lists
edit level. This avoids an issue with in-place editing in a grid where that childs EditLevel
would otherwise be out of sync.
4. After deserialization (such as after cloning or when the object comes through the data
portal from an application server), the ListChanged event is now raised such that it
includes the PropertyDescriptor for the changed property. This matches the behavior of
the ListChanged event before deserialization.
5. During an undo operation, deleted items that should be undeleted are completely
undeleted before the ListChanged event is raised. This way the code handling the event
can interact with the lists IsDirty property, which will be correct before the event is
raised.
These changes involve fairly deep technical changes within BusinessListBase itself, and the only
result you will see is that in-place data binding of a list in a Windows Forms grid should work better
than before.

Using CSLA .NET 3.0


Copyright 2007 Rockford Lhotka

Page 121

Chapter 7:
Miscellaneous
CSLA .NET 3.0 includes a number of smaller, miscellaneous changes and enhancements. The
majority of these changes are due to suggestions made on the CSLA .NET forum at
http://forums.lhotka.net, and were listed on the wish list at www.lhotka.net/cslanet/wishlist.aspx.

SmartDate
The SmartDate type now has a TryParse() method to better match the behavior of DateTime.
The new TryParse() method works like other Microsoft .NET TryParse() methods, allowing
code to attempt to parse a value without an exception if the parse fails. The following code illustrates
how to use this method:
SmartDate dt;
if (SmartDate.TryParse("1/1/2008", ref dt))
// dt is now a valid SmartDate
else
// parse failed - dt does not contain a parsed value

Using TryParse() can be more efficient than calling Parse(), because a parse failure doesnt
result in an exception.

SortedBindingList
The SortedBindingList class now includes a property to expose a reference to the original
underlying list object. It also includes a fix to a bug when sorting null key values.

Obtaining Reference to Original List


A SortedBindingList is just a sorted view over an original list object. The new
provides access to that underlying list object:

SourceList

property

List<string> original = new List<string>();


SortedBindingList<string> sortedList = new SortedBindingList<string>(original);
IList<string> source = sortedList.SourceList;

The SourceList property returns a value of IList<T> , where T is the type of object contained in
the list. You can cast this value to your original list type, because it really is just a reference to the
original list:
List<string> sourceList = (List<string>)sortedList.SourceList;

The SourceList property is useful, because UI code may not retain an explicit reference to the
source list, but may still need that reference later in the application. This property allows the UI code
to maintain only a reference to the SortedBindingList, and to still be able to get back to that source
list object.

Using CSLA .NET 3.0


Copyright 2007 Rockford Lhotka

Page 122

CompareTo Method Bug Fix


The CompareTo() implementation did not handle null key values properly when sorting. The new
behavior is to always treat a null value as smaller than any non-null value. This means that in an
ascending sort, null key values always sort at the top of the list.

FilteredBindingList
The FilteredBindingList class now includes a property to expose a reference to the original
underlying list object. A FilteredBindingList is just a filtered view over an original list object. The
new SourceList property provides access to that underlying list object:
List<string> original = new List<string>();
FilteredBindingList<string> filteredList = new FilteredBindingList<string>(original);
IList<string> source = filteredList.SourceList;

The SourceList property returns a value of IList<T> , where T is the type of object contained in
the list. You can cast this value to your original list type, because it really is just a reference to the
original list:
List<string> sourceList = (List<string>)filteredList.SourceList;

The SourceList property is useful, because UI code may not retain an explicit reference to the
source list, but may still need that reference later in the application. This property allows the UI code
to maintain only a reference to the FilteredBindingList, and to still be able to get back to that
source list object.

NameValueListBase
The NameValueListBase class now includes GetKey() and GetValue() methods to make it easier to
translate keys to values and vice versa:
RoleList _roles = RoleList.GetList();
int key = _roles.GetKey("Project manager");
string text = _roles.GetValue(1);

The GetKey() method accepts a value and returns the key corresponding to the first matching
value in the list. If there are duplicate matches in the list, only the first one is returned.
The GetValue() method accepts a key and returns the value corresponding to that key.

Data Portal
The client-side DataPortal class now includes a ReleaseProxy() method. This method immediately
causes the data portal to de-reference any cached data portal proxy. It also now supports a new
CslaAutoCloneOnUpdate configuration setting that causes the data portal to clone business objects
before performing an Update() operation, so the cloning process doesnt need to be done by hand in
the UI code.

Using CSLA .NET 3.0


Copyright 2007 Rockford Lhotka

Page 123

ReleaseProxy Method
The client-side DataPortal caches the data portal proxy reference as a performance optimization.
This means that after the first data portal call made by an application, the appropriate data portal
proxy object (based on the config file settings) is loaded and maintained in memory.
Normally this is desirable, since the same proxy can be reused throughout the life of the
application. However, if the applications config settings are changed while the application is
running, the proxy must be released and recreated to use the new settings.
It is important to realize that there is no elegant way to change the applications settings at
runtime. The System.Configuration implementation provided in .NET automatically caches the
settings values in memory, and doesnt reload them even if the underlying config file is changed.
Note: In a web application, when web.config is changed the settings are not reloaded
into the existing AppDomain. Instead, ASP.NET shuts down the old AppDomain and
starts an entirely new one, effectively restarting the application from scratch. Due to
this, you probably dont need to call ReleaseProxy() in a web environment, because
all objects are destroyed and recreated automatically.
However, if you develop your own scheme by which application settings can be changed or
reloaded as the application is running (a topic outside the scope of this book), you can use
ReleaseProxy() to force the client-side DataPortal to reload the data portal proxy object using the
new settings:
DataPortal.ReleaseProxy();

Once this method has been called, the next data portal call will cause a new proxy object to be
created and cached.

Automatic Cloning on Update


When building applications, especially when using data binding in Windows Forms or WPF, it is a
best practice to clone the business object before calling Save():
try
{
Project tmp = _project.Clone();
_project = tmp.Save();
}
catch (Exception ex)
{
// handle exception
}
// use _project field

The reason for cloning the object is that the original object instance is unchanged during the save,
because the clone is being saved. If theres an exception part way through the save process, the field
(_project in this example) is unchanged. Effectively, an exception means that the object in memory
is reset to the state it was in before the save operation was attempted.
This only matters when you are using a local data portal configuration. If you are using a remote
data portal configuration then the business object is cloned across the network and the save occurs

Using CSLA .NET 3.0


Copyright 2007 Rockford Lhotka

Page 124

on the application server. But with a local data portal configuration the save occurs on the client
workstation, and it is the actual object instance that is saved.
So the complete best practice is to clone the object only when using a local data portal and not
when using a remote data portal.
Making the UI developer worry about these details is against the philosophy of CSLA .NET. A
primary goal of CSLA .NET is to minimize the code and complexity of the UI.
To help resolve this issue, CSLA .NET now supports a new configuration setting,
This setting is used in the clients app.config or web.config:

CslaAutoCloneOnUpdate.

<appSettings>
<add key="CslaAutoCloneOnUpdate" value="True" />
</appSettings>

This setting defaults to False for backward compatibility. If it is False, the UI developer must
manage the cloning manually.
If the setting is True however, the data portal will automatically clone the object before doing any
update operation, but only if the data portal is configured to be local.
Note: In a future version of CSLA .NET the default for this setting will probably
change to True .
So if the setting is True, then the UI code is simpler:
try
{
_project = _project.Save();
}
catch (Exception ex)
{
// handle exception
}
// use _project field

The object will be cloned only if necessary, and in all cases an exception during the Save()
method will result in _project still pointing to the original, unchanged, object as it was before
Save() was called.
CSLA .NET version 3.0 provides enhancements to version 2.1.4, and important new features for
support of Microsoft .NET 3.0. The primary focus is on supporting both WCF and WPF. However, a
number of other enhancements have been made that support some important scenarios that many
people encounter when using CSLA .NET to build applications.

Using CSLA .NET 3.0


Copyright 2007 Rockford Lhotka

Page 125

Index
A
AddAuthorizationRules() method..........................111
AddBusinessRules() method..................................107
AddNewCore() method............................................61
AdornedElementPlaceHolder element....................68
AllowNew property..................................................61
ApplicationCommands.New command ...................59
ApplicationCommands.Save command.............59, 60
ApplicationCommands.Undo command............59, 60
Authorizer control .................................46, 69, 70, 72
B
BinaryFormatter ....................................18, 19, 89, 90
Binding expression.................................52, 60, 74, 76
BindingSource control ... 114, 115, 116, 118, 119, 120
BindsDirectToSource property ................................55
BusinessListBase class............................................121
C
CanExecuteMethod() method ...............................111
code activities ..............................................80, 83, 88
ComboBox control.......................................62, 63, 64
CommandTarget property.......................................60
Converter property..................................................76
Csla.Wpf namespace ................. 51, 65, 69, 70, 73, 76
CslaAutoCloneOnUpdate...............................123, 125
CslaDataProvider control.... 46, 50, 51, 52, 54, 55, 56,
58, 59, 60, 61, 62, 64, 71
D
DataChanged event .................................................62
DataContract attribute ....................18, 19, 20, 23, 89
DataPortal class .....................................................123
DataSource property .....................................115, 116
DeferRefresh() method............................................57
dependency event...................................................79
dependency events..................................................79
dependency properties 73, 79, 80, 82, 83, 85, 87, 88,
89
dependency property ... 71, 81, 82, 83, 84, 85, 87, 88,
96, 98
DisableIEditableObject ..........................................121
DisplayMemberPath property .................................63

ErrorTemplate property ..........................................67


Execute() method ..............................................79, 85
external activities ..............................................83, 86
F
FactoryMethod property.........................................58
FactoryParameters collection............................55, 58
FilteredBindingList class.........................................123
format mask...................................................101, 104
G
GetPropertyName() method .................................103
Grid control........................................................52, 56
H
HasErrors property ..................................................67
I
IAuthorizeReadWrite interface................................69
IBindingList interface...............................................61
IDataErrorInfo interface ....................................64, 66
IdentityConverter control..................................46, 76
IEditableObject interface.......................................120
IsAsynchronous property.........................................56
IsChecked property..................................................74
IsInitialLoadEnabled property............................55, 57
IsReadOnly property................................................72
ItemsSource property..............................................63
L
ListChanged event ...........................................61, 121
M
ManageObjectLifetime property .............................59
N
n-level undo... 7, 9, 19, 20, 49, 50, 113, 114, 115, 120
NotSerialized attribute ............................................89
NotVisibleMode property........................................71
NullResultOptions enumerated list ...............102, 109
O
ObjectStatus control..........................................73, 74
OptionalField attribute ......................................89, 90

EditLevel ................................................113, 114, 121


Error property....................................................62, 93
ErrorProvider control...................................46, 64, 69

Path property.....................................................55, 74
ProcessThroughPriority property ..................104, 105
PropertyChanged event...............................72, 73, 75

Using CSLA .NET 3.0


Copyright 2007 Rockford Lhotka

Page 126

PropertyDescriptor ................................................121
PropertyFriendlyName property ...........................103
R
ReadWriteAuthorization control .......................46, 69
RelativeSource binding............................................74
ReleaseProxy() method .........................................123
RuleArgs class ........................................................103
RuleDescription class.............................................110
S
SelectedValue property...........................................64
SelectedValuePath property....................................64
sequential workflow ..........................................80, 83
Serializable attribute ....................... 12, 18, 19, 20, 89
short-circuiting ......................................101, 104, 105
SmartDate type......................................................122
SortedBindingList class ..........................................122
Source property.......................................................55
SourceList property .......................................122, 123
StopProcessing property .......................................105
StringMinLength method.......................................102
System namespace ............................................53, 54
T

TryParse() method.................................................122
U
UnbindBindingSource() method............116, 119, 120
unloadOnIdle parameter.........................................94
UpdateSourceTrigger property................................55
use case ...................................................................77
V
Validator control............ 46, 64, 65, 66, 67, 68, 69, 70
value converter..................................................75, 76
Value property...................................................63, 64
W
Windows Communication Foundation....................12
Windows Presentation Foundation.........................46
Windows Workflow Foundation........................19, 77
workflow activity ...................................77, 79, 81, 84
workflow instance .......................................90, 93, 95
workflow runtime instance................................90, 94
WorkflowManager object........................................91
X
x

Key property .................................................52, 57

Text property...........................................................55
TextBox control............................................55, 67, 72

Using CSLA .NET 3.0


Copyright 2007 Rockford Lhotka

Page 127

You might also like