Using CSLA 4 Creating Business Objects

317226777

贡献于2015-06-03

字数:0 关键词: .NET开发

Using CSLA 4 Creating Business Objects Rockford Lhotka Using CSLA .NET 4: Creating Business Objects Copyright © 2011-2012 by Marimer LLC 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 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 4 version 4.1.0) is available at http://www.lhotka.net/cslanet. Errata or other comments about this book should be emailed to errata@lhotka.net. Revision: 1.1 Acknowledgements Neither this book, nor CSLA 4, would have been possible without 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 and feedback provided by this stellar community. Thank you all! About the Author Rockford Lhotka is the author of more than 17 books on developing software using the Microsoft platform and technologies. He is a Microsoft Regional Director and a Microsoft MVP. Rockford speaks at many conferences and user groups around the world. He is the Chief Technology Officer at Magenic (www.magenic.com), a company that specializes in planning, designing, building and maintaining your enterprise’s most mission critical systems. Using CSLA 4: Creating Business Objects Page i Copyright © 2012 Marimer LLC Contents Introduction ........................................................................................................................................................... 1 Organization of the Book .......................................................................................................................................... 1 Framework Code and Samples .................................................................................................................................. 1 Chapter 1: Key Object Concepts ............................................................................................................................. 3 Stereotypes ............................................................................................................................................................... 3 Serialization .............................................................................................................................................................. 4 Choosing Serializers ............................................................................................................................................. 5 Configuring CSLA .NET to use NetDataContractSerializer ............................................................................... 5 Configuring CSLA .NET to use MobileFormatter ............................................................................................. 6 Object Lifetime .......................................................................................................................................................... 6 Object Relationships ................................................................................................................................................. 6 Containment ........................................................................................................................................................ 7 Using .................................................................................................................................................................... 7 Object Identity and Equality ...................................................................................................................................... 8 Platform Differences in .NET and Silverlight ............................................................................................................. 9 Limitations on Reflection ..................................................................................................................................... 9 Asynchronous Server Access .............................................................................................................................. 10 Threading ........................................................................................................................................................... 11 Data Access ........................................................................................................................................................ 11 Property Declarations ............................................................................................................................................. 12 Basic CSLA 4 Property Concepts ......................................................................................................................... 13 Basic Property Implementation..................................................................................................................... 13 Helper Methods ............................................................................................................................................ 14 RegisterProperty and PropertyInfo ......................................................................................................... 14 Manual and Managed Backing Fields ............................................................................................................ 17 Properties and Stereotypes................................................................................................................................ 18 Types of Property Declaration ........................................................................................................................... 18 Read-Write .................................................................................................................................................... 19 Read-Write with No Rules ............................................................................................................................. 22 Read-Write with Value Conversion ............................................................................................................... 23 Read-Only ...................................................................................................................................................... 25 Read-Only with Value Conversion ................................................................................................................. 25 Child Object Reference .................................................................................................................................. 26 Child Object Reference with Lazy Loading .................................................................................................... 27 Properties with Manual Backing Fields ......................................................................................................... 30 Inter-Graph Reference .................................................................................................................................. 35 Using Reference ............................................................................................................................................ 35 Non-Generic LoadProperty Method .............................................................................................................. 37 Method Declarations .............................................................................................................................................. 37 Using CSLA 4: Creating Business Objects Page ii Copyright © 2012 Marimer LLC Metastate ............................................................................................................................................................... 37 Accessing Metastate .......................................................................................................................................... 39 Public Properties ........................................................................................................................................... 39 ITrackStatus Interface.................................................................................................................................... 40 INotifyBusy Interface ..................................................................................................................................... 40 INotifyChildChanged Interface ...................................................................................................................... 40 Data Binding .................................................................................................................................................. 40 Basic Status ........................................................................................................................................................ 40 IsNew Property .............................................................................................................................................. 40 IsDeleted Property ........................................................................................................................................ 41 IsSavable Property ......................................................................................................................................... 41 MarkDeleted and Delete Methods ................................................................................................................ 42 MarkNew Method ......................................................................................................................................... 42 MarkOld Method ........................................................................................................................................... 42 Change Tracking ................................................................................................................................................. 43 IsDirty Property ............................................................................................................................................. 43 IsSelfDirty Property ....................................................................................................................................... 43 MarkClean Method ....................................................................................................................................... 44 PropertyHasChanged Method ....................................................................................................................... 44 MarkDirty Method ........................................................................................................................................ 44 Object Validity .................................................................................................................................................... 45 IsValid Property ............................................................................................................................................. 45 IsSelfValid Property ....................................................................................................................................... 45 Busy Tracking ..................................................................................................................................................... 45 IsBusy Property.............................................................................................................................................. 45 IsSelfBusy Property ........................................................................................................................................ 46 IsPropertyBusy Method ................................................................................................................................. 46 BusyChanged Event ....................................................................................................................................... 46 MarkBusy Method ......................................................................................................................................... 46 MarkIdle Method .......................................................................................................................................... 47 Chapter 2: Solution Structure .............................................................................................................................. 48 Project Types and Assembly References ................................................................................................................. 48 Combining Projects to Create Solutions .................................................................................................................. 49 Reusing Business Assemblies Across Platforms ................................................................................................. 50 Controlling the Assembly and Namespace Names ........................................................................................ 51 Linking Files ................................................................................................................................................... 52 Chapter 3: Object Stereotypes ............................................................................................................................. 55 Editable Objects ...................................................................................................................................................... 55 Editable Root and Child ...................................................................................................................................... 55 Editable Root and Child List ............................................................................................................................... 59 Adding New Items ......................................................................................................................................... 60 Dynamic Root List and Dynamic Root ................................................................................................................ 63 Dynamic Root ................................................................................................................................................ 64 Dynamic Root List .......................................................................................................................................... 64 Read-Only Objects ................................................................................................................................................... 66 Read-Only Root and Child .................................................................................................................................. 66 Read-Only Root and Child List ............................................................................................................................ 67 Name-Value List ................................................................................................................................................. 68 Using CSLA 4: Creating Business Objects Page iii Copyright © 2012 Marimer LLC Execution Objects .................................................................................................................................................... 70 Command ........................................................................................................................................................... 70 Unit of Work ....................................................................................................................................................... 72 Data Retrieval ................................................................................................................................................ 72 Data Update .................................................................................................................................................. 74 Criteria Objects ....................................................................................................................................................... 75 Simple Criteria .................................................................................................................................................... 75 Complex Criteria ................................................................................................................................................. 76 Using CriteriaBase ......................................................................................................................................... 76 Using BusinessBase ....................................................................................................................................... 77 LINQ Types .............................................................................................................................................................. 77 LinqObservableCollection .................................................................................................................................. 78 Windows Forms Types ............................................................................................................................................ 78 BusinessBindingListBase .................................................................................................................................... 80 DynamicBindingListBase .................................................................................................................................... 80 ReadOnlyBindingListBase ................................................................................................................................... 80 Chapter 4: Business Rules .................................................................................................................................... 82 Business and Validation Rules ................................................................................................................................. 83 Associating Rules with Properties and Types ..................................................................................................... 84 Using DataAnnotations Attributes ................................................................................................................ 84 Using AddBusinessRules ................................................................................................................................ 84 Rule Priorities ................................................................................................................................................ 85 Short-Circuiting Rules .................................................................................................................................... 86 Rule Sets ........................................................................................................................................................ 87 Executing Rules .................................................................................................................................................. 88 Per-Property Rules ........................................................................................................................................ 88 Dependent Properties ................................................................................................................................... 88 Per-Type Rules ............................................................................................................................................... 90 Manually Executing Rules .............................................................................................................................. 90 Executing Rules in Parent Objects ................................................................................................................. 91 Suppressing Rule Execution........................................................................................................................... 91 Implementing Business and Validation Rules .................................................................................................... 93 Structure of a Rule ......................................................................................................................................... 93 RuleContext Parameter ................................................................................................................................. 94 IBusinessRule Interface ................................................................................................................................. 96 BusinessRule Base Class ................................................................................................................................ 97 Rule State ...................................................................................................................................................... 99 Rule Chaining ............................................................................................................................................... 100 Asynchronous Rules .................................................................................................................................... 101 Implementing Business Rules........................................................................................................................... 108 Using the Target Property ........................................................................................................................... 109 Interacting with the Entire Object Graph .................................................................................................... 110 Implementing Validation Rules ........................................................................................................................ 110 Broken Rules Collection ............................................................................................................................... 110 Validation Rule Classes ................................................................................................................................ 111 DataAnnotations Attributes ........................................................................................................................ 112 Accessing Rule Information and Results .......................................................................................................... 113 Get a List of Rules for a Type ....................................................................................................................... 114 Get Rule Results from an Object ................................................................................................................. 115 Using CSLA 4: Creating Business Objects Page iv Copyright © 2012 Marimer LLC Authorization Rules ............................................................................................................................................... 117 Associating Rules with Properties and Types ................................................................................................... 119 Using AddBusinessRules .............................................................................................................................. 119 Using AddObjectAuthorizationRules ........................................................................................................... 120 Rule Sets ...................................................................................................................................................... 121 Executing Rules ................................................................................................................................................ 121 Manually Executing Rules ............................................................................................................................ 122 Suppressing Rule Execution......................................................................................................................... 123 Implementing Authorization Rules .................................................................................................................. 124 Structure of a Rule ....................................................................................................................................... 124 AuthorizationContext Parameter ................................................................................................................ 125 IAuthorizationRule Interface ....................................................................................................................... 126 AuthorizationRule Class............................................................................................................................... 127 Rule State .................................................................................................................................................... 127 Conclusion ......................................................................................................................................................... 128 Using CSLA 4: Creating Business Objects Page v Copyright © 2012 Marimer LLC List of Tables Table 1. Stereotypes directly supported by CSLA 4 4 Table 2. Kinds of object relationship 7 Table 3. Scenarios where reflection limitations affect code 10 Table 4. Asynchronous features in CSLA 4 10 Table 5. Property getter and setter helper methods 14 Table 6. Elements defined by IPropertyInfo in Csla.Core 16 Table 7. Types of property declaration supported by CSLA 4 19 Table 8. Types directly serialized by MobileFormatter 33 Table 9. Serialization base classes in Csla.Core 34 Table 10. Metastate properties of CSLA .NET business objects 38 Table 11. Events raised by CSLA .NET business objects 39 Table 12. Common project types and related CSLA assembly references 48 Table 13. Typical projects in a WPF Application 49 Table 14. Typical proejcts in a 4-tier Silverlight Application 50 Table 15. Execution sequence of a command object 71 Table 16. Collection types and UI technologies 79 Table 17. Types of business rule supported by CSLA 4 82 Table 18. Methods used to manually execute rules 90 Table 19. Techniques for suppressing business rule execution 93 Table 20. Members provided by the RuleContext type 95 Table 21. Members defined in IBusinessRule interface 96 Table 22. Examples of rule:// URI values 97 Table 23. Properties available on a BrokenRule object 116 Table 24. Properties available on a BrokenRulesNode object 117 Table 25. Members defined by the AuthorizationContext type 125 Table 26. Members defined by the IAuthorizationRule interface 126 List of Figures Figure 1. Process followed when setting a property value ........................................................... 21 Figure 2. Setting the assembly and namespace names in a .NET project ..................................... 51 Figure 3. Setting the assembly and namespace names in a Silverlight project ............................. 51 Figure 4. Setting the assembly and namespace names in a Windows Phone project ................... 52 Figure 5. Class library containing code files ................................................................................ 52 Figure 6. Add an existing item ...................................................................................................... 53 Figure 7. Adding files as a link ..................................................................................................... 53 Figure 8. Project containing linked files ....................................................................................... 54 Using CSLA 4: Creating Business Objects Page 1 Rev 1.1 Introduction Welcome to Using CSLA 4: Creating Business Objects. This book will provide you with the information necessary to use the CSLA .NET framework to create the business domain objects that make up a business layer for your application. These domain objects encapsulate behavior and contain the state necessary to implement that behavior. In this context, behavior includes business rules, validation rules, and authorization rules, along with public properties that provide access to select elements of the object’s state. This book is part of a multi-part book series, consisting of several related ebooks. While each ebook is separate, they are designed to work together to provide information about CSLA 4. This book series will show you how to use the CSLA framework to build powerful and scalable applications for Windows, Silverlight and the web. This book builds on the information provided in the Using CSLA 4: CSLA .NET Overview ebook. It is assumed that you have an understanding of the content from that ebook. Organization of the Book This ebook is the second in a series of related ebooks that together comprise the Using CSLA 4 book. Each ebook is separate, but they are designed to work together in a flexible manner so you can get exactly the information you need for your application or project. All subsequent ebooks in the series assume you have read the first two ebooks: Using CSLA 4: CSLA .NET Overview and Using CSLA 4: Creating Business Objects (this book). This book walks through the major base classes provided by the CSLA .NET framework. Each base class is designed to support one or more object stereotypes, or basic types of business object useful in most business applications. The base classes provide functionality you will use when building business classes, including:  Standardized property declarations  Authorization rules  Validation rules  Business rules  Metastate management  Interaction with child and parent objects  Interaction with objects outside the current object graph By the end of this book you will understand the base classes, their related stereotypes and the primary features of each base class supporting these areas of functionality. Framework Code and Samples The CSLA .NET framework and related code samples are available from the download page at: Using CSLA 4: Creating Business Objects Page 2 Rev 1.1 http://www.lhotka.net/cslanet/download.aspx This ebook uses CSLA 4 version 4.1, and I may refer to samples available in the standard Samples download for CSLA .NET. Additionally, in the Support folder provided as part of the CSLA 4 installation you will find a Templates folder that contains very basic sample code illustrating the structure of each business object stereotype described in this ebook. Any code samples unique to this ebook will be included as a separate download available from http://download.lhotka.net This is also where you will have downloaded this ebook. Using CSLA 4: Creating Business Objects Page 3 Rev 1.1 Chapter 1: Key Object Concepts Object-oriented design and programming are large topics, and this book is not a comprehensive discussion of OOD or OOP. All the same, I will be using some key concepts throughout this book, and it is important to ensure we have a shared understanding of these concepts and the terms I’ll be using. Stereotypes Object-oriented design and programming involve the idea of taking similar concepts and implementations and grouping them together into classes and stereotypes. You know about classes, they are bits of code that organize related behaviors together so you can create individual instances of those classes. Stereotypes are a broader concept that group together broad types of behavior. Not necessarily specific behaviors, but specific types of behavior. For example, a class might implement a specific business rule, and all instances of that class (objects) have that rule. A stereotype is broader, suggesting that there’s a type of class that can implement rules. So any class that has a rule is part of this “type that has rules” stereotype. Good object-oriented design relies on stereotypes to help organize such broad concepts into classes, allowing developers to combine those classes through composition, inheritance, or other techniques into reusable and comprehensible types. CSLA .NET relies on this concept of a stereotype to help define how it supports the creation of powerful and flexible business domain objects that compose a business layer. The stereotypes directly supported by CSLA 4 are listed in Table 1. Stereotype Description Base Class Editable root Object containing read-write properties; object can be retrieved/stored directly to database. BusinessBase Editable child Object containing read-write properties; object is contained within another object and can not be retrieved/stored directly to database. BusinessBase Editable root list List object containing editable child objects; list can be retrieved/stored directly to database. BusinessListBase BusinessBindingListBase Editable child list List object containing editable child objects; list is contained within another object and can not be retrieved/stored directly to database. BusinessListBase BusinessBindingListBase Dynamic root list List object containing editable root objects; list is retrieved directly from database. DynamicListBase DynamicBindingListBase Using CSLA 4: Creating Business Objects Page 4 Rev 1.1 Command Object that executes a command on the application server and reports back with the results. CommandBase Unit of Work Object that combines operations against several other business objects. ReadOnlyBase or CommandBase Read-only root Object containing read-only properties; object can be retrieved directly from database. ReadOnlyBase Read-only child Object containing read-only properties; object is contained within another object and can not be retrieved directly from database. ReadOnlyBase Read-only root list List containing read-only child objects; list can be retrieved directly from database. ReadOnlyListBase ReadOnlyBindingListBase Read-only child list List containing read-only child objects; list is contained within another object and can not be retrieved directly from database. ReadOnlyListBase ReadOnlyBindingListBase Name/value list List object containing read-only name/value objects. NameValueListBase Table 1. Stereotypes directly supported by CSLA 4 I will discuss each of these stereotypes in detail in Chapter 3, but first I’ll continue to cover some other key concepts. Serialization All business domain objects created for use with CSLA .NET must be serializable. The term serializable means that the .NET and CSLA .NET frameworks are allowed to pull the field values (even if not public) from a business object instance, and then use those field values to create an exact clone of the original object, either on the same computer or on a computer across the network. This serialization, or cloning, is required by the mobile object concept I discussed in the Using CSLA 4: CSLA .NET Overview ebook. Because mobile objects are a central feature of CSLA .NET, it should make sense that all business types must be serializable to work with CSLA .NET. The .NET framework has numerous types called serializers or formatters that clone object graphs. Only two are capable of creating true clones of objects: the BinaryFormatter and the NetDataContractSerializer (NDCS). Silverlight and WP7 have no serializers that can create true clones, so CSLA .NET includes the MobileFormatter, which provides the necessary functionality on Silverlight, WP7 and .NET. In all cases, to be serializable, you must apply an attribute on your business class to give the runtime permission to serialize your objects. Normally, this is the Serializable attribute. Using CSLA 4: Creating Business Objects Page 5 Rev 1.1 [Serializable] public class CustomerEdit : BusinessBase When using the NDCS, you can choose to use the DataContract and DataMember attributes over the Serializable attribute. I recommend against this, because it prevents the use of the BinaryFormatter or MobileFormatter. Even worse, you must remember to apply the DataMember attribute on every single field declaration in your business class or you won’t get a complete clone of the object. This “opt-in” technique is great for creating public service APIs, but is extra manual work when creating business objects. Later in this ebook, I’ll discuss the recommended property declaration and implementation syntax for use in business types. That syntax is connected to serialization, and specifically, to the way the MobileFormatter serializer works. Whereas the BinaryFormatter and NDCS are able to use reflection to get and set non-public field values in your objects, that type of reflection is prohibited in Silverlight for security reasons. This is the reason those serializers don’t exist in Silverlight. The MobileFormatter serializer avoids this issue by not using reflection, and instead relying on active participation from each object to get and set the field values. CSLA .NET includes several base class types that mostly automate this entire process, so you don’t have to worry about serialization. As I’ll discuss later, you can choose to manually declare your own private fields for your property values. In that case, you assume responsibility for overriding methods to get and set those field values for serialization. Choosing Serializers When running on the .NET platform, the BinaryFormatter is used for serialization by default. When running on Silverlight or WP7 (or if a .NET application server is interacting with a Silverlight client), the MobileFormatter is used for serialization. The MobileFormatter is the only option for Silverlight and WP7 applications. For .NET applications you can choose between the three serializers through configuration. Configuring CSLA .NET to use NetDataContractSerializer For pure .NET applications you can choose to force CSLA .NET to use the NDCS instead of the BinaryFormatter. 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. Because I don’t recommend using the DataContract attribute, I generally don’t recommend forcing the use of NDCS. 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 4: Creating Business Objects Page 6 Rev 1.1 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. The important thing to remember is that all business classes must use Serializable for DataContract to work with CSLA .NET. Throughout the rest of the book I’ll assume the use of the Serializable attribute. Configuring CSLA .NET to use MobileFormatter Similarly, you can specify that CSLA .NET should use its own MobileFormatter instead of the BinaryFormatter for all serialization, even for pure .NET applications. This is done with the following configuration in your app.config or web.config file: Normally the BinaryFormatter is a faster and more efficient serializer, so you shouldn’t override the default. That said, the MobileFormatter avoids the use of reflection and so it may work in medium trust environments where the BinaryFormatter is disallowed. Again, I recommend not overriding the default configuration of the serializer in most application scenarios. Object Lifetime Within the .NET framework all object instances have a lifetime. Typically an object is created using the new keyword, and it is removed from memory by the .NET garbage collector some time after all references to the object have been removed. Business objects created using CSLA .NET often have a slightly more complex lifetime, because it is assumed that these objects often contain data that must be retrieved from a database and later saved into that database. The Using CSLA 4: Data Access ebook will cover these processes in detail. For now you should understand that most business objects are created using the data portal, not the new keyword. This allows CSLA .NET to help you manage the object’s lifetime, including initializing the object’s state and metastate, as I’ll discuss later in this ebook. Object Relationships An object is a specific instance of a class, or type. Objects can have properties that refer to other objects, creating a relationship between those two objects. It is important to understand that there are different kinds of relationship that can exist between objects. Table 2 lists some important kinds of relationship. Relationship Description Containment One object contains another object, such that the contained object is part of the containing object. This is often referred to as a parent-child relationship, because the child is part of the parent. Using CSLA 4: Creating Business Objects Page 7 Rev 1.1 Using One object uses the behaviors of another object, collaboring with that object to accomplish some end goal. In this type of relationship the objects are separate and independent, and neither contains the other. Table 2. Kinds of object relationship The distinction here is important, and both types of relationship are very common in business applications. Containment For example, an OrderEdit object contains a LineItems collection, which in turn contains LineItemEdit objects. These are all containment relationships, and all these objects together form something called an object graph. An object graph is a family of related objects that directly rely on each other to implement their respective behaviors. In this example, the OrderEdit object may calculate a TotalAmount value, and that can only be done given access to the LineItemEdit objects it contains. There are some important terms used to describe the objects in a containment relationship: parent and child. A parent object is an object that contains other objects. A child object is an object that is contained within a parent object. It is important to realize that an object can be both a parent and a child at the same time. For example, consider that the LineItemEdit object could contain a LineItemDetails collection of LineItemDetailEdit objects. In that case the LineItemEdit object is a child of LineItems, but is also a parent. A parent object that is not a child is called a root object. A root object is special, because it is the top of the object graph. It is the one object that isn’t contained within another object, and so it is the root of the object graph. Using A using relationship is quite different. For example, each LineItemEdit object might need to calculate a tax on its price. But the concept of calculating a tax can vary from country to country, and between provinces and states. Rather than embedding the tax calculation in the LineItemEdit class, it is better to have a separate TaxCalculator type, allowing different implementations for different geographic settings. In this case the LineItemEdit objects might use a TaxCalculator to calculate the tax, but there’s no way you could argue that the TaxCalculator is a child of a LineItemEdit object. Not only is there an important conceptual difference here, but the actual code that implements such relationships is different, as you’ll see when I walk through the implementation of the various stereotypes and show how they interact. Using CSLA 4: Creating Business Objects Page 8 Rev 1.1 Object Identity and Equality Each object is a unique instance of a type, and so it has an identity. In fact, objects have more than one “identity”, which can be confusing. The most obvious type of object identity is that the object consumes physical memory in your computer, and any variable or field referring to that object is a pointer to that area of memory. You can find out if two objects are literally the same object in memory with the ReferenceEquals method: bool result = ReferenceEquals(a, b); If result is true then you know that the two fields are pointing to the same object in memory. You can also ask if two objects are equal to each other, but this is a much more complex question. Obviously an object instance is always equal to itself, but what if you have two different instances (and therefore consume different memory), but which share exactly the same public property values? Such objects might be considered equal, but suppose they have different private field values, even though their public properties match? Your application might need to apply a different definition of equality from other applications for various reasons, and so the .NET framework allows you to override the Equals method in your classes, and to implement the IEquatable interface. Prior to .NET 3.0, this was fairly common practice. Now, WPF imposes some very severe restrictions on equality, at least for items contained within collections. For collections to function properly when bound to any WPF UI elements, the items in a collection can implement only a couple forms of equality:  Reference equality  100% property and field equality between both objects For all practical purposes this means that you should avoid overriding Equals, because the default implementation is reference equalty. This is important for CSLA .NET developers, because earlier versions of CSLA .NET enabled the use of something called logical equality, where CLSA .NET made it relatively easy for you to override Equals by implementing a method called GetIdValue in your business classes. You should avoid overriding GetIdValue, as it is now considered deprecated and will be removed in a future version of CSLA .NET. CSLA .NET 3.0 and higher doesn’t support logical equality. The GetIdValue method exists only for backward compatibility. Using CSLA 4: Creating Business Objects Page 9 Rev 1.1 Platform Differences in .NET and Silverlight It is important to understand that Silverlight is basically a subset of .NET. While not a true subset, it is helpful to think about Silverlight andWP7 as having most of the important parts of .NET, plus a few extra features. There are a couple areas where the differences between the platforms will cause direct differences in your business class code.  Limitations on reflection  Asyncronous server access  Threading  Data Access I’ll discuss these differences here, and you’ll see examples of them in Chapter 3 as I walk through each business object stereotype. Limitations on Reflection On the .NET platform reflection can be used to do many things, even interacting with non-public members of an object. For example, you can use reflection to get or set private field values in an object, or invoke a private method, or even create an instance of an object that has no public constructors. The .NET and CSLA .NET frameworks make use of these capabilities to do things like serialize objects and make dynamic method calls. The CSLA .NET framework also uses reflection to get information about business objects, such as retrieving a list of the object’s properties. On the Silverlight and WP7 platforms reflection is restricted for security reasons. It is not possible to use reflection to interact with non-public members of an object. You can use reflection to interact with non-public members in your specific class. Not in a subclass or base class, just your class. And certainly not in other types, which prevents reflection from being useful in any scenario required by CSLA .NET. Starting with CSLA .NET 3.6 the framework has included features to avoid and minimize the use of reflection so the framework could support Silverlight 2 and higher. Table 3 lists some specific places this affects your code. Scenario Description Property implementation CSLA .NET supports two basic ways to implement properties. The concept of managed backing fields (discussed later) allows automatic serialization of objects without using reflection. Member scope In CSLA .NET code for .NET, many members can be private, including the PropertyInfo fields, data portal methods, and the AddObjectAuthorizationRules method. In Silverlight these must be public, otherwise CSLA .NET won’t be able to discover or interact with these members and your business Using CSLA 4: Creating Business Objects Page 10 Rev 1.1 objects won’t function correctly. Constructors In CSLA .NET code for .NET I used to recommend that all business classes have a non-public default constructor. The idea was to remind users of the business class that they should use factory methods, not the new keyword, to create instance of the type. In Silverlight you must supply a default public constructor for serialization to function. Table 3. Scenarios where reflection limitations affect code Because my intent is to have CSLA 4 support .NET, Silverlight and WP7 equally, I typically write my business classes so they work on Silverlight. If they work on Silverlight, they’ll also work on .NET. This means that I typically have a public default constructor, and all my PropertyInfo fields and other members listed in Table 3 are public. Asynchronous Server Access Most developers build applications assuming synchronous access to servers. When you use ADO.NET to get data from a database, this is typically a synchronous call. When you use WCF to call a service on an application server, this is typically a synchronous call. Silverlight specifically requires that all server calls be asynchronous. This is because the Silverlight runtime’s UI thread is the browser’s UI thread, and blocking that thread would block the entire browser hosting Silverlight. That’d obviously generate a lot of negative feelings from end users every time they used Silverlight, so Microsoft chose to require that all server interactions be asynchronous, thus making it very hard for developers to accidentally block the UI thread and lock up the browser. The impact of all server interactions being async is substantial. It affects the way the data portal works, because all calls to an application server are async. Even more, it affects how business rules work, because some business rules need to interact with an application server. This is where the idea of async business rules comes into the picture. If an object or property is executing an async operation, and we don’t yet know the result, that object or property is in an indeterminate state. CSLA 4 supports several asynchronous features as listed in Table 4. Feature Description Asynchronous data portal The data portal can be invoked synchronously or asynchronously on .NET, and only asynchronously on Silverlight and WP7. Asynchronous business rules Business and validation rules can be synchronous or asynchronous. You should use asynchronous rules if your rule needs to interact with the data portal or another object’s factory methods in an async manner. Busy status tracking Business objects have properties such as IsBusy and IsSelfBusy that indicate whether the object (or one of its properties) is currently executing an async operation. Table 4. Asynchronous features in CSLA 4 Using CSLA 4: Creating Business Objects Page 11 Rev 1.1 I typically always implement async factory methods in my classes, and implement synchronous factory methods only for the .NET platform. To do this, I use a compiler directive so the synchronous code doesn’t compile for Silverlight. For example: #if !SILVERLIGHT // .NET-only code goes here #endif You should be aware that asynchronous operations are usually beneficial for any smart client applications, including Silverlight, WPF, WP7, and Windows Forms. The primary benefit comes through the fact that interactions with the server don’t lock up the UI, so the user has a better overall experience. That said, you should keep in mind that asynchronous operations make web development more complex. This is because each web request is a non-interactive request from the browser that normally runs on a single thread. It is critical that this primary thread not complete its work until any async operations are complete, and that doesn’t happen automatically. This means you need to write your own synchronization code to prevent that main thread from completing before all your async operations complete. This leads to complexity, so I recommend avoiding the use of async operations in ASP.NET applications as a general rule. Threading Threading is closely related to asynchronous operations, because the completion callback from a normal async operation in .NET normally occurs on a background thread. You are responsible for moving that callback to the UI thread before interacting with any UI objects, or objects that are data bound to the UI. Silverlight and WP7 avoid this complexity by automatically shifting completion callback calls to the UI thread for you. This is a pretty significant difference between the .NET and Silverlight platforms. CSLA 4 helps you avoid confusion and complexity by also automatically shifting completion callback calls from the data portal and async business rules to the UI thread for you. This means that when using the async features of CSLA 4 you shouldn’t normally have to worry about marshaling calls to the UI thread, as all completion callbacks are already on the UI thread. The exception to this is in a console application, where .NET doesn’t provide a synchronization context, so it isn’t possible to automatically marshal calls onto the UI thread. In Chapter 4, I’ll discuss asynchronous business rules in more detail, including a discussion of where you can write code that will run on a background thread, and where your code is automatically running on the UI thread. Data Access Silverlight has no data access API such as ADO.NET, and so it is not possible to write typical data access code in Silverlight. You can use simple files for data storage through the isolated storage API in Silverlight, but a typical Silverlight application will either use the data portal to interact with a remote application server, or will make remote service calls to a service API by using WCF. Using CSLA 4: Creating Business Objects Page 12 Rev 1.1 I’ll discuss data access in more detail in the Using CSLA 4: Data Access ebook. What is important to understand right now, is that any data access code you might write in your business classes needs to be wrapped in a compiler directive so the classes aren’t built on the Silverlight or WP7 platforms: #if !SILVERLIGHT // .NET-only data access code goes here #endif CSLA 4 tries to abstract the differences between the .NET and Silverlight platforms. Where this isn’t possible, you will need to accommodate the differences in your own code, often using the compiler directive strategy I’ve just discussed. Property Declarations Any .NET developer knows how to declare a standard property using the long and short syntax available in C# or VB. For example: public int MyNumber { get; set; } private string _myText; public string MyText { get { return _myText; } set { _myText = value; } } For simplistic coding scenarios, this code can work fine. The story is very different if you are creating business objects that support data binding, business rules, validation and authorization. In that case, property declarations can become much more complex. As an example, before returning a value in the property getter, you should check to see if the user is authorized to see this value. Similarly, in the property setter you should do all the following:  See if the user is authorized to change the value  See if the new value is different from the existing value  Raise the PropertyChanging event  Record the new value  Run any business and validation rules associated with this property  Run any business and validation rules associated with related properties  Mark the object as having been changed  Raise the PropertyChanged event (using the correct behavior for Windows Forms or other UI environments as appropriate) Using CSLA 4: Creating Business Objects Page 13 Rev 1.1 All of a sudden declaring a property isn’t so simple anymore. Writing all the code to support these behaviors into every property declaration results in huge class files that are hard to read and hard to maintain. Basic CSLA 4 Property Concepts CSLA 4 provides several more abstract concepts to simplify the declaration and implementation of properties and the associated fields that contain the property values. The idea behind these concepts is to support the behaviors required to implement a powerful property, without forcing the business developer to always deal with the related complexity. These concepts include a standardized property declaration syntax that includes property metadata description and helper methods that abstract most of the complex behaviors. Additionally, it is possible to implement properties that use private backing fields, or allow CSLA .NET to automatically manage the value of each property. Some properties reference other objects, implementing containment or using relationships and there are specific property declarations to support those scenarios. Basic Property Implementation For example, to avoid the complexity of implementing a setter as described earlier, CSLA 4 offers an alternative, by providing a more concise and abstract syntax for property declaration. This syntax includes the declaration of a static field that contains metadata about the property (helping to miminize or eliminate the use of reflection) and helper methods that encapsulate the authorization, validation and other behaviors I listed. As a result, a typical read-write property looks like this: public static readonly PropertyInfo MyTextProperty = RegisterProperty(c => c.MyText); public string MyText { get { return GetProperty(MyTextProperty); } set { SetProperty(MyTextProperty, value); } } The GetProperty method checks authorization to make sure the current user is allowed to see the value before returning the value. The SetProperty method does a lot more, including:  Checks authorization to make sure the current user is allowed to change the value  Ensures the new value is different from the existing value  Raises the PropetyChanging event  Records the new value  Runs any business and validation rules associated with this property  Runs any business and validation rules associated with related properties  Marks the object as having been changed  Raises the PropertyChanged event (using the correct behavior for Windows Forms or other UI environments as appropriate) Using CSLA 4: Creating Business Objects Page 14 Rev 1.1 In short, all the behaviors you’d otherwise have to implement by hand are encapsulated in the GetProperty and SetProperty helper methods. Helper Methods I’ve already mentioned the GetProperty and SetProperty helper methods. There are several additional helper methods you’ll need to use to implement all the different property implementation options. These are listed in Table 5. Method Description GetProperty Gets a property value, first checking authorization rules ReadProperty Gets a property value without checking any authorization rules SetProperty Sets a property value performing all processing as discussed in the previous section in this chapter (running business rules, etc.) LoadProperty Sets a property value without checking any rules, raising any events or marking the object as having been changed GetPropertyConvert Gets a property value, first checking authorization rules and converting the backing field value to the appropriate property value type ReadPropertyConvert Gets a property value without checking any authorization rules and converting the backing field value to the appropriate property value type SetPropertyConvert Sets a property value performing all processing as discussed in the previous section in this chapter (running business rules, etc.) and converting the new property value to the appropriate backing field type LoadPropertyConvert Sets a property value without checking any rules, raising any events or marking the object as having been changed and converting the new property value to the appropriate backing field type Table 5. Property getter and setter helper methods You’ll see how these helper methods are used as I discuss the different types of property declaration supported in the framework. RegisterProperty and PropertyInfo I briefly mentioned a static field containing metadata about a property; its purpose being to minimize or eliminate the use of reflection. It also eliminates the need to use any string literal values to represent the name of a property by providing a strongly typed token that represents the property name. This static field is declared for any CSLA style property: public static readonly PropertyInfo MyTextProperty = RegisterProperty(c => c.MyText); Using CSLA 4: Creating Business Objects Page 15 Rev 1.1 There are two key elements at work here. First, there’s the RegisterProperty method that is registering the property metadata with the CSLA .NET property management subsystem (often referred to as the field manager). Second, there’s the PropertyInfo type that is storing the property metadata. Registering a property’s metadata is a requirement for many features of CSLA 4, and you should expect to register your properties using this technique. RegisterProperty The RegisterProperty method is responsible for registering metadata about a property. This method has numerous overloads to accommodate different property declaration scenarios, and you’ll see them in use as I walk through each type of property declaration. Ultimately, RegisterProperty does two basic things. First, it gets or creates an IPropertyInfo (from the Csla.Core namespace) instance that contains the property metadata. Typically, this is a PropertyInfo instance as shown in this example. That value is returned as a result of the RegisterProperty method, so you can store the value in a static field for use throughout your code. Second, this IPropertyInfo instance is added to a static data structure maintained by CSLA .NET so it is possible for the framework (and therefore your code) to get a list of all the properties registered for a business class type without the need for reflection. Avoiding reflection is often a good idea for performance reasons, and that’s a nice side-benefit in this case. But the real value here is that this mechanism allows CSLA 4 to automatically serialize and deserialize objects on the Silverlight and WP7 platforms where reflection has restrictions that make it otherwise impractical to create a normal .NET serialization component. From a day to day practical perspective, the benefit to registering properties like this is that your business code has easy access to a static field that represents the property. This makes it easy to use metadata about the property, or to refer to the property without resorting to the use of the property name in string form. So you can use MyTextProperty instead of “MyText” to talk about the MyText property. This increases the maintainability of your code, because standard refactoring tools will automatically rename the strongly typed metadata field, but wouldn’t rename any string literal names referring to your properties. PropertyInfo and IPropertyInfo As I discussed, the RegisterProperty method has numerous overloads, some of which accept a pre-created IPropertyInfo (usually a PropertyInfo), or the RegisterProperty method will create an instance of PropertyInfo for you. The important type is IPropertyInfo from the Csla.Core namespace, because that’s the base type required by the field manager and other parts of CSLA .NET. That said, the generic PropertyInfo is more efficient because it is strongly typed, and it is used whenever possible for performance reasons. Using CSLA 4: Creating Business Objects Page 16 Rev 1.1 Either way, the IPropertyInfo or PropertyInfo field is what contains the metadata about each of your properties. Table 6 lists the elements defined by IPropertyInfo. Element Description Name Gets the name of the property (or method) Type Gets the type of the property FriendlyName Gets the human readable friendly display name of the property DefaultValue Gets the default value for a new instance of the property NewFieldData Gets a new field data container for the property; for advanced scenarios only RelationshipType Gets the relationship type for the property Index Gets the index position of the property in the internal field manager data store; for internal CSLA .NET use only Table 6. Elements defined by IPropertyInfo in Csla.Core Most of these elements are intended for use by CSLA .NET or your code. For example, you might use the FriendlyName property to get a human readable display name for the property, and then use that value for a label on a form. The Index element is specifically intended for internal use by CSLA .NET itself. In some cases CSLA .NET will manage your property’s field value on your behalf, and this Index value is used to provide fast access to the field value within internal CSLA .NET data structures. You should avoid using this value for anything within your application code. The DefaultValue property should only be used for value types or immutable reference types such as the string type. The DefaultValue property is set when the RegisterProperty method is called, and that occurs exactly one time per AppDomain. This means the default value is shared across all instances of the business class. You can not use the DefaultValue property to set a reference to a CSLA .NET business type, because the resulting default value would end up having numerous parent objects. A CSLA .NET business object can have exactly one parent object, and attempting to reuse an object as a child of numerous parent objects will result in runtime errors that can be hard to detect and resolve. Because this is all interface based, and PropertyInfo is not sealed, it is possible for you to extend the metadata stored for each property. This is an advanced scenario not covered in this book, but if you do wish to maintain extra metadata about each of your properties it is possible. The NewFieldData method supports that advanced scenario, allowing you to override the behavior that creates a PropertyInfo so you can create your own IPropertyInfo or your own subclass of PropertyInfo when that value is required by the framework. Again, you will typically use the standard metadata fields, including the FriendlyName and Name properties, which provide valuable metadata about each property. Using CSLA 4: Creating Business Objects Page 17 Rev 1.1 Manual and Managed Backing Fields Traditional property declarations have what is called a private backing field or a manual backing field. For example: private string _myText; public string MyText { get { return _myText; } set { _myText = value; } } In this case, the _myText field is the backing field for the property, and it is manually declared. When using the more compact C# (or VB) syntax for property declaration a field is still declared, but the compiler makes up a name for the field on your behalf. Even this property has a backing field: public int MyNumber { get; set; } Even though you don’t explicitly declare the field or know its name, it is there thanks to some compiler magic. CSLA 4 allows you to implement properties using a manual backing field, or with what is called a managed backing field. In the case of a managed backing field, the CSLA .NET base class manages the property’s value on your behalf. Examine this property implementation carefully: public static readonly PropertyInfo MyTextProperty = RegisterProperty(c => c.MyText); public string MyText { get { return GetProperty(MyTextProperty); } set { SetProperty(MyTextProperty, value); } } Notice that no backing field is explicitly defined, and in this case there’s no compiler magic involved either; there is no real backing field. Instead, the CSLA .NET base class (usually BusinessBase) is managing the value of the property on your behalf. The advantage of using manual backing fields is that you gain a slight performance benefit, because private fields are directly allocated in memory. If you are building Silverlight or WP7 applications, the drawback to using these fields is that you must write code to explicitly serialize those property values during all serialization and deserialization processing. CSLA 4 supports this scenario, but you need to be aware that this extra work is required. I’ll discuss the specifics later in this chapter. The advantage of using managed backing fields is that your code is simpler, and if you are building Silverlight or WP7 applications all managed field values are automatically serialized and deserialized as needed. It is important to note that there is a small performance impact to using managed backing fields; because the CSLA .NET base class must manage the values in a data structure somewhat like an optimized dictionary. Using CSLA 4: Creating Business Objects Page 18 Rev 1.1 My general recommendation is to use managed backing fields, and to only switch to manual backing fields for specific classes when required by performance or other specific issues. In the next section of this chapter I’ll walk through the types of property declaration and implementation using managed backing fields, and then I’ll show how they are implemented using manual backing fields. Properties and Stereotypes In Chapter 3, I will discuss various business domain object stereotypes, and the CSLA .NET base classes that support each stereotype. These stereotypes interact with property implementations, because different base classes provide different behaviors to support each stereotype. For example, BusinessBase supports the editable object stereotype, and so enables property implementations that utilize authorization, business rules, data binding and so forth. The CriteriaBase class, on the other hand, is designed to be a very lightweight container for data values, and so has no behaviors around authorization, business rules or data binding. In all cases, the same basic structure applies: register the property metadata, then implement getter and setter code using GetProperty, ReadProperty, SetProperty, and LoadProperty depending on which behaviors are required for your scenario. Types of Property Declaration CSLA 4 supports several types of property syntax for business classes created by subclassing BusinessBase or ReadOnlyBase. These are listed in Table 7. Declaration type Property gets or sets Read-write property value A primitive or standard .NET type (such as string) that can can be read or altered Read-write property value with conversion A value where the property type is different from the type of the field containing the property value; the property can be read and altered, and the property value is automatically converted to and from the field type Read-only property value A primitive or standard .NET type (such as string) that can be read, and has a non-public setter Read-only property value with conversion A primitive or standard .NET type (such as string) that can be read, and has a non-public setter, and the property value is automatically converted to and from the field type Child object reference A reference to a child object (containment relationship), typically with a public getter and non-public setter Using CSLA 4: Creating Business Objects Page 19 Rev 1.1 Child object reference with lazy loading A reference to a child object (containment relationship), typically with a public getter and non-public setter, where the child object is only created or retrieved on-demand Inter-graph reference A reference to another object in the same object graph, but not a child, typically with a public getter and setter Using reference A reference to another object outside the object graph, typically with a public getter and no setter Properties with manual backing fields Implementing properties using private manual backing fields, including read-write and read- only properties Table 7. Types of property declaration supported by CSLA 4 I’ll walk through each type of property, starting with the most common: a read-write property. I will discuss the read-write property in a lot of detail, with a little less focus on the other property types, mostly because once you understand how a read-write property works, the other property types follow the same pattern, with slight variations. Read-Write Perhaps the most common type of property is the read-write property. This is a property with a public getter and setter designed to allow code using the object to get and set the property value, subject to all authorization and business rules, and triggering all appropriate data binding and other behaviors. public static readonly PropertyInfo MyTextProperty = RegisterProperty(c => c.MyText); public string MyText { get { return GetProperty(MyTextProperty); } set { SetProperty(MyTextProperty, value); } } Registering the Property First, the property metadata is defined: public static readonly PropertyInfo MyTextProperty = RegisterProperty(c => c.MyText); Notice that the RegisterProperty method is generic, and so it gets the type of the value being stored for the property (string in this case). Also notice that a lambda expression is passed as a parameter to RegisterProperty to provide a strongly-typed reference to the property itself: c => c.MyText Behind the scenes, the RegisterProperty method takes this lambda expression and uses reflection to get access to the PropertyInfo object from System.Reflection corresponding to this property. It uses that PropertyInfo object to obtain other metadata about the property. Using CSLA 4: Creating Business Objects Page 20 Rev 1.1 Some alternate overloads of RegisterProperty allow you to pass the text name of the property as a string value, but I recommend you avoid those overloads because they reduce the maintainability of your code. The RegisterProperty method also looks at the property to see if it has a Display attribute from the System.ComponentModel.DataAnnotations namespace. This attribute can be used to provide a human readable friendly name for the property, and if that attribute is present then that friendly name is used as part of the property’s metadata. Getting the Value The property getter uses the GetProperty method, which accepts the static metadata field, MyTextProperty, as a parameter. This metadata field is what tells GetProperty which property value to retrieve. As a first step, any authorization rule associated with this property is checked, and the property value is only returned if the user is authorized to see the value. If the user is not authorized to read the property value, the default value of the property is returned. No exception is thrown if the user is not authorized to read the value. This is important, because there are numerous data binding scenarios where a UI developer can not prevent data binding from reading the value, and so if the getter threw an exception in that case it may crash the application, or at least cause issues with data binding. A good UI will have also used the authorization rule support in CSLA 4 to hide or disable the UI element that would display the value, so the user won’t see even this default value that is returned. The property itself will return only the default value, not the real value, and a good UI won’t even show that default value to the user because it will take the authorization rule into account as well. To support this scenario, every editable business object exposes a public method CanReadProperty that can be invoked by the UI code. In many cases CSLA 4 has UI helper components that assist the UI developer in building an authorization-aware user experience, and I’ll discuss those components in the ebooks dealing with each UI technology. Setting the Value The property setter uses the SetProperty method, which accepts the static metadata field, to identitfy the property value to be set, as well as the value parameter that contains the new value for the property. For simple properties, the SetProperty flow is relatively straightforward and usually involves the following steps: 1. Check authorization rules and throw an exception if user isn’t allowed to change the value 2. Raise the PropertyChanging event 3. Change the property value 4. Mark the business object as having been changed 5. Invoke any business rules associated with the property Using CSLA 4: Creating Business Objects Page 21 Rev 1.1 6. Invoke any business rules for properties associated with this property (dependent properties) 7. Raise the PropertyChanged event Add a few more elements, and the process can become much more complex, as illustrated in Figure 1. New value different? Ignore input No Unhook any old event handlers (child object only) Raise PropertyChanging event New value is child object? Mark field as changed? Raise PropertyChanged event Mark field as changed Yes Yes No Set parent referenceYes Set edit level Done No Hook changed event Change value Change value Figure 1. Process followed when setting a property value Using CSLA 4: Creating Business Objects Page 22 Rev 1.1 You should keep in mind that Figure 1 covers not only setting a primitive or simple property value, but also shows some elements that occur when setting a child object reference. I’ll discuss how child, inter-graph and using object references work later in this chapter, at which time I’ll refer back to this figure again. I want to call your attention to the fact that a SecurityException is thrown if an attempt is made to set the property when the current user isn’t authorized to change the property. This is different from the GetProperty behavior, where an exception isn’t thrown. The reason for throwing an exeption in the setter, is that a good UI will never allow the user to attempt to change the property, so there should never be a case where the setter is called if the user isn’t authorized. If the setter is called when the user isn’t authorized, throwing an exception makes sense, because it is an indication that the UI developer has introduced a bug into the application by allowing this to occur. You should also notice that the SetProperty method is what invokes business rules associated with this property, and with any other properties marked as being dependent on this property. I’ll discuss this behavior in more detail later in this ebook in the chapter covering business rules. Finally, notice that SetProperty also interacts with data binding by raising the PropertyChanged event. This is the minimum functionality required by all data binding mechanisms, and is one of several areas of data binding support provided by CSLA .NET. You should be aware that the PropertyChanged event is handled differently by Windows Forms, and if you are building a Windows Forms user interface you should set Csla.ApplicationContext.PropertyChangedMode to Windows, instead of Xaml (the default). While this isn’t strictly necessary, you may see a performance improvement because this setting will optimize how the event is raised to work better with Windows Forms. At this point, you should have an understanding of the property registration process and how property getters and setters work with read-write properties. The other types of property implementation are mostly variations on this same set of concepts. Read-Write with No Rules A scenario closely related to the standard read-write property is where you need a read-write property, but without any authorization or business rule processing. Sometimes you might want this type of property as a “private field” within a business object; merely as a place to store a value. While you can use private fields for .NET-only code, when building business types that need to serialize to Silverlight or WP7 it is often easier to create a private managed property as I’ll show here. Sometimes this is a requirement of a business stereotype or CSLA .NET base class. Several base classes, including CommandBase, CriteriaBase, and CslaIdentity don’t support any sort of authorization or business rules, and so this syntax is required when implementing properties when you subclass those base types. Using CSLA 4: Creating Business Objects Page 23 Rev 1.1 The only difference in code is that the ReadProperty and LoadProperty methods are used instead of the GetProperty and SetProperty methods: public static readonly PropertyInfo MyTextProperty = RegisterProperty(c => c.MyText); public string MyText { get { return ReadProperty(MyTextProperty); } set { LoadProperty(MyTextProperty, value); } } Remember from Table 5 that these two helper methods get and set property values without invoking any business rules. When implementing read-write properties in classes other than those inheriting from BusinessBase, this is the syntax you should use. If you want to create a “private field” you can make the property non-public: public static readonly PropertyInfo MyTextProperty = RegisterProperty(c => c.MyText); private string MyText { get { return ReadProperty(MyTextProperty); } set { LoadProperty(MyTextProperty, value); } } Notice that I’ve left the static metadata field public in scope, even though the property itself is private. You may choose to make the metadata field private in scope, but this will block certain data access models. In particular, you won’t be able to easily use the ObjectFactory data access models because the factory object will have no way to interact with the field value. I’ll discuss this in more detail in the CSLA 4: Data Access ebook. Read-Write with Value Conversion Sometimes you may have a property where the type of the property is not the same as the type of the underlying data field. As long as the two types (the type of the property and the type of the backing field) are convertible, CSLA .NET makes it very easy for you to create a property of one type, but maintain the value in a different type. Perhaps the most common example of this is where you have a string property that is backed by an enum value. While this technique is widely used, it is important to realize that it doesn’t lend itself to localization because there’s no way to change the enum to reflect the user’s culture or language. Suppose you have a simple enum like this: public enum TestEnum { Item1, Item2, Item3 } This is the value you want to have in your object, but you want to expose the value to the UI or other consuming code as its string representation, with automatic parsing of any input values into the enum value type and having your property getter convert the enum to a string. Using CSLA 4: Creating Business Objects Page 24 Rev 1.1 In this example, a string property should only allow the values "Item1", "Item2" or "Item3", because those are the text representations of the underlying numeric enum value. This can be done by using GetPropertyConvert and SetPropertyConvert in your property implementation: public static readonly PropertyInfo MyStringEnumProperty = RegisterProperty(c => c.MyStringEnum); public string MyStringEnum { get { return GetPropertyConvert(MyStringEnumProperty); } set { SetPropertyConvert(MyStringEnumProperty, value); } } Notice that the property type is string, but the RegisterProperty call uses the type TestEnum. What this means is that the CSLA .NET field manager infrastructure will store a value of type TestEnum in memory, but the public-facing property is of type string. The GetPropertyConvert method takes two generic type parameters. The first is the type of the backing field value (TestEnum) and the second is the type of the property (string). Code setting the property might look like this: obj.MyStringEnum = "Item2"; The code could attempt to set the value to text that can’t be parsed as an enum value: obj.MyStringEnum = "abc"; That will result in a PropertyLoadException from the Csla namespace being thrown by the SetPropertyConvert method. Any code reading the property value will get a string result, containing the text name of one of the valid enum values. As I mentioned earlier, this value conversion process will work as long as the property type and the backing field type are convertible following the .NET type conversion rules. There are many mechanisms by which .NET allows a value of one type to be converted into another type, and CSLA .NET will attempt to apply every one of those techniques to perform the conversion. If two types are not convertible, then GetPropertyConvert and SetPropertyConvert will throw type conversion exceptions at runtime. Accessing the Underlying Field Value The final thing that is important to understand when using properties that do type conversion is how to get at the underlying backing field value. Any use of the property itself will trigger the conversion, but the code within your business class will almost certainly need access to the underlying backing field value. To accomplish this, I recommend implementing another property that exposes the value directly: private TestEnum MyEnum { get { return GetProperty(MyStringEnumProperty); } set { SetProperty(MyStringEnumProperty, value); } } Using CSLA 4: Creating Business Objects Page 25 Rev 1.1 You can choose to make this property public or non-public as you choose. The important thing to notice is that you do not need to call RegisterProperty a second time. This property is already registered, you are exposing it directly without conversion of the value type. Read-Only Perhaps the second most common type of property is a read-only property. There are a couple variations on how you will implement a read-only property, depending on the CSLA .NET base class from which you inherit to create your business class. Read-Only with Authorization The BusinessBase and ReadOnlyBase classes support authorization rules for read-only properties and so your subclasses will implement read-only properties to use the GetProperty method: public static readonly PropertyInfo IdProperty = RegisterProperty(c => c.Id); public int Id { get { return GetProperty(IdProperty); } private set { LoadProperty(IdProperty, value); } } In this case, the property getter is the same as with a read-write property, and the GetProperty method will enforce any authorization rule associated with this property as I discussed earlier. The property setter is private in scope, and uses the LoadProperty method instead of SetProperty. The LoadProperty method is used based on the assumption that read-only properties won’t normally be changed during the lifetime of the object. If you do intend to change the value of a read-only property during the lifetime of the object, you will typically change the property setter to call SetProperty instead, so you get normal business rule and data binding behaviors as the property changes. Read-Only without Authorization There are several base classes in the CSLA .NET framework that support read-only properties, but which don’t support business or authorization rules. These include CommandBase, CriteriaBase, and CslaIdentity. In this case you’ll need to use the ReadProperty method instead of the GetProperty method when implementing the property: public static readonly PropertyInfo IdProperty = RegisterProperty(c => c.Id); public int Id { get { return ReadProperty(IdProperty); } private set { LoadProperty(IdProperty, value); } } The basic structure is the same, but in this case the ReadProperty method is used, so no authorization rules are checked when the property getter is invoked. Read-Only with Value Conversion Creating a read-only property with conversion involves the use of the ReadPropertyConvert and LoadPropertyConvert methods. By now you are probably familiar with the overall pattern and should be getting a solid understanding of the basic concepts behind property implementations. Using CSLA 4: Creating Business Objects Page 26 Rev 1.1 Here’s an example of a read-only property with value conversion, using an enum as the field type and a string as the property type: public static readonly PropertyInfo MyStringEnumProperty = RegisterProperty(c => c.MyStringEnum); public string MyStringEnum { get { return ReadPropertyConvert(MyStringEnumProperty); } private set { LoadPropertyConvert(MyStringEnumProperty, value); } } Similarly, you can have a read-only property with value conversion that applies authorization rules in the getter by using GetPropertyConvert: public static readonly PropertyInfo MyStringEnumProperty = RegisterProperty(c => c.MyStringEnum); public string MyStringEnum { get { return GetPropertyConvert(MyStringEnumProperty); } private set { LoadPropertyConvert(MyStringEnumProperty, value); } } Once the static metadata field has been declared, you have access to all the getter and setter helper methods and can mix and match them as necessary to implement your properties. Child Object Reference Implementing a property that references a child object is a little different from the simple properties you’ve seen so far. Specifically, I need to introduce a new overload of RegisterProperty where you specify a RelationshipTypes value. The RelationshipTypes enum includes a Child entry, which is used to specify that a property value represents a reference to a child object. In this section I’ll be using an Addresses property in a PersonEdit parent class: public static readonly PropertyInfo AddressesProperty = RegisterProperty(c => c.Addresses, RelationshipTypes.Child); public AddressEditList Addresses { get { return GetProperty(AddressesProperty); } private set { LoadProperty(AddressesProperty, value); } } The CSLA .NET framework uses this relationship type information to understand that this is a child reference, so it can automatically handle a number of scenarios around n-level undo and object persistence on your behalf. Notice that the getter uses the GetProperty method, so only authorized users can get access to the child object, and that the setter is private in scope and uses the LoadProperty method to set the field value with the child object reference. With a property implementation like the one shown here, the child object is typically created as part of the process of creating the parent object. I’ll discuss this in more detail in the Using CSLA 4: Data Access ebook, but in general your code that creates or retrieves the parent object will include a line of code to initialize the child property: Addresses = DataPortal.CreateChild(); Using CSLA 4: Creating Business Objects Page 27 Rev 1.1 The specifics of this code can be different depending on which data access model you are using, but the basic concept is the same: an instance of the child object is created or retrieved, and the property value is set to reference that child object instance. Child Object Reference with Lazy Loading Sometimes you’ll want to lazy load a child object. This is useful in situations where the user usually doesn’t view or use the child object, but might occasionally need access to the child behaviors or data. If the user usually views or uses the child object, it is more efficient to create or retrieve the child object at the same time you create or retrieve the parent object. Lazy loading is typically implemented by leaving the child property value as null until the property getter is invoked. Only if some code calls the property getter do you know that the child object is required, and so that’s the point at which you create or retrieve the child object. This process is different depending on whether you are using synchronous or asynchronous data access. While I will show you how to implement each type of property here, I’ll cover the sync vs async issues in more detail in the Using CSLA 4: Data Access ebook. You must specify another relationship type in RegisterProperty to tell CSLA .NET that you are using lazy loading for the property: RelationshipTypes.LazyLoad. This information is used by CSLA .NET to properly handle child properties that are lazy loaded. For example, the previous Addresses property could be changed like this: public static readonly PropertyInfo AddressesProperty = RegisterProperty(c => c.Addresses, RelationshipTypes.Child | RelationshipTypes.LazyLoad); Notice that both the Child and LazyLoad values are specified because this is a lazy loaded child property. Creating or Retrieving the Child Object The next thing to consider when lazy loading a child object is that CSLA .NET is designed to support n-tier deployments. It is quite possible that the parent object is running on a client workstation when the child property getter is invoked. In that case, it is often necessary to communicate with the application server to create or retrieve the child object, because client workstations typically can’t interact with the database server directly. This means you need to use the data portal to create or retrieve the child object. There are two ways to do this. One is to directy use the data portal to create the child object as a root object, and then to coerce the object to be a child, the other is to create a separate “child object creator” object that is responsible for creating the child object. I will show you this second option, as it is the most elegant and clear solution. Using the idea of an Addresses property where I want to lazy load an AddressEditList object, I’ll create an AddressListCreator class that has a Result property: [Serializable] public class AddressListCreator : ReadOnlyBase { public static readonly PropertyInfo ResultProperty = Using CSLA 4: Creating Business Objects Page 28 Rev 1.1 RegisterProperty(c => c.Result); public AddressEditList Result { get { return ReadProperty(ResultProperty); } private set { LoadProperty(ResultProperty, value); } } public static void GetAddressListCreator( EventHandler> callback) { DataPortal.BeginFetch(callback); } #if !SILVERLIGHT public static AddressListCreator GetAddressListCreator() { return DataPortal.Fetch(); } private void DataPortal_Fetch() { Result = DataPortal.CreateChild(); } #endif } Notice how the example data access code in the AddressListCreator class populates the Result property with the newly created child object. Again, the specifics of this code can be different depending on which data access model you are using, but the basic concept is the same: an instance of the child object is created or retrieved, and the property value is set to reference that child object instance. This AddressListCreator is then used to implement the lazy loading of the child object in the parent class’s property getter. The specific code to do this will be somewhat different for synch vs async scenarios, so I’ll cover each option. Synchronous Lazy Loading If you are going to synchronously load the child object, you’ll build your child property in the parent class like this: public static readonly PropertyInfo AddressesProperty = RegisterProperty(c => c.Addresses, RelationshipTypes.Child | RelationshipTypes.LazyLoad); public AddressEditList Addresses { get { if (!FieldManager.FieldExists(AddressesProperty)) { var creator = AddressListCreator.GetAddressListCreator(); Addresses = creator.Result; } return GetProperty(AddressesProperty); } private set { LoadProperty(AddressesProperty, value); OnPropertyChanged(AddressesProperty); } } Using CSLA 4: Creating Business Objects Page 29 Rev 1.1 The FieldManager.FieldExists method is used to determine whether the child property has already been loaded. If not, then the data portal is used to retrieve an instance of the AddressListCreator type. When an AddresssListCreator instance is created on the server, it creates or retrieves an instance of the AddressEditList child type, which is then available in the Result property. The parent object’s Addresses property value is then set to this Result value. Notice that the setter includes an explicit call to OnPropertyChanged, because LoadProperty doesn’t raise the PropertyChanged event, but when doing lazy loading it is a good idea to ensure this event is raised so any UI elements or other code bound to this property value are notified that there is now a child object available. Now that the property value has been loaded with a value, the normal GetProperty method is invoked to return the value to the calling code. Asynchronous Lazy Loading For most smart client scenarios you’ll want to use asynchronous lazy loading to avoid locking up the user interface while the child object is being created. In the case of Silverlight and WP7 you must use async lazy loading, because those platforms require async interaction with servers. Async lazy loading is somewhat similar in implementation: public static readonly PropertyInfo AddressesProperty = RegisterProperty(c => c.Addresses, RelationshipTypes.Child | RelationshipTypes.LazyLoad); public AddressEditList Addresses { get { if (!FieldManager.FieldExists(AddressesProperty)) { LoadProperty(AddressesProperty, null); AddressListCreator.GetAddressListCreator((o, e) => { if (e.Error != null) throw e.Error; else Addresses = e.Object.Result; }); } return GetProperty(AddressesProperty); } private set { LoadProperty(AddressesProperty, value); OnPropertyChanged(AddressesProperty); } } The primary difference from the synchronous implementation is that the BeginFetch method is called on the data portal instead of the Fetch method. This means that the AddressListCreator is retrieved asynchronously. Obviously this has a pretty significant impact on the overall workflow. The calling code that attempted to call this property will get a null result, because the property doesn’t yet exist, but the attempt to access the property starts the async process of loading the property with a value. Using CSLA 4: Creating Business Objects Page 30 Rev 1.1 Notice that the property value is set to null before the async load process is started. This ensures that the temporary null value is available to any code calling the property getter, and also that the async load process only runs one time. The BeginFetch method of the data portal requires a callback implementation: code that will be invoked when the asynchronous operation is complete. In this implementation I’m using a lambda expression for the callback, so this is the code that runs when the async task completes: { if (e.Error != null) throw e.Error; else Addresses = e.Object.Result; } It is important to always check the e.Error value to find out if any exceptions occurred during the async operation. One of the most common challenges people face when doing async programming is that an exception occurs on a background thread or on the server, and they forget to check e.Error and so are unaware of what went wrong. Exceptions that occur on a background thread or on the server during an async operation will silently disappear unless you explicitly check e.Error. It is important to always check e.Error in your callback code. If there was no exception, e.Object contains the result of the BeginFetch call, which means it contains the AddressListCreator object. That means the code can load the Addresses property value from its Result property. When the Addresses property is set, the OnPropertyChanged method is invoked in the setter, which means the PropertyChanged event is raised for the child property. This is the signal to any calling code (such as the UI) that there is now a child object available. In most cases, data binding will automatically respond to the PropertyChanged event and will rebind the UI controls to the newly available child object. I will revisit lazy loading of child properties in the Using CSLA 4: Data Access ebook, because your implementation of the “child creator” object (AddressListCreator in this example) is dependent on the data access model you choose. The important thing to understand is that the parent object’s property implementation is unaffected by the data access model. The implementation I’ve shown here is consistent, and any differences due to data access choices are abstracted within the child creator class implementation. Properties with Manual Backing Fields So far I’ve been showing you how to implement properties using managed backing fields. In these examples, CSLA .NET has been managing the underlying field value of each property. There are some cases where you’ll want or need to use private or manual backing fields. In those cases the code for implementing a property is a little different. Examples of where you might use manual backing fields include:  Inter-graph references Using CSLA 4: Creating Business Objects Page 31 Rev 1.1  Using relationship references  Scenarios where you need to apply attributes to fields  High-performance scenarios where using managed backing fields is a performance bottleneck The first three items in the list are inter-related, because implementing an inter-graph reference or using relationship requires that you apply attributes directly to the backing field for the property. The last item, performance, should be relatively rare. While there is some performance benefit to using manual backing fields over managed backing fields, it is typically not a meaningful difference. My recommendation is to implement using managed backing fields, and fall back to using manual backing fields only if you encounter an issue where their performance benefit is worth the extra complexity. Even when using a manual backing field, you will still register a static metadata field for the property. It is important to specify the PrivateField relationship type when registering such a property. Overloads of the helper methods exist that work with private fields. So the overall structure of a property remains consistent. For example: public static readonly PropertyInfo CityProperty = RegisterProperty(c => c.City, RelationshipTypes.PrivateField); private string _city = CityProperty.DefaultValue; public string City { get { return GetProperty(CityProperty, _city); } set { SetProperty(CityProperty, ref _city, value); } } Notice how a private backing field is manually declared, and initialized with the DefaultValue property from the static metadata field. Initializing the field like this helps maintain consistency with the behavior of managed backing fields. Next, look at the GetProperty and SetProperty method calls, and notice how the backing field is passed as a parameter to each method. Most notably, it is passed by ref to SetProperty so that helper method can efficiently change the field value, while still applying the same authorization, business rules and other processing that you get with managed backing fields. In fact, the behavior of this property is identical to its managed implementation, which would look like this: public static readonly PropertyInfo CityProperty = RegisterProperty(c => c.City); public string City { get { return GetProperty(CityProperty); } set { SetProperty(CityProperty, value); } } It is important to realize that only GetProperty and SetProperty have overloads that support manual backing fields. There’s no need for ReadProperty, LoadProperty or the type conversion overloads, because your code always has direct access to the field itself. If you need to get or set the field value without invoking business rules or other standard processing, you can interact Using CSLA 4: Creating Business Objects Page 32 Rev 1.1 directly with the field. Similarly, if you need to do type conversion between the field and the property types, you can manually do that in your getter and setter code. Loosely Typed ReadProperty and LoadProperty Methods The exception is that there are loosely typed ReadProperty and LoadProperty overloads that can be used to set any field value, managed or manual: var val = ReadProperty(CityProperty); LoadProperty(CityProperty, value); These overloads exist to enable certain data access layer scenarios and I’ll discuss them in more detail in the Using CSLA 4: Data Access ebook. You should know that they involve the use of some late bound technologies to get and set the value and so do have some performance cost. Despite this performance cost, they can provide a standard, loosely typed way to get and set values, and so are very valuable when building certain types of decoupled data access layers. Serialization of Simple Types for Silverlight and WP7 If you implement properties with manual backing fields, and plan to use your types in Silverlight or WP7, you will need to write some extra code to enable serialization and deserialization of those field values. In pure .NET code, CSLA .NET will use serializers that are able to directly access all fields of an object, even private fields. Silverlight doesn’t allow that sort of access for security reasons, and so those .NET serializers (BinaryFormatter and NetDataContractSerializer) don’t exist in Silverlight. To overcome that limitation, CSLA .NET includes its own serializer: MobileFormatter. This serializer is designed to automatically serialize properties that use managed backing fields. Properties implemented with manual backing fields are not automatically serialized, and you must help the MobileFormatter serialize and deserialize the field values. If you do not add the code to support serialization, your field values will not be copied between client and server, nor will n-level undo operate against those field values. For simple property types (not reference types), this is a matter of overriding two methods in your class: OnGetState and OnSetState. If your property is a reference type, you’ll need to override OnGetChildren and OnSetChildren, and you’ll also need to make certain that the object being referenced implements the IMobileObject interface from Csla.Core. Table 8 lists the types that are considered “simple types” by MobileFormatter. These types are directly supported as field values by the serialization process. Type Description Value types Types such as int, double, DateTime, etc. string The string data type Zero-based enum values A value of type enum can be serialized, as long as the enum is zero-based (has a value corresponding to 0) Using CSLA 4: Creating Business Objects Page 33 Rev 1.1 Table 8. Types directly serialized by MobileFormatter For the City property example, you need to implement the following code to support serialization: protected override void OnGetState( Csla.Serialization.Mobile.SerializationInfo info, Csla.Core.StateMode mode) { info.AddValue("_city", _city); base.OnGetState(info, mode); } protected override void OnSetState( Csla.Serialization.Mobile.SerializationInfo info, Csla.Core.StateMode mode) { _city = info.GetValue("_city"); base.OnSetState(info, mode); } Because string is a simple property type, the value can be added to the serialization stream in OnGetState, and retrieved from the serialization stream in OnSetState. The result is that when MobileFormatter serializes the object, the _city value is included in the serialized data, and when it deserializes the byte stream, the _city value is restored from the serialized data. Serialization of Reference Types for Silverlight and WP7 If your field is an object reference, then the object it references must implement IMobileObject or MobileFormatter won’t be able to serialize the reference. The easiest way to implement this interface is to inherit from one of the standard CSLA .NET base classes (such as BusinessBase), or to subclass one of the more primitive types in Table 9 from Csla.Core. Base type Description MobileBindingList A subclass of BindingList from System.ComponentModel that can be serialized by MobileFormatter; objects in the list must be simple types or must implement IMobileObject (in Silverlight this type is a subclass of ObservableCollection, because BindingList doesn’t exist in Silverlight) MobileDictionary A subclass of Dictionary from System.Collections.Generic that can be serialized by MobileFormatter; keys and objects in the dictionary must be simple types or must implement IMobileObject MobileList A subclass of List from System.Collections.Generic that can be serialized by MobileFormatter; objects in the list must be simple types or must implement IMobileObject MobileObject Implements IMobileObject, enabling relatively easy construction of types with fields and child objects that can be serialized by Using CSLA 4: Creating Business Objects Page 34 Rev 1.1 MobileFormatter MobileObservableCollection A subclass of ObservableCollection from System.Collections.ObjectModel that can be serialized by MobileFormatter; objects in the list must be simple types or must implement IMobileObject Table 9. Serialization base classes in Csla.Core Additionally, any such type must have the Serializable attribute on the class. To serialize and deserialize a reference type, the parent object must override the OnGetChildren and OnSetChildren methods. Consider a property that references an object that conforms to the requirements listed above (marked as Serializable and implements IMobileObject): public static readonly PropertyInfo PersonProperty = RegisterProperty(c => c.Person, RelationshipTypes.PrivateField); private PersonEdit _person = null; public PersonEdit Person { get { return GetProperty(PersonProperty, _person); } set { SetProperty(PersonProperty, ref _person, value); } } The following code is necessary to serialize and deserialize this reference on Silverlight and WP7: protected override void OnGetChildren( Csla.Serialization.Mobile.SerializationInfo info, Csla.Serialization.Mobile.MobileFormatter formatter) { base.OnGetChildren(info, formatter); if (_person != null) { var personInfo = formatter.SerializeObject(_person); info.AddChild("_person", personInfo.ReferenceId); } } protected override void OnSetChildren( Csla.Serialization.Mobile.SerializationInfo info, Csla.Serialization.Mobile.MobileFormatter formatter) { if (info.Children.ContainsKey("_person")) { var personInfo = info.Children["_person"]; _person = (PersonEdit)formatter.GetObject(personInfo.ReferenceId); } base.OnSetChildren(info, formatter); } The OnGetChildren method serializes the PersonEdit object by calling the SerializeObject method of the provided formatter object. The resulting serialized data is added to the serialization stream by calling the AddChild method. In the OnSetChildren method, the PersonEdit object is restored from the serialization stream by retrieving the serialized data, and then calling the GetObject method on the supplied formatter object. The result is cast to the correct type and used to set the _person field value. Using CSLA 4: Creating Business Objects Page 35 Rev 1.1 As you can see, using manual backing fields with Silverlight and WP7 requires some extra work, especially for object references. When possible you should use the standard CSLA .NET base types, or at least the serialization base types in Csla.Core to simplify the process of implementing serialization and the IMobileObject interface. If you absolutely must manually implement IMobileObject, I recommend using the classes in Csla.Core as examples for your implementation. Inter-Graph Reference Sometimes you’ll have one object in your object graph that references another object, but it isn’t a child relationship. For example, a PersonEdit object might contain a list of AddressEdit objects, and those would be child objects. But PersonEdit might also have a PrimaryAddress property that references one of the addresses in the list. This is an inter-graph reference, but isn’t a child reference. The distinction here is subtle but important, and it relates to the way n-level undo works within CSLA .NET. Calling BeginEdit on a parent object raises the parent’s edit level by one, and also raises all its child objects’ edit levels by one. This allows you to later call CancelEdit or ApplyEdit on the parent to undo or commit all changes made to the object graph in memory. If a child object is considered to be a child twice then when BeginEdit was called on the parent object, that child object would have its edit level raised twice instead of once. The result would be an edit level mismatch, because the child would be doubly edited and out of sync with the rest of the object graph. To avoid this issue, you need to mark the reference with the NotUndoable attribute. This attribute goes on the backing field for the property, and so you must implement this property using a manual backing field. For example, here’s a PrimaryAddress property from the PersonEdit class: public static readonly PropertyInfo PrimaryAddressProperty = RegisterProperty(c => c.PrimaryAddress, RelationshipTypes.PrivateField); [NotUndoable] private AddressEdit _primaryAddress = PrimaryAddressProperty.DefaultValue; public AddressEdit PrimaryAddress { get { return GetProperty(PrimaryAddressProperty, _primaryAddress); } set { SetProperty(PrimaryAddressProperty, ref _primaryAddress, value); } } Notice the use of the NotUndoable attribute on the backing field. This tells CSLA .NET that this object reference should not be included as part of any n-level undo operation. Remember that you must override the OnGetChildren and OnSetChildren methods for this property value to correctly serialize to and from the server if this code is used on Silverlight or WP7. Using Reference A using relationship is a reference to an object outside the object graph. In other words, you have a business object that is referencing another object, and that other object is not contained within, or included as part of, the same set of objects. Another way to think about this, is that this other object shouldn’t serialize to and from the server along with your business object. The best way to implement a using relationship is to avoid storing any reference to the other object at all. Instead, any time you need to interact with this other object you should create an Using CSLA 4: Creating Business Objects Page 36 Rev 1.1 instance of that object, or get a reference to that object and only keep the reference in a local variable. For example, suppose I have an InvoiceEdit object that for some reason needs to change a person’s name. An InvoiceEdit object doesn’t contain a PersonEdit object, but it might use a PersonEdit object to implement a SetPersonName method: public void SetPersonName(int personId, string name) { var person = PersonEdit.GetPerson(personId); person.FirstName = name; person = person.Save(); } While the InvoiceEdit object is retrieving and using a PersonEdit object, it doesn’t keep the reference, and so there are no issues with maintaining object references, n-level undo or serialization. This is the ideal situation. Maintaining a Reference There may be scenarios where you do need to maintain a reference to an object you are using over a period of time. In that case you’ll need to implement a property with a manual backing field so that field can be decorated with the NotUndoable and NonSerialized attributes. It is also the case that this type of property is typically implemented with a form of lazy or on- demand loading of the object to be used. This is important, because after deserialization the property value will always be null, so the property getter must handle that case and initialize the value. public static readonly PropertyInfo PersonProperty = RegisterProperty(c => c.Person); [NonSerialized] [NotUndoable] private PersonEdit _person = PersonProperty.DefaultValue; public PersonEdit Person { get { if (_person == null) _person = PersonEdit.NewPerson(); return GetProperty(PersonProperty, _person); } } Notice the use of the NonSerialized and NotUndoable attributes, which prevent this field from being serialized by .NET and from being part of n-level undo processing in CSLA .NET. Also notice that the getter includes lazy loading code to get an instance of the object on- demand. This way the target object is created when necessary, even after serialization and deserialization. In this example I’ve chosen not to implement a setter at all, because the value is only retrieved in the getter. This is a pretty typical implementation. If necessary you may certainly implement a setter. Finally, you should not override OnGetChildren or OnSetChildren for this field value. The whole idea is to not serialize the value, so you don’t want to add code to serialize it by overriding these methods. Using CSLA 4: Creating Business Objects Page 37 Rev 1.1 Non-Generic LoadProperty Method Several types of property implementation use the LoadProperty method to set a property’s value without executing business rules. The LoadProperty method shown in the examples in this chapter is a generic overload of the method that accepts an IPropertyInfo parameter to identity the affected property. There is also a non-generic LoadProperty method overload designed for use in certain scenarios. Specifically, this non-generic overload is designed for use when implementing web or service interfaces, or data access code, where the types of the input data and property aren’t known until runtime. The non-generic overload is substantially slower than the generic overload, because the value types aren’t known at compile time. You should use the non-generic overload only if there’s no way to invoke the generic overload. Normal property implementation code should always have access to the field containing the property’s metastate, and so should be able to use the generic overload. At this point, you should understand how to declare and implement properties using managed and manual backing fields, as well as properties that reference child and non-child objects. Method Declarations As with properties, CSLA 4 provides a formal syntax for declaration and implementation of methods in a business class. This includes a static field containing metadata about the method, and a helper method that invokes authentication so your method can easily determine whether the current user should be allowed to invoke the method. As you might guess, this means there’s a RegisterMethod method that corresponds to RegisterProperty, and an IMethodInfo interface in Csla.Core that corresponds to the IPropertyInfo interface. You use RegisterMethod to establish metadata about the method, and to create a static metadata field you can use to represent that method when adding your authorization rule. public static readonly MethodInfo TestMethod = RegisterMethod(typeof(EditableProperties), "Test"); public void Test() { CanExecuteMethod(TestMethod, true); // do some work here } The primary difference from a property is that it is up to the author of the method to check the authorization rule before allowing the method to execute. This means that the first line of code in your method is typically a call to CanExecuteMethod, which will throw a SecurityException if the current user isn’t authorized to execute the method. Metastate The CSLA .NET framework helps you create business domain objects that maintain their own status or metastate. These days this type of object is sometimes called a self-tracking object. Using CSLA 4: Creating Business Objects Page 38 Rev 1.1 Business domain objects created using CSLA .NET automatically track a number of properties and raise a number of events to provide rich behaviors you can leverage when building and using your business layer. Table 10 lists the metastate properties tracked by CSLA .NET objects. Property Description Stereotypes IsNew Is there a reasonable expectation that the object’s primary key value has a corresponding record in the database or other data store Editable root and child IsDeleted Is the object marked for deletion, so when it is saved it will either not do an insert, or will do a delete instead of an update Editable root and child IsChild Is this object a child of another object Editable root and child Editable list IsDirty Has the object, or any of its child objects, been changed Editable root and child Editable list IsSelfDirty Has the object been changed (ignoring the state of any child objects) Editable root and child IsValid Is the object, and all its child objects, valid: meaning there are no broken validation rules Editable root and child Editable list IsSelfValid Is the object valid (ignoring the state of any child objects): meaning there are no broken validation rules Editable root and child IsBusy Is the object or any of its properties or child objects executing any asynchronous operations Editable root and child Editable list IsSelfBusy Is the object or any of its properties (ignoring the state of any child objects) executing any asynchronous operations Editable root and child IsSavable True only if the object and its child objects are valid, has changes, isn’t running any async operations, and the current user is authorized to perform the operation Editable root and child Editable list Table 10. Metastate properties of CSLA .NET business objects Using CSLA 4: Creating Business Objects Page 39 Rev 1.1 Where possible CSLA 4 maintains this metastate on your behalf, so you can use use the various metastate properties. In some cases you may want or need to directly manipulate the metastate, and there are methods that allow your code to do so. Additionally there are some cases where you may want to redefine how the metastate works, and there are virtual properties or methods that enable those scenarios as well. Obviously you must fully understand how CSLA .NET uses the metastate before overriding any existing behaviors, or you can cause unexpected and undesirable results. Beyond these properties, there are numerous events raised at different points in an object’s lifecycle. The most important events are listed in Table 11. Event Description Stereotypes PropertyChanged A property has changed (if the property name is null or string.Empty then all properties are assumed to have changed) Editable root and child PropertyChanging A property is about to be changed Editable root and child ListChanged The list, or an item in the list, has changed (applies to BindingList subclasses only) Editable list CollectionChanged The collection, or an item in the collection, has changed (applies to ObservableCollection subclasses only) Editable list ChildChanged A child object somewhere deeper in the object graph has changed (may apply to child, grandchild, etc) Editable root and child Editable list Saved The object graph has been saved Editable root Editable root list BusyChanged The object’s busy status (IsBusy or IsSelfBusy) has changed Editable root and child Editable list Table 11. Events raised by CSLA .NET business objects Accessing Metastate The metastate values tracked by business objects are available directly on editable objects, and also through interfaces. Public Properties The metastate properties listed in Table 10 are all public properties of BusinessBase (and BusinessListBase where appropriate). This means you can access these properties directly on any editable business object instance. Using CSLA 4: Creating Business Objects Page 40 Rev 1.1 ITrackStatus Interface The ITrackStatus interface exposes the most commonly used metastate properties. The reason for exposing these properties through an interface is to enable polymorphism across different types of editable object, so you can write code that uses the metastate regardless of the specific object type. This interface is implemented by BusinessBase, BusinessListBase and BusinessBindingListBase, making the metastate easily available from any editable object. INotifyBusy Interface The INotifyBusy interface enables you to write polymorphic code to determine the busy status of any business object. Through this interface, you can access the object’s busy status and also handle its BusyChanged event to be notified when the status has changed. INotifyChildChanged Interface The INotifyChildChanged interface defines an event you can use to determine when a child object in an object graph has been changed. The ChildChanged event is unique, in that it cascades all the way up the object graph, starting with the immediate parent of the object that changed, but being raised by every parent object all the way to the root object of the object graph. You can use this interface to be notified of changes to an object graph regardless of the type of objects in the graph. Data Binding The metastate properties do not directly support data binding, and as they change they do not raise the PropertyChanged event. This is due to the way Windows Forms data binding works, and due to the way Visual Studio has implemented its drag-and-drop data binding support. In WPF, Silverlight and WP7 you will want your UI to bind to many of these metastate properties to automatically enable and disable various UI elements. In the Using CSLA 4: Silverlight 4 and WPF ebook I’ll discuss how the ViewModelBase, ViewModel and CslaDataProvider types in the Csla.Xaml assembly/namespace address this issue. Basic Status The basic status properties indicate whether the object is believed to be new or old, and whether it has been marked for deletion. IsNew Property A “new” object is one where there is a reasonable expectation that the object has no corresponding data in the database or data store. Typically this means that the object’s primary key value doesn’t correspond to a primary key in the database. Objects are typically new when:  They were created by the data portal by calling Create, BeginCreate or CreateChild  The object has been deleted Using CSLA 4: Creating Business Objects Page 41 Rev 1.1 In both of these cases, there is a reasonable expectation that the object does not correspond to data in the database. You can determine if an object is new by checking the IsNew property on any subclass of BusinessBase. IsDeleted Property Any subclass of BusinessBase will have an IsDeleted property. This property will be true if the object is marked for deletion. CSLA .NET supports two models for deleting editable root objects: immediate and deferred. I’ll discuss these in more detail in the Using CSLA 4: Data Access ebook, but you should know that it is rare for an editable root object to support deferred deletion. As a result, IsDeleted is almost always false for root objects. Conversely, editable child objects are always deleted through deferred deletion. This means that the object is marked for deletion, and is only deleted when the object graph is saved. After a child object has been marked for deletion, IsDeleted is true. Once an object’s IsDeleted property has been set to true, it can’t be directly changed to false. You must use the undo concept provided by CSLA .NET to revert the object graph to a previous state in order to “undelete” an object. The reason for this requirement is that when a child object is marked for deletion the parent object is usually also affected. For example, removing a child object from a BusinessListBase collection not only causes the child’s IsDeleted property to be true, but it causes the parent collection to move the child out of the active collection and into to a DeletedList collection object. To undelete the child, the object must be moved back into the active collection, and its IsDeleted property must be set to false. The undo behavior will automatically ensure that both the parent and child objects are reverted to an earlier state prior to the deletion of the child object. IsSavable Property The IsSavable property exists on all editable objects, and it indicates whether the object is in a state where it can be saved (inserted, updated or deleted in the data store). The IsSavable property is a combination of several other properties and exists as a convenience. An object is considered savable if:  The object and all child objects are valid  The object or any child object has been changed  The object and all child objects are not running any async operations  The user is authorized to perform the save operation The primary purpose of this property is to allow a UI developer to easily determine whether the object can be saved so appropriate buttons, links and menu items can be enabled and disabled as appropriate. Using CSLA 4: Creating Business Objects Page 42 Rev 1.1 MarkDeleted and Delete Methods The MarkDeleted method is a protected method of BusinessBase, and you can call this method to mark an object for deletion. This method sets the IsDeleted property to true. You should not normally need to directly call MarkDeleted, because it is invoked automatically in almost all cases. There is a Delete method as well, and it is public in scope. This method calls MarkDeleted, but it only operates on editable root objects. By default, calling Delete on an editable child object will cause an exception to be thrown. The Delete method exists to help support the use of deferred deletion of editable root objects. The reason Delete isn’t available on child objects, is that child objects should be deleted through their parent. Typically child objects are contained in collections (a subclass of BusinessListBase), and they are “deleted” by calling one of the remove methods on the collection. When an object is “removed” from a BusinessListBase or BusinessBindingListBase collection, it is marked for deletion and is moved to a DeletedList, so when the object graph is saved the underlying data can be deleted from the database. This is all automatic and you don’t need to do anything for this to occur. The Delete method is virtual, so if you need to customize the rules around marking editable root objects for deletion, you can override the existing behavior. Once an object is marked for deletion there is no “undelete” concept. If you need to “undelete” an object, you chould call CancelEdit on that object, or its parent object, thus causing the IsDeleted property value to be restored to its previous state. MarkNew Method The MarkNew method is a protected method of BusinessBase, and you can call this method to mark an object as being new. You should not normally need to directly call MarkNew, because it is invoked automatically in most cases. This method sets the IsNew property to true, the IsDeleted property to false and calls the MarkDirty method (which I’ll discuss later). The MarkNew method is virtual so you can override its behavior. The most common reason for overriding MarkNew is to change whether a new object is considered to have been changed or not. By default, when MarkNew is called it invokes MarkDirty. This means all new objects are also considered to have changed. Sometimes people prefer that new objects not be considered to have changed, and so they override MarkNew as follows: protected override void MarkNew() { base.MarkNew(); MarkClean(); } The result of this change is that new objects are considered to be new (IsNew is true), but they are considered to have no changes (IsSelfDirty is false). MarkOld Method The MarkOld method is a protected method of BusinessBase, and you can call this method to mark an object as being old (not new). An “old” object is an object where there’s a reasonable Using CSLA 4: Creating Business Objects Page 43 Rev 1.1 expectation that the object’s data corresponds to data in the database or data store. In most cases this means that the primary key value of the object directly corresponds to a row with that primary key in the database. The MarkOld method sets IsNew to false and calls MarkClean (which I’ll discuss later). Normally this method is called automatically by CSLA .NET when an object is inserted or updated into the database, at which point there is clearly a reasonable expectation that the data in the object corresponds directly to a set of data in the database. This method is virtual, so you can override its behavior. Change Tracking Editable business objects keep track of whether their state has been changed. An object is considered “dirty” if any of its properties have been changed. For performance and memory optimization reasons, CSLA .NET uses a fairly simplistic mechanism to track whether an object has been changed. If a property value is changed, the object is considered to have been changed. Even if the property is changed back to its orginal value, the object is still considered to have been changed. If you need finer grained information over whether the object has been changed, it is possible (with some work) to override the way CSLA .NET tracks that whether objects and properties have been changed. The technique involved is covered by Jason Bock in this blog post: http://www.jasonbock.net/JB/Default.aspx?blog=entry.9cc70d85bef34e2b9a683ba82615f8a3 Change tracking is managed by a couple properties and methods. IsDirty Property The IsDirty property is true if the object, or any of its child objects, have been changed. The data portal uses this property to avoid trying to save objects that haven’t been changed, and you can use this property to detect whether the object graph has changes as well. This property is virtual, so you can override its behavior to implement more complex change tracking mechanisms, though in most cases you should look at overriding the IsSelfDirty property instead. IsSelfDirty Property The IsSelfDirty property returns true if the object’s state has been changed. This includes changes to the object’s properties, as well as some metastate. For example, calling MarkDeleted will cause IsSelfDirty to return true, because that represents a change to the object’s state. When any property in the object is changed, a Boolean flag is set to true. By default, the IsSelfDirty property returns the value of that flag. This is a very low-impact implementation of change tracking that provides high performance and little memory consumption. This property is virtual, so you can override its behavior to implement more complex change tracking mechanisms. For example, if you replace the way field values are managed as described in Jason Bock’s blog post, you can override IsSelfDirty to determine whether the object has been changed by finding out whether any specific properties of the object have been changed. Using CSLA 4: Creating Business Objects Page 44 Rev 1.1 MarkClean Method The MarkClean method is a protected method on BusinessBase. Normally you don’t have to call this method directly, as it is invoked automatically by CSLA .NET. This method sets the internal Boolean change tracking flag to false, indicating that the object has no changes. The MarkClean method also tells the field manager to mark every property as being unchanged. By default this has no impact, but if you’ve replaced the default field management types with more complex types you can use this information to update your internal field storage values. Finally, the method calls OnUnknownPropertyChanged, which raises the PropertyChanged event with a property name of string.Empty. This tells data binding to refresh the bindings on all the properties of the object, ensuring that the UI is up to date based on any changes that were committed to the object’s state or metastate. PropertyHasChanged Method The PropertyHasChanged method is a protected method on BusinessBase that is invoked when individual property values of a business object are changed. You don’t normally need to invoke this method, as it is invoked automatically by CSLA .NET when property values change. This method calls the MarkDirty method (discussed later), and raises the PropertyChanged event as appropriate based on the value of Csla.ApplicationContext.PropertyChangedMode. The default mode is Xaml, which means the PropertyChanged event is raised in a way that is compatible with all UI technologies, including WPF, Silverlight, WP7 and Windows Forms. Please note that it is not optimized for Windows Forms and the default will cause a lot of extra UI refresh activity. So if you are building a Windows Forms application you should change the mode to Windows to optimize for that environment. Be aware that setting the PropertyChangedMode to Windows will cause WPF data binding to work incorrectly. If you do set the PropertyChangedMode, remember it is a global setting that affects the way the PropertyChanged event is raised by all editable objects in your application. MarkDirty Method The MarkDirty method is a protected method on BusinessBase. You don’t normally have to call this method directly, as it is invoked automatically by CSLA .NET (typically from the PropertyHasChanged method). This method sets the internal Boolean change tracking flag to true, indicating that the object has been changed. By default this method not only effectively sets IsSelfDirty to true, it also raises the PropertyChanged event with a property name of string.Empty. This tells data binding to refresh the bindings on all the properties of the object, ensuring that the UI is up to date based on any changes that were committed to the object’s state or metastate. It is also possible to call a MarkDirty overload that accepts a Boolean value indicating that the method should suppress the raising of the PropertyChanged event. In this case the method marks the object as having been changed. Using CSLA 4: Creating Business Objects Page 45 Rev 1.1 Object Validity Editable business objects keep track of whether they are valid. Being “valid” means that the object has no broken validation rules for the object or any of its properties. I’ll discuss validation and business rules in Chapter 4, but for now you should understand that when validation rules are executed, the result is a list of broken rules that is maintained for each business object. If there are no broken rules (with Error severity) in this list, then the object is valid. IsValid Property The IsValid property returns true if the current object and all its child objects are valid. If the current object or any of its child objects have any broken validation rules then IsValid will return false. This property is virtual so you can customize its behavior if required. This would be an advanced scenario where you’ve decided to create your own validation mechanism or something of that sort. IsSelfValid Property The IsSelfValid property returns true if the current object has no broken validation rules. The state of child objects is ignored by this property. You can use this property to determine the validity of a specific object rather than an object and its children. This property is also virtual so you can customize its behavior if required. This would be an advanced scenario where you’ve decided to create your own validation mechanism or something of that sort. Busy Tracking A “busy” object is an object that is executing an asynchronous operation. Examples of asynchronous operations include:  Asynchronous business rules  Asynchronous validation rules  Asynchronous data portal calls (such as calling BeginSave) Asynchronous business and validation rules are managed at the property level. This means a specific property is busy, as well as the business object. Another way to say this, is that a business object is busy if it is busy, or if any of its properties are busy. A busy object can’t be saved. This is because the results of the asynchronous operation won’t be known until that operation completes, so the object is in an indeterminate state while any async operation is executing. IsBusy Property The IsBusy property returns true if the object or any child object is executing an async operation. This property allows you to easily determine of an object graph has any async operations running, so you can do things like disable UI elements or show a busy animation as appropriate. Using CSLA 4: Creating Business Objects Page 46 Rev 1.1 This property is virtual so you can override its behavior. You shouldn’t normally need to override the IsBusy property, because even if you customize the way async operations work in your objects, you’d normally override the IsSelfBusy property. IsSelfBusy Property The IsSelfBusy property returns true if the object or any of its properties are executing async operations. The state of child objects is ignored by this property. Again, you can use this property to enable busy animations or disable parts of the UI as appropriate. This property is virtual so you can override its behavior. You may choose to do this if your object has alternate ways of becoming busy. If you implement new ways to execute async operations beyond the ones I’ve listed in this chapter, you would override the IsSelfBusy property so you can include your async operations as part of the object’s busy status. IsPropertyBusy Method The IsPropertyBusy method can be used to determine if a specific property is busy. A property is busy if one or more async business or validation rules attached to the property are currently executing. BusyChanged Event The IsBusy property can change for several reasons:  A child object becomes busy or idle  A property a child object becomes busy or idle  A property of the object becomes busy or idle  The object itself becomes busy or idle The BusyChanged event is raised when the busy status of the object or a property on the object changes, and includes a BusyChangedEventArgs parameter that contains information about the change. The BusyChangedEventArgs type includes a Busy property and a PropertyName property. When a property of the object becomes busy or idle, the BusyChanged event is raised to indicate the change for that specific property. In that case the PropertyName property contains the name of the affected property. When the object or a child of the object becomes busy or idle, the BusyChanged event is raised and the PropertyName property of the event args parameter is string.Empty. An empty PropertyName value indicates that the state of the object itself has changed, not only the state of a specific property. MarkBusy Method If you want to directly mark the object as busy you can call the MarkBusy method. This is a protected method available in BusinessBase and ReadOnlyBase. Normally MarkBusy is invoked automatically, and you don’t need to call this method. Using CSLA 4: Creating Business Objects Page 47 Rev 1.1 It is important to understand that calling MarkBusy has no effect on whether there are async operations running for specific properties, this method only impacts the object’s busy status. An object is only allowed to become busy if it is idle. Calling MarkBusy when the object is already busy will result in an exception. Calling MarkBusy marks the object as busy and raises the BusyChanged event. MarkIdle Method If you want to directly mark the object as idle you can call the MarkIdle method. This method marks the object as idle and raises the BusyChanged event. It is important to understand that calling MarkIdle has no effect on whether there are async operations running for specific properties, this method only impacts the object’s busy status. You should not normally need to call the MarkIdle method, because it is called automatically as appropriate. You should only call MarkIdle if it was your code that explicitly called MarkBusy to make the object appear busy. At this point, you should have an understanding of the basic concepts surrounding business domain objects created using CSLA .NET. These concepts include support for various object relationships, property and method declarations and metastate management. Next, I’ll discuss the basic solution structures typically used when creating applications using CSLA .NET. Then I’ll cover the different object stereotypes supported by the CSLA .NET base classes. Finally, I’ll cover business rules. Using CSLA 4: Creating Business Objects Page 48 Rev 1.1 Chapter 2: Solution Structure When using CSLA .NET to create a project, you can use many different project structures: ways of organizing your classes into projects and projects into solutions. In this chapter, I’ll discuss the way I structure solutions and projects to make the best of use CSLA 4. Project Types and Assembly References It is important to reference the correct assemblies and correct build of each assembly based on your project types. If you attempt to reference the incorrect assembly build, Visual Studio may stop you with an error. Alternatly, it may allow the reference, but you’ll encounter runtime errors when attempting to execute your code. Table 12 shows the most common project types and the CSLA .NET assemblies typically referenced by those projects. Project type Referenced assemblies Visual Studio Profile .NET Class Library Csla.dll .NET Client Profile Silverlight Class Library Csla.dll Silverlight 4 Windows Phone Class Library Csla.dll Windows Phone 7 WPF Application Csla.dll Csla.Xaml.dll .NET Client Profile Windows Forms Application Csla.dll Csla.Windows.dll .NET Client Profile Silverlight Application Silverlight Business Application Silverlight Navigation Application Csla.dll Csla.Xaml.dll Silverlight 4 Windows Phone Application Windows Phone Databound Application Windows Phone Panorama Application Windows Phone Pivot Application Csla.dll Csla.Xaml.dll Windows Phone 7 ASP.NET Web Application Csla.dll Csla.Web.dll .NET Full Profile ASP.NET MVC Web Application Csla.dll Csla.Web.dll Csla.Web.Mvc.dll .NET Full Profile Table 12. Common project types and related CSLA assembly references Taking some care to reference the correct assemblies and assembly builds when setting up each project can save you a lot of headaches when you start trying to build and run your solution code. Using CSLA 4: Creating Business Objects Page 49 Rev 1.1 Combining Projects to Create Solutions Most applications will be created by building a Visual Studio solution that is composed of several projects. In many cases these projects correspond directly to the logical architectural layers discussed in the Using CSLA 4: CSLA .NET Overview ebook. Table 13 shows a typical set of projects for a simple WPF Application. Project name Project type Description References MyApp WPF Application Contains the Interface and Inteface Control layers that comprise the user interface MyApp.Library.dll MyApp.Library .NET Class Library Contains the business classes that make up the business layer of the application MyApp.Dal.dll MyApp.Dal .NET Class Library Contains the data access code necessary to map data from your data store into and out of the business objects Various options as discussed in the Using CSLA 4: Data Access ebook Table 13. Typical projects in a WPF Application The references listed here in Table 13 are in addition to the CSLA .NET references listed earlier in Table 12. While it is possible to combine all the code into a single project, I recommend using this type of multi-project solution to help reinforce the concept of the logical layers within your application’s architecture. Even most highly skilled and disciplined developers have a very difficult time maintaining clear layer boundaries without using this technique. In an n-tier physical deployment, you’ll have at least one server between the client and database. For example, Table 14 shows a 4-tier Silverlight solution. Project name Project type Description References MyApp Silverlight Application Contains the Interface and Inteface Control layers that comprise the user interface MyApp.Library.dll (build for Silverlight) MyApp.Library.Net .NET Class Library Contains the business classes that make up the business layer of the application MyApp.Dal.dll MyApp.Library.Sl Silverlight Class Library Contains the business classes that make up the business layer of the application Using CSLA 4: Creating Business Objects Page 50 Rev 1.1 MyAppWeb ASP.NET Web Application The web site that hosts the Silverlight application, and exposes the Silverlight data portal so the Silverlight app can call back to the server MyApp.Library.dll (build for .NET) MyAppAppServer ASP.NET Web Application or AppFabric host Hosts the .NET data portal, and therefore the server- side application server behaviors MyApp.Library.dll (build for .NET) MyApp.Dal.dll MyApp.Dal .NET Class Library Contains the data access code necessary to map data from your data store into and out of the business objects Various options as discussed in the Using CSLA 4: Data Access ebook Table 14. Typical proejcts in a 4-tier Silverlight Application It is important to remember that this is probably the most complex scenario supported by CSLA 4, and even most Silverlight applications won’t use a 4-tier deployment. Even in this more complex solution you should be able to pick out the key parts of the logical layered architecture from the Using CSLA 4: CSLA .NET Overview:  Client application (interface and interface control)  Business layer (in this case build for both Silverlight and .NET)  Data access layer  Data portal hosts (necessary for routing messages between client and server) This is the consistent theme you should look for in any CSLA .NET solution. In general terms, there should always be a presentation project, a business layer project and a data access project. Other projects may exist to provide specialized functions such as hosting the data portal, or providing more flexible data access layers (as discussed in the Using CSLA 4: Data Access ebook). But the overall pattern should remain consistent. Reusing Business Assemblies Across Platforms One important feature of CSLA 4 is that it allows you to create business classes that can be used to build Windows, Web, Silverlight and Windows Phone applications; all built against the same business layer and business classes. To make this possible, you must create your own business layer assemblies (Class Library projects) that contain the same code but which are built for each target platform (.NET, Silverlight and WP7). In the example from Table 14 you can see a 4-tier Silverlight application that has a business layer running on the client, and on the server. The important thing to understand is that we want the client-side Silverlight and server-side .NET code to use the same business layer; literally to use the same classes! To make this possible, your solution will need a .NET Class Library to run on the server, and a Silverlight Class Library to run on the client. Both of these projects need to contain the exact same business classes, but need to be built for the .NET and Silverlight platforms respectively. Using CSLA 4: Creating Business Objects Page 51 Rev 1.1 The same concepts and techniques apply to sharing business types with WP7 class libraries. Visual Studio supports this concept through the idea of linked files. It is possible to create a Class Library project (the .NET Class Library for example) that contains all your business classes, and to then link all the code files from that project into another project (such the Silverlight Class Library). This doesn’t duplicate the files on disk, it shares the exact same code files between the two projects. Controlling the Assembly and Namespace Names CSLA .NET assumes your business type names will be the same regardless of platform. This includes not only the class name, but also the namespace and assembly names. This means you’ll need to make sure the .NET and Silverlight projects share the same assembly and namespace names. Figure 2 shows how the Assembly name and Default namespace values have been changed in a .NET project. Figure 2. Setting the assembly and namespace names in a .NET project Notice that the project name is Library.Net, but the assembly and namespace names have been changed to just Library. Assuming you’ll be linking class files into a corresponding Silverlight class library, you’d do the same thing (as shown in Figure 3) to the Silverlight project. Figure 3. Setting the assembly and namespace names in a Silverlight project Using CSLA 4: Creating Business Objects Page 52 Rev 1.1 Again, notice that the project name is Library.Sl, but the assembly and namespace values have been changed to be the same as in the .NET project. As you can guess, Figure 4 shows the same thing for a WP7 project. Figure 4. Setting the assembly and namespace names in a Windows Phone project By setting the assembly and default namespace values to be the same in all the projects, you are ensuring that when the solution is built that you’ll end up with compiled Library.dll files, built for .NET, Silverlight, and WP7 respectively. And you help ensure that all the classes in both assemblies share the same namespace. The whole idea here is that the exact same class code gets compiled for .NET, Silverlight, and WP7. The only difference is the platform for which the code is compiled, not the code itself. Linking Files Once the projects have been set up and their assembly and namespace names changed to be consistent you can start adding code files to one of the projects. I recommend putting all the code files into one of the projects and linking the files into the other projects to keep things as simple as possible. As an example, Figure 5 shows the .NET project containing code files. Figure 5. Class library containing code files These same physical files can be included as part of other projects using this concept of linking that is built into Visual Studio. To use this feature, right-click on the project where you want to add the file (such as the Library.Sl project) and choose to add an existing item as shown in Figure 6. Using CSLA 4: Creating Business Objects Page 53 Rev 1.1 Figure 6. Add an existing item In the resulting add file dialog, navigate to the folder where the files exist (such as Library.Net) and select the files to be linked. Then open the Add button menu and choose Add As Link as shown in Figure 7. Figure 7. Adding files as a link The result is that the files are linked into the project as shown in Figure 8. Using CSLA 4: Creating Business Objects Page 54 Rev 1.1 Figure 8. Project containing linked files Notice the extra arrow glyph in the lower-left part of each file icon. That indicates that this is a linked file. The files are not copied, they only exist on disk one time, in the original project. But they are included in this other project, and when this project is built these code files are compiled as though they were directly in this project’s folder on disk. The value of this is that you only have to maintain the code one time, a change to any business class immediately affects all projects into which that file is linked. So you are able to share code files across multiple platforms with very little effort or additional cost. Remember that in this example Library.Net is a .NET Class Library project referencing Csla.dll built for the .NET Client Profile, and that Library.Sl is a Silverlight Class Library project referencing Csla.dll built for Silverlight 4. A corresponding Library.Wp project would be a Windows Phone Class Library project referencing Csla.dll built for WP7. So even though all three projects might contain the exact same code files (directly or linked), they’ll each build the code for a different target platform: .NET, Silverlight, and WP7. Using CSLA 4: Creating Business Objects Page 55 Rev 1.1 Chapter 3: Object Stereotypes I will now walk through each object stereotype, explaining its purpose and illustrating the basic implementation of a business class following that stereotype. You can find example template code for each stereotype in the \Support\Templates folder in the CSLA .NET download. Editable Objects Editable objects can be created, retrieved, updated, and deleted from the database. These objects typically contain public read-write properties or child objects, and business rules to support business, validation, and authorization behaviors for these properties and objects. Editable Root and Child Editable root and child objects are objects that support most of the key features of CSLA .NET, including:  Contains data that is persisted in a database  Manages all metastate (new, deleted, valid, busy, dirty)  Public read-write properties  Business type is protected with authorization rules  Properties are protected with authorization rules  Changing a property triggers business rules (including validation)  Changing a property triggers data binding behaviors A parent object’s IsChild property is false, while a child will return true. This property affects how the object is persisted by the data portal, and is an important distinction I’ll discuss further in the Using CSLA 4: Data Access ebook. For the purposes of this ebook, you can consider that editable root and child objects share the same basic class structure. [Serializable] public class EditableRoot : BusinessBase { public static readonly PropertyInfo NameProperty = RegisterProperty(p => p.Name); public string Name { get { return GetProperty(NameProperty); } set { SetProperty(NameProperty, value); } } protected override void AddBusinessRules() { // TODO: add validation rules base.AddBusinessRules(); //BusinessRules.AddRule(new Rule(NameProperty)); } Using CSLA 4: Creating Business Objects Page 56 Rev 1.1 public static void AddObjectAuthorizationRules() { // TODO: add authorization rules //BusinessRules.AddRule(...); } #if !SILVERLIGHT public static EditableRoot NewEditableRoot() { return DataPortal.Create(); } public static EditableRoot GetEditableRoot(int id) { return DataPortal.Fetch(id); } public static void DeleteEditableRoot(int id) { DataPortal.Delete(id); } #endif public static void NewEditableRoot(EventHandler> callback) { DataPortal.BeginCreate(callback); } public static void GetEditableRoot(int id, EventHandler> callback) { DataPortal.BeginFetch(id, callback); } public static void DeleteEditableRoot(int id, EventHandler> callback) { DataPortal.BeginDelete(id, callback); } } Because this is the first stereotype I’m covering, I’ll walk through this code in some detail. First, notice that the class is marked with the Serializable attribute: [Serializable] All business types must be serializable. Next, look at the class declaration and notice that this is a subclass of BusinessBase, one of the primary base classes provided by CSLA .NET. The BusinessBase class supports the editable object stereotypes: public class EditableRoot : BusinessBase The T type parameter must always be the type of the business class you are creating. This is because the BusinessBase class needs to have access to this type value for implementation of strongly-typed property declarations, object cloning and object persistence. Your class will define its own properties. The one shown here is just an example: public static readonly PropertyInfo NameProperty = RegisterProperty(p => p.Name); public string Name { get { return GetProperty(NameProperty); } set { SetProperty(NameProperty, value); } } Using CSLA 4: Creating Business Objects Page 57 Rev 1.1 You can use any of the property implementation techniques I discussed earlier in this ebook to define public or non-public read-write or read-only properties as required by your specific business requirements. This includes properties that reference child objects. These properties can be decorated with attributes from System.ComponentModel.DataAnnotations, including the Display attribute to provide a friendly name for the property, as well as validation attributes. This is true even in WP7, because CSLA 4 includes an implementation of the DataAnnotations attributes for use in WP7 applications, even though there is no native implementation provided by WP7. Next notice the override of the AddBusinessRules method. I’ll discuss this method in more detail in Chapter 4. For now, you should understand that this is the method you need to override to attach custom business, validation and authorization rules to the properties of your business class. protected override void AddBusinessRules() { // TODO: add validation rules base.AddBusinessRules(); //BusinessRules.AddRule(new Rule(NameProperty)); } If you override this method, it is important to call the base implementation because that is where the DataAnnotations validation attributes are integrated into the CSLA 4 business rules subsystem. The next bit of code is an implementation of the AddObjectAuthorizationRules method: public static void AddObjectAuthorizationRules() { // TODO: add authorization rules //BusinessRules.AddRule(...); } This is a static method where you can write code to register authorization rules with your business class at the type level. I’ll discuss per-type authorization rules in Chapter 4. For now you should understand that these rules allow you to control which users are allowed to create, retrieve, update and delete instances of this business type. For editable child objects this is all the code that is relevant. The remaining code implements the public factory methods and is found in root objects only. Child types don’t have public factory methods. These factory methods call the data portal, which I’ll cover in detail in the Using CSLA 4: Data Access ebook. You can think of each data portal call as a potential call to an application server, where the business object will be persisted by your application’s data access layer. The first set of factory methods are wrapped in a compiler directive that prevents them from being compiled for Silverlight (or WP7). This is required because these are synchronous methods, and Silverlight doesn’t support synchronous calls to an application server. #if !SILVERLIGHT public static EditableRoot NewEditableRoot() Using CSLA 4: Creating Business Objects Page 58 Rev 1.1 { return DataPortal.Create(); } public static EditableRoot GetEditableRoot(int id) { return DataPortal.Fetch(id); } public static void DeleteEditableRoot(int id) { DataPortal.Delete(id); } #endif } These factory methods are pretty straightforward. They are static methods that perform create, retrieve or delete operations for this business type. They rely on the data portal to do all the work. The factory methods are an abstraction to make it easy for UI code to interact with your business type. For example, if someone wants to retrieve a specific EditableRoot object, code like this will do the job: var obj = EditableRoot.GetEditableRoot(123); This level of abstraction is very important, because the calling code doesn’t know how the object was created or loaded with data. The remaining factory methods are asynchronous, and so can be used on .NET, Silverlight or WP7. public static void NewEditableRoot(EventHandler> callback) { DataPortal.BeginCreate(callback); } public static void GetEditableRoot(int id, EventHandler> callback) { DataPortal.BeginFetch(id, callback); } public static void DeleteEditableRoot(int id, EventHandler> callback) { DataPortal.BeginDelete(id, callback); } These factory methods do the same thing: create, retrieve or delete the business class. But they are asynchronous, and so return no value directly. Instead, any values they return become available when the asynchronous operation completes, at which time the callback parameter is invoked. This callback parameter is the address of a method you supply, so it is your method that is invoked when the operation is complete. Typical calling code would look like this: EditableRoot.GetEditableRoot(123, (o, e) => { if (e.Error != null) throw e.Error; else _editableRoot = e.Object; }); Using CSLA 4: Creating Business Objects Page 59 Rev 1.1 Editable business objects are designed to support public read-write properties with attached business, validation and authorization rules. They are also designed to support persistence through the data portal, including create, retrieve, update and delete operations. Editable Root and Child List Another type of editable object is an editable collection. This stereotype represents a collection that supports some key concepts:  Contains data that is persisted in a database  Manages some metastate (valid, busy, dirty)  Business type is protected with authorization rules  Changing a child object triggers data binding behaviors An editable list is, by definition, a parent object because the whole idea of a collection is to contain other objects. The difference between an editable root and child list, is that an editable root list can be directly persisted through the data portal, while a child list is persisted as part of its parent. Changes made to child objects contained in an editable list are made in memory, and are only persisted to the database when the object graph is persisted through the data portal. The user can change many child objects, and they’ll be saved to the database as a batch, not one at a time. [Serializable] public class EditableRootList : BusinessListBase { public static void AddObjectAuthorizationRules() { // TODO: add authorization rules //AuthorizationRules.AllowGet(typeof(EditableRootList), "Role"); } #if !SILVERLIGHT public static EditableRootList NewEditableRootList() { return DataPortal.Create(); } public static EditableRootList GetEditableRootList(int id) { return DataPortal.Fetch(id); } #endif public static void NewEditableRootList(EventHandler> callback) { DataPortal.BeginCreate(callback); } public static void GetEditableRootList(int id, EventHandler> callback) { DataPortal.BeginFetch(id, callback); } } Using CSLA 4: Creating Business Objects Page 60 Rev 1.1 This type is serializable, and it inherits from a CSLA .NET base class: BusinessListBase. The BusinessListBase class supports the editable list stereotype by providing support for data binding, persistence and the parent-child relationships necessary for containment. Collections do not support direct declaration of properties, so you can’t implement properties in this class. If properties are required in addition to the list of child objects, you should create an editable parent object that contains the collection, along with those other properties and their rules. Because there are no properties in a collection, the only rules that make sense are the per-type rules you define by implementing the AddObjectAuthorizationRules method. I’ll discuss authorization rules in Chapter 4. This method allows you to specify the rules controlling which users can create, get, save, or delete the collection. The rest of the code in the class applies to root objects only. Child objects do not have public factory methods. The factory methods shown here include synchronous methods wrapped in a compiler directive so they don’t build for Silverlight or WP7. Again, Silverlight requires asynchronous communication with application servers, and you should assume any call to the data portal will be calling an application server. The last two methods are asynchronous factory methods and so work in .NET, Silverlight, and WP7. Adding New Items Adding new items to a collection can be done in many ways. It is important to remember that an editable list can only contain editable child objects. Calling Code Adds Item One way to add items to a collection is to create the new item (new editable child object) and to then use the collection’s Add method to add the item to the collection. This is not terribly abstract, because it requires that the calling code understand how to create a new editable child object. That is typically done using a Unit of Work object (a stereotype I’ll discuss later in this chapter). For the following code examples, assume you have an EditableChildCreator object that understands how to properly create and initialize a new child object. To support this, it would have a static method named CreateChild that invokes the data portal to create the new child object. In a synchronous environment the calling code would look like this: var child = EditableChildCreator.CreateChild(); _list.Add(child); In an async environment the calling code would look like this: EditableChildCreator.CreateChild((o, e) => { if (e.Error != null) throw e.Error; else _list.Add(e.Object); }); Using CSLA 4: Creating Business Objects Page 61 Rev 1.1 The lack of abstraction in this approach is far from ideal, because it pushes too much knowledge into the calling code. It would be better to hide these details. Collection Adds Item Another way to add items to a collection is to implement an AddChild method in the editable list class itself. Then the calling code would look like this: _list.AddChild(); The AddChild method itself would still use a Unit of Work object to create the child object, but it can encapsulate that effort, and can also hide the details of synchronous vs asynchronous implementations. A synchronous AddChild method would look like this: public void AddChild() { Add(EditableChildCreator.CreateChild()); } While an asynchronous AddChild method would look like this: public void AddChild() { EditableChildCreator.CreateChild((o, e) => { if (e.Error != null) throw e.Error; else Add(e.Object); }); } The calling code can rely on the collection’s CollectionChanged event to know when the async operation is complete and the new object has been added to the collection. The advantage of the AddChild method technique is that you encapsulate the details of creating and adding the child object within the collection class itself. Also, your AddChild method can accept parameters that might be required to initialize the new child object. Implementing AddNew The final technique you can use is the standard AddNew method on editable list objects. This is very similar to the idea of implementing your own AddChild method, except that you can’t pass parameter values to the AddNew method. The benefit of using the AddNew method is that this technique integrates with data binding and many datagrid controls (in smart client UI technologies) so when the user moves to the bottom row of a datagrid control a new object is automatically created and added to the collection (and thus the datagrid). Although the implementiation of the AddNew is different between .NET and Silverlight, you do need to set the AllowNew property to true in your collection’s constructor in both platforms: AllowNew = true; Setting this property to true tells the runtime that you have implemented the behaviors necessary for AddNew to work. Using CSLA 4: Creating Business Objects Page 62 Rev 1.1 The AllowNew property is true by default, and CSLA 4 includes a default synchronous implementation of the AddNew behavior for both .NET and Silverlight. The AddNew behavior will work by default. I will walk through how you implement AddNew. Keep in mind that if you need to customize its behavior or initialize new child objects with specific data, you’ll need to provide your own implementation. In .NET code you implement AddNew by overriding the AddNewCore method from the collection base class. In your collection class you write code like this: protected override EditableChild AddNewCore() { var child = EditableChildCreator.CreateChild(); Add(child); return child; } Again, I’m using a Unit of Work object to create and initialize a new child object. That child object is then added to the collection, and is returned as a result of the method. The code is a little different in Silverlight and WP7, where the implementation is asynchronous. protected override void AddNewCore() { EditableChildCreator.CreateChild((o, e) => { if (e.Error != null) { throw e.Error; } else { Add(e.Object); OnAddedNew(e.Object); } }); } Because the operation is async, AddNewCore is a void method. Once the new child is created, this method must call the OnAddedNew method to indicate that the new child has been added to the collection. Creating the Child Directly If you don’t need to initialize your child object with any data as it is created, you can avoid the use of a Unit of Work object and any communication with any application server. The code I’ve been using so far relies on a Unit of Work object to abstract the use of the data portal, so any child object is created by communicating with any application server so the new object can be initialized with data as necessary. If you can create child objects with hard-coded default values, you don’t need to go through a Unit of Work object, and instead you can directly call the CreateChild method on the data portal. While I’ll get into more detail in the Using CSLA 4: Data Access ebook, I’ll quickly show you the relevant code here. For example, the .NET implementation of AddNewCore could look like this: Using CSLA 4: Creating Business Objects Page 63 Rev 1.1 protected override EditableChild AddNewCore() { var child = DataPortal.CreateChild(); Add(child); return child; } This will cause the data portal to directly create an instance of the child object without any attempt at talking to an application server. Similarly, you can do the same thing on Silverlight: protected override void AddNewCore() { var child = DataPortal.CreateChild(); Add(child); OnAddedNew(child); } The data portal’s CreateChild method is synchronous, so this converts the Silverlight implementation into what is effectively a synchronous operation. Notice, that OnAddedNew is still invoked, because that closes the loop to tell the collection and runtime that the add operation is complete. The default .NET and Silverlight implementations of AddNew use the data portal’s CreateChild method that I’ve shown here. The editable list stereotype enables batch updates of changes to a list of child objects, along with authorization rules at the type level. Dynamic Root List and Dynamic Root CSLA .NET supports two stereotypes for editable collections, the editable list stereotype I discussed earlier, and a dynamic root list stereotype. The dynamic root list stereotype is quite different, because in this case, the collection contains root objects that are persisted individually instead of in a batch. This also requires a slight variation on the editable root stereotype for the objects contained in a dynamic root list. The basic concept is that a smart client UI will retrieve a dynamic root list, which is a collection of dynamic root objects (basically editable root objects). This list will then be data bound to a datagrid control for in-place editing. This stereotype is designed specifically to support smart client UI scenarios with a datagrid control. To use a dynamic root list without a fully functional datagrid control will require that you simulate all the details of data binding that a datagrid would normally implement. As the user edits and leaves each row in the datagrid control, they are effectively concluding the edit of a root object in the list, so the dynamic root list collection automatically saves the changes to that object. This occurs automatically and immediately as the user leaves the row in the datagrid control. Using CSLA 4: Creating Business Objects Page 64 Rev 1.1 Similarly, if the user adds a new item to the datagrid control, edits the values and leaves the row, the corresponding root object in the list is saved (inserted) into the database. If the user presses the ESC key, the new item is removed from the datagrid and collection without being saved. If the user deletes a row in the datagrid control, the corresponding root object in the collection is automatically and immediately deleted. These save and delete operations are managed automatically by the dynamic root list collection in coordination with the data binding behaviors expected from datagrid controls. You should expect this to work with most datagrid controls in Silverlight, WPF, and Windows Forms. All persistence operations are handled by the data portal, and I’ll discuss them in more detail in the Using CSLA 4: Data Access ebook. Dynamic Root A normal editable root object has public factory methods allowing the UI to directly retrieve instances of the business type. A dynamic root is an editable root designed to be contained within a dynamic root list, and so responsibility for retrieving each object belongs to the collection. In most cases this means that a dynamic root object is a normal editable root object without a public factory method for retrieving (getting) an instance of the type. This also impacts the implementation of the data access code for fetching the object, because that code will now be invoked by the “parent” collection instead of directly by the data portal. I’ll get into more details in the Using CSLA 4: Data Access ebook. Otherwise, a dynamic root is like an editable root. It will have the same properties, business rules, factory method for creating the object and so forth. Dynamic Root List A dynamic root list is created by subclassing the DynamicListBase base class. This base class contains the functionality necessary to respond to the data binding interaction with a datagrid control to automatically create, save and delete the dynamic root objects contained in the collection. [Serializable] public class DynamicRootList : DynamicListBase { public static void AddObjectAuthorizationRules() { // TODO: add authorization rules // AuthorizationRules.AllowGet(typeof(DynamicRootList), "Role"); // AuthorizationRules.AllowEdit(typeof(DynamicRootList), "Role"); } public DynamicRootList() { AllowNew = true; } #if SILVERLIGHT protected override void AddNewCore() { DynamicRoot.NewDynamicRoot((o, e) => { if (e.Error != null) { Using CSLA 4: Creating Business Objects Page 65 Rev 1.1 throw e.Error; } else { Add(e.Object); OnAddedNew(e.Object); } }); } #else protected override DynamicRoot AddNewCore() { DynamicRoot item = DynamicRoot.NewDynamicRoot(); Add(item); return item; } #endif #if !SILVERLIGHT public static DynamicRootList NewDynamicRootList() { return DataPortal.Create(); } public static DynamicRootList GetDynamicRootList(int id) { return DataPortal.Fetch(id); } #endif public static void NewDynamicRootList(EventHandler> callback) { DataPortal.BeginCreate(callback); } public static void GetDynamicRootList( int id, EventHandler> callback) { DataPortal.BeginFetch(id, callback); } } As with the other editable types I’ve discussed, this one implements the AddObjectAuthorizationRules method where you can associate authorization rules to specify the users that are allowed to create, get, and save this collection. This class has a constructor that sets AllowNew to true, because the code overrides AddNewCore to implement the AddNew behavior. While this is optional, you will usually implement the AddNew behavior so the user can automatically add new items to the datagrid by moving to the last row in the display. The concept of overriding AddNewCore is the same as I discussed earlier for the editable list stereotype. The implementation is different for .NET and Silverlight because the Silverlight implementation must be asynchronous. And as with the other root object stereotypes, the dynamic root list implements public factory methods to allow creation or retrieval of the collection. The synchronous factory methods are wrapped in a compiler directive so they only build for .NET, while the asynchronous factory methods are available to .NET, Silverlight, and WP7 code. Using CSLA 4: Creating Business Objects Page 66 Rev 1.1 The dynamic root list stereotype is very useful for the specific scenario of supporting in-place editing in a smart client datagrid control, where changes should be automatically committed as the user leaves each row in the datagrid. Read-Only Objects Read-only object stereotypes support the idea that there are many objects that contain read-only data. These include objects with read-only properties, and collections that don’t allow adding or removing of items. Read-only objects are generally “lighter weight” than editable objects, because they don’t need to support concepts like validation rules or change notification for data binding. Read-only objects don’t track the IsChild metastate value, or most other metastate values. Because a read-only object only supports read-only properties or child objects, there’s no need to track whether the object is new, marked for deletion, is valid, or has been changed. None of these concepts make sense for a read-only object. Read-Only Root and Child The read-only root and child stereotypes are essentially identical. The only difference is that a read- only root will have public factory methods and the child will not. [Serializable] public class ReadOnlyRoot : ReadOnlyBase { public static readonly PropertyInfo NameProperty = RegisterProperty(p => p.Name); public string Name { get { return GetProperty(NameProperty); } private set { LoadProperty(NameProperty, value); } } protected override void AddBusinessRules() { // TODO: add authorization rules //BusinessRules.AddRule(...); } public static void AddObjectAuthorizationRules() { // TODO: add authorization rules // BusinessRules.AddRule(...); } #if !SILVERLIGHT public static ReadOnlyRoot GetReadOnlyRoot(int id) { return DataPortal.Fetch(id); } #endif public static void GetReadOnlyRoot(int id, EventHandler> callback) { DataPortal.BeginCreate(id, callback); } } The base class that supports the read-only object stereotype is ReadOnlyBase. Notice that this type is serializable and that it inherits from the appropriate base class. Using CSLA 4: Creating Business Objects Page 67 Rev 1.1 You will implement your own read-only properites based on your business requirements. The property shown here is one possible example. public static readonly PropertyInfo NameProperty = RegisterProperty(p => p.Name); public string Name { get { return GetProperty(NameProperty); } private set { LoadProperty(NameProperty, value); } } You can use any of the read-only property implementations I discussed earlier in the chapter. It is important to understand that the SetProperty and SetPropertyConvert helper methods do not exist in ReadOnlyBase, because those only make sense for read-write properties. The GetProperty method exists, because it applies authorization rules as the user attempts to read the property. The AddBusinessRules and AddObjectAuthorizationRules methods are similar to those you’d find in an editable object, except that the only rules enforced in a read-only object are authorization rules dealing with reading properties and getting an instance of the business type. There is no concept of business rules, validation rules, or authorization rules covering writing to a property or saving the object. It makes no sense to create a “new” read-only object, because that would create an object that has no data in its read-only properties. It does make sense to retrieve a read-only object, so that you can see the factory methods (synchronous and asynchronous) which use the data portal to fetch an instance of the read-only type: #if !SILVERLIGHT public static ReadOnlyRoot GetReadOnlyRoot(int id) { return DataPortal.Fetch(id); } #endif public static void GetReadOnlyRoot(int id, EventHandler> callback) { DataPortal.BeginCreate(id, callback); } Only a read-only root will have these factory methods. A read-only child will be retrieved as part of its parent. The read-only object stereotype is very valuable, because most applications have a lot of information that needs to be retrieved for display. This stereotype supports that requirement, with authorization rules and data portal integration. Read-Only Root and Child List One of the most common scenarios in an application is the “search screen” where the user is shown a read-only list of search results, they pick a result and are shown a detail or edit screen. That scenario is supported by the read-only list stereotype. A read-only list is a collection that isn’t changed after it is created (no items are added or removed) and it contains read-only objects. The only difference between a read-only root and child list is that a read-only root list has public factory methods and a child does not. Here’s an example of a read-only list: Using CSLA 4: Creating Business Objects Page 68 Rev 1.1 [Serializable] public class ReadOnlyList : ReadOnlyListBase { public static void AddObjectAuthorizationRules() { // TODO: add authorization rules // AuthorizationRules.AllowGet(typeof(ReadOnlyList), "Role"); } #if !SILVERLIGHT public static ReadOnlyList GetReadOnlyList(string filter) { return DataPortal.Fetch(filter); } #endif public static void GetReadOnlyList(string filter, EventHandler> callback) { DataPortal.BeginFetch(filter, callback); } } The base class that supports the read-only list stereotype is ReadOnlyListBase. The only type of business rules available in a read-only list are per-type authorization rules that are set up by implementing the AddObjectAuthorizationRules method. As you might expect, the only meaningful authorization rule deals with the retrieval of the list object. The remaining code applies to root objects only, and illustrates the basic factory method structure you’ve seen in previous stereotypes. Again you see the synchronous .NET-only factory and the asynchronous factory that applies to all platforms. Name-Value List In many older applications one of the most common requirements for a read-only list was to retrieve a simple name-value pair for use in populating a listbox or combobox control in the UI. This requirement was so common that I defined the NameValueListBase base class to support a name-value list stereotype. The name-value list stereotype is an example of a read-only list, but with a pre-built read-only child object type that only contains a key and a value. This stereotype is sometimes still useful, but I find that most XAML-based applications require richer information than a name-value pair for populating their listbox and combobox controls. The fact that modern UI technologies allow the display of more than a single string value means that the name-value list stereotype has become less useful over time. You should feel free to use the NameValueListBase base class if it fits into your application’s requirements. If you need something more than name-value pairs then you should create a read- only list with richer read-only child objects. The most interesting part of the following code is probably the implementation of a simple client-side cache using a static field. This is entirely optional, but is pretty common for name-value lists because they often contain data that doesn’t change while the application is running: [Serializable] public class NameValueList : NameValueListBase { private static NameValueList _list; Using CSLA 4: Creating Business Objects Page 69 Rev 1.1 public static void InvalidateCache() { _list = null; } #if !SILVERLIGHT public static NameValueList GetNameValueList() { if (_list == null) _list = DataPortal.Fetch(); return _list; } #endif public static void GetNameValueList(int id, EventHandler> callback) { if (_list == null) { DataPortal.BeginFetch(id, (o, e) => { _list = e.Object; callback(o, e); }); } else { callback(null, new DataPortalResult(_list, null, null)); } } } The class declaration uses the NameValueListBase base class, providing it with the types of the key and value for the child objects in the list: public class NameValueList : NameValueListBase In this example the key is of type int and the value is of type string. The next bit of code implements a simple static field as a cache for the list: private static NameValueList _list; public static void InvalidateCache() { _list = null; } There’s also a method to clear the cache, which can be useful if the application does need to force the cache to be reloaded with data from the server. The factory methods are very similar to those you’ve seen, except that they check the static cache field and return that value if it exists. The data portal is only invoked if there is no pre-existing cached value. You can use this same static field cache technique with read-only object and read-only list objects as well. The read-only stereotypes are widely used in most applications, because it is extremely common to display read-only data to a user. In contrast, if your object requires a read-write property, or your collection must support adding or removing of items, then you should use the editable object stereotypes. Using CSLA 4: Creating Business Objects Page 70 Rev 1.1 Execution Objects Editable and read-only stereotypes have one thing in common: they typically have properties that are displayed to the user. But not all objects are so focused on data and properties. Nearly every application needs objects that just do something. These are objects that represent the execution of an action or a command or a verb or a task. For example, your application may allow users to create and edit sales orders. But at some point the user probably chooses a menu option that ships an order, or ships all orders for a certain date. It is possible that the process of shipping orders is non-interactive. It is a server-side task that runs through the database updating data and printing shipping documents. As another example, your Silverlight UI for editing a sales order might need to retrieve the SalesOrderEdit object, along with several read-only list objects containing values necessary to populate listbox and combobox controls in the UI. Because all server access in Silverlight is asynchronous, you need some way to make a single async call to the server to get all those objects at the same time so your UI will have all the data needed for data binding to work properly. Theses are examples of execution objects following the two primary stereotypes:  Command  Unit of Work I’ll discuss each stereotype. Command The command object stereotype supports a specific sequence of code execution as listed in Table 15. Step Description Create object The command object is created by the calling code, typically on the client workstation. Initialize object The command object is initialized by the calling code, either through its constructor or by explicitly setting properties on the object. Client execution The command object can run any code that needs to run on the client before interacting with the server. Server execution The command object is executed by the data portal, which means any server-side code is executed after the data portal has moved the object to the application server. Client execution The command object comes back from the server through the data portal, and it can run any code that needs to run on the client after the server interaction. Calling code reacts The calling code can interact with the command object once its execution is complete, often reading property values as results Using CSLA 4: Creating Business Objects Page 71 Rev 1.1 from the object. Table 15. Execution sequence of a command object The base class that supports the command stereotype is CommandBase. Here is an example of a command class: [Serializable] public class CommandObject : CommandBase { #if !SILVERLIGHT public static bool Execute() { CommandObject cmd = new CommandObject(); cmd.BeforeServer(); cmd = DataPortal.Execute(cmd); cmd.AfterServer(); return cmd.Result; } #endif public static void BeginExecute(EventHandler> callback) { CommandObject cmd = new CommandObject(); cmd.BeforeServer(); DataPortal.BeginExecute(cmd, (o, e) => { if (e.Error != null) throw e.Error; e.Object.AfterServer(); callback(o, e); }); } public static PropertyInfo ResultProperty = RegisterProperty(c => c.Result); private bool Result { get { return ReadProperty(ResultProperty); } private set { LoadProperty(ResultProperty, value); } } private void BeforeServer() { // TODO: implement code to run on client // before server is called } private void AfterServer() { // TODO: implement code to run on client // after server is called } protected override void DataPortal_Execute() { // TODO: implement code to run on server // and set result value(s) Result = true; } } The code starts out with static factory methods that make it easy to invoke the command synchronously and asynchronously. These factory methods follow the sequence of steps from Table 15, calling methods that are stubbed out for illustration purposes. Using CSLA 4: Creating Business Objects Page 72 Rev 1.1 The DataPortal_Execute method contains the code that runs on the server. In the Using CSLA 4: Data Access ebook I’ll describe how this is one of several possible implementations of the server- side code. Notice how the asynchronous factory method invokes the AfterServer method in the completion callback from the data portal’s BeginExecute method. This ensures that the server-side processing is complete and that the command object has moved back to the client before the client-side post-processing code is executed. Also notice the implementation of the Result property: public static PropertyInfo ResultProperty = RegisterProperty(c => c.Result); private bool Result { get { return ReadProperty(ResultProperty); } private set { LoadProperty(ResultProperty, value); } } The only helper methods provided by CommandBase are ReadProperty and LoadProperty. Command objects have no inherent business rule concept, nor do they support data binding, so the other helper methods have no meaning. You can implement any properties that are required by your business requirements. Property values automatically flow from the client to the server and back to the client through the data portal as the object moves to and from the application server. This ensures that both client-side and server-side code have access to the same set of properties. Unit of Work The unit of work (UOW) stereotype has always been important, but its use has become more widespread with the increasing popularity of asynchronous server access techniques. This is because it is very common for a UI to require several objects be present in memory on the client before a page, form, or window can be fully rendered or data bound. If you asynchronously request each of these objects you can’t predict the order in which they’ll be returned to the client, and writing code to hold off rendering the UI until all the objects are available isn’t easy. A simpler solution is to make a single request to the server for all the objects required by the UI. This makes retrieval of several different objects into a single unit of work. Another somewhat less common scenario, is where more than one object needs to be saved at the same time, as part of the same logical operation. Perhaps the application allows the user to enter information about a customer and sales order on the same screen. When the user clicks the Save button both the CustomerEdit and SalesOrderEdit objects must be saved at the same time. If either fails, both must fail. Again, a simple solution to this requirement is to make a single call to the server that combines the save operations of both objects into a single unit of work. Data Retrieval Data retrieval can be described as combining what would have been several object fetch requests into a single request. The individual objects being requested might be of almost any stereotype Using CSLA 4: Creating Business Objects Page 73 Rev 1.1 (editable, read-only, name-value list, etc.), but the UOW object itself is a read-only object that is responsible for fetching all these other objects and returning them to the calling code. The data retrieval UOW stereotype is a specialized read-only object. For example, here’s a data retrieval UOW that retrieves two other objects: [Serializable] public class CustomerEditRetriever : ReadOnlyBase { public static PropertyInfo CustomerEditProperty = RegisterProperty(c => c.CustomerEdit); public CustomerEdit CustomerEdit { get { return GetProperty(CustomerEditProperty); } private set { LoadProperty(CustomerEditProperty, value); } } public static PropertyInfo SalesRegionListProperty = RegisterProperty(c => c.SalesRegionList); public SalesRegionList SalesRegionList { get { return GetProperty(SalesRegionListProperty); } private set { LoadProperty(SalesRegionListProperty, value); } } #if !SILVERLIGHT public static CustomerEditRetriever GetCustomerEditRetriever(int customerId) { return DataPortal.Fetch(customerId); } #endif public static void GetCustomerEditRetriever( int customerId, EventHandler> callback) { DataPortal.BeginFetch(customerId, callback); } } This UOW class defines two read-only properties; one for each of the objects it will be retrieving. It implements synchronous and asynchronous factory methods to call the data portal to do the retrieval. When this object is returned by the data portal, those two properties will contain references to the objects that were requested. Although I’ll go into more detail about the data access aspects of this process in the Using CSLA 4: Data Access ebook, here’s one example of how the server-side code might be implemented. This DataPortal_Fetch method would be included in the CustomerEditRetriever class: #if !SILVERLIGHT private void DataPortal_Fetch(int customerId) { CustomerEdit = CustomerEdit.GetCustomer(customerId); SalesRegionList = SalesRegionList.GetList(); } #endif I am assuming that the CustomerEdit and SalesRegionList classes are normal root editable and read-only list objects, and so they have factory methods. The “data access” code in the UOW class needs to use those factory methods to retrieve the two objects so they can be returned to the calling code. Using CSLA 4: Creating Business Objects Page 74 Rev 1.1 This technique is something you should consider any time you need to retrieve multiple objects, and where the calling code needs to have access to all those objects before it can proceed to use them. Data Update The data update UOW stereotype is slightly different, because any save operation is a “round trip”. The object starts on the client, and the save operation moves it to the application server so the object’s state can be saved into the database by the data access layer. Then the object is returned to the client, because you must assume the object was changed during the save operation. For example, insert operations typically create new primary key or id values for the object. Update operations typically create new timestamp values for the object. Delete operations convert the object from being an existing (old) object into being a new object. The CommandBase base class is designed to move from client to server and back to the client, carrying any property values with the object. So it is the perfect base class for building a data update UOW: [Serializable] public class DataUpdateUow : CommandBase { public static PropertyInfo CustomerEditProperty = RegisterProperty(c => c.CustomerEdit); public CustomerEdit CustomerEdit { get { return ReadProperty(CustomerEditProperty); } private set { LoadProperty(CustomerEditProperty, value); } } public static PropertyInfo SalesOrderEditProperty = RegisterProperty(c => c.SalesOrderEdit); public SalesOrderEdit SalesOrderEdit { get { return ReadProperty(SalesOrderEditProperty); } private set { LoadProperty(SalesOrderEditProperty, value); } } #if !SILVERLIGHT public static DataUpdateUow Update(CustomerEdit customer, SalesOrderEdit order) { var cmd = new DataUpdateUow { CustomerEdit = customer, SalesOrderEdit = order }; cmd = DataPortal.Execute(cmd); return cmd; } #endif public static void Update(CustomerEdit customer, SalesOrderEdit order, EventHandler> callback) { var cmd = new DataUpdateUow { CustomerEdit = customer, SalesOrderEdit = order }; DataPortal.BeginExecute(cmd, callback); } } First, you should notice the CustomerEdit and SalesOrderEdit properties. These properties contain the business objects that are to be updated. Those objects will be serialized from the client to the server along with the DataUpdateUow object, and they’ll come back from the server through the data portal as well. One possible implementation of the server-side code you might write in DataUpdateUow is this: Using CSLA 4: Creating Business Objects Page 75 Rev 1.1 #if !SILVERLIGHT [Transactional(TransactionalTypes.TransactionScope)] private void DataPortal_Execute() { CustomerEdit = CustomerEdit.Save(); SalesOrderEdit = SalesOrderEdit.Save(); } #endif Both business objects are carried to the application server by the UOW object, which then saves them within a single transactional context. The resulting business objects are then returned to the client, so the calling code can make use of any new database-generated id values, timestamp values, or other new values. Notice how the factory methods in the UOW class return the UOW object as a result, which is how the calling code gains access to the resulting properties or results. The unit of work pattern is a powerful one, and can be used for retrieving or updating data as I’ve shown here, or for any other scenario where you need do perform more than one operation as a logical unit of work. Criteria Objects When you create a factory method to fetch a root object the factory method typically calls the Fetch or BeginFetch method of the data portal. This method takes zero or one parameter, which is the criteria used on the server to find the right information to create and populate the root object being requested. The same is true for the data portal’s Create, BeginCreate, Delete, and BeginDelete methods. That single parameter value is the criteria value used to identify the root object. Criteria values can be any serializable type. If you are building a pure .NET application this means any value that can be serialized with the BinaryFormatter or NDCS. If your code will run on Silverlight or WP7 this means any value that can be serialized with the MobileFormatter. Generally this means any primitive value type (such as int or double), and some special reference types like string. It also means any class you’ve created that is serializable. Simple Criteria Simple criteria values include:  Primitive .NET types such as int, double, char, etc.  Serializable value types such as DateTime and DateTimeOffset  Special types such as string or Guid  Serializable reference types (classes you create) Most business objects use simple primary keys of type int or Guid, which means most of your data portal calls can pass the simple id or key value as the criteria value. Using CSLA 4: Creating Business Objects Page 76 Rev 1.1 Complex Criteria Sometimes multiple values are required to identify a root object. Examples include cases where the database tables use compound keys, or where the user can provide several search criteria values that are used to populate a results collection. In this case you’ll need to create a serializable class with properties for each of the values that make up the criteria. There are two primary base classes used to create complex criteria types: CriteriaBase and BusinessBase. Using CriteriaBase The CriteriaBase base class is designed to make it easy to create complex criteria types that are serializable in .NET, Silverlight and WP7. This base class only supports the ReadProperty and LoadProperty helpers for property implementation, and has no support for business rules, authorization or data binding. Here’s an example of a criteria class containing two values: [Serializable] public class CustomerCriteria : CriteriaBase { public static PropertyInfo RegionIdProperty = RegisterProperty(c => c.RegionId); public int RegionId { get { return ReadProperty(RegionIdProperty); } private set { LoadProperty(RegionIdProperty, value); } } public static PropertyInfo CustomerIdProperty = RegisterProperty(c => c.CustomerId); public int CustomerId { get { return ReadProperty(CustomerIdProperty); } private set { LoadProperty(CustomerIdProperty, value); } } public CustomerCriteria(int regionId, int customerId) { RegionId = regionId; CustomerId = customerId; } } You would use these criteria in a factory method. For example, the following might be a factory method for a CustomerEdit editable root: public static CustomerEdit GetCustomerEdit(int regionId, int customerId) { var criteria = new CustomerCriteria(regionId, customerId); return DataPortal.Fetch(criteria); } Notice how the criteria class is used within the factory, so the code calling the factory doesn’t have to worry about those details. The calling code provides strongly typed parameter values and gets back the result. The value provided by CriteriaBase is that it includes the field manager so you can implement properties as shown in this example, following the same basic structure as with any other business type. Using CSLA 4: Creating Business Objects Page 77 Rev 1.1 Using BusinessBase It is also possible to create a criteria object by subclassing BusinessBase. In this case you are literally using an editable object as a criteria object. The value of doing this is that an editable object supports data binding and business rules. If you are having the user enter the criteria values directly through the UI, it can be very convenient to use data binding to connect the criteria object to the UI to collect the values. Even better, you can use standard business rules to verify that the criteria values meet any requirements or other rules. Here’s the same criteria class, but as an editable object: [Serializable] public class CustomerCriteria : BusinessBase { public static PropertyInfo RegionIdProperty = RegisterProperty(c => c.RegionId); public int RegionId { get { return GetProperty(RegionIdProperty); } set { SetProperty(RegionIdProperty, value); } } public static PropertyInfo CustomerIdProperty = RegisterProperty(c => c.CustomerId); public int CustomerId { get { return GetProperty(CustomerIdProperty); } set { SetProperty(CustomerIdProperty, value); } } } The only real difference is in the property implementations, which are now standard read-write properties. I’ve also removed the constructor, because the idea is that these property values will be set by the user through data binding. Whether you use CriteriaBase or BusinessBase, the central point I’m making is that if your criteria consists of multiple values, you need to create a serializable type to contain those values. LINQ Types CSLA 4 collection objects automatically work with LINQ to Objects, like any IEnumerable in the .NET framework. The one problem with LINQ to Objects queries is that they return an IEnumerable, which is the most basic collection type in the .NET framework. This means that if you perform a query with an identity projection (a query that returns the child objects from a collection) the result is a much more primitive collection type than the one you had originally. For example, suppose you have a CustomerInfoList read-only list, and you want to sort that list: var sorted = from r in _customerList orderby r.Name select r; The original _customerList field is a CustomerInfoList, which fully supports data binding and provides all the other services and features of a ReadOnlyListBase object. The query result, the sorted field, is a simple IEnumerable and doesn’t support data binding or any of the features of a read-only list. Using CSLA 4: Creating Business Objects Page 78 Rev 1.1 This is even more confusing if you start with an editable list object. In that case, your query will also return a simple IEnumerable that doesn’t support data binding. But worse, if you add or remove items from the query result they are not added or removed from the original list. It is that original list that will be saved through the data portal! Sometimes these issues aren’t a problem. Sometimes you need a sorted or filtered version of the original list and you won’t bind it to a UI or otherwise manipulate the result of the query. But if you do need to use the result of the query for data binding or manipulation, CSLA 4 includes a solution: the LinqObservableCollection type. LinqObservableCollection The LinqObservableCollection class is an “intelligent wrapper” around a LINQ query result. It creates a view over the original list based on the query result that supports data binding. Even better, when you add or remove items from a LinqObservableCollection, those items are automatically added or removed from the original list. There are several ways to create a LinqObservableCollection, but the most common technique is to use the ToSyncList extension method provided by CSLA 4. Alter the previous query like this: var sorted = (from r in _customerList orderby r.Name select r).ToSyncList(_customerList); The sorted field is now a LinqObservableCollection that provides a “live view” over the original collection. You get the same basic result, which is that the original collection has been sorted, but this result supports data binding, change notification, and when items are added or removed from the sorted field, they are automatically added or removed from _customerList as well (assuming _customerList is an editable list). Another way to create a LinqObservableCollection is to explicitly create the object: var tmp = from r in _customerList orderby r.Name select r; var sorted = new LinqObservableCollection(_customerList, tmp); The result of this code is the same as the previous example, I’m explicitly doing by hand what the ToSyncList extension method did in the previous example. This example probably shows more clearly that the LinqObservableCollection combines the original source list and the query result to create a live view of the original list based on the query results. You can use LinqObservableCollection to filter and sort any ObservableCollection, including those created with BusinessListBase and ReadOnlyListBase from CSLA .NET. Windows Forms Types Before WPF was introduced in .NET 3.0 most smart client applications were created using Windows Forms. The Windows Forms technology laid the groundwork for most of the data binding concepts we know today, but when WPF was introduced Microsoft decided to replace the interfaces and related BindingList base class with a new interface and ObservableCollection type for bindable collections. Using CSLA 4: Creating Business Objects Page 79 Rev 1.1 Windows Forms pre-dates WPF and its ObservableCollection, so you can’t bind an ObservableCollection to a Windows Forms UI and get the behaviors you’d expect. Windows Forms data binding only works properly with BindingList collections. On the other hand, WPF only works properly with ObservableCollection collections. It works to some degree with BindingList collections, but you give up normal sorting and filtering capabilities that are automatic with ObservableCollection types. As developers, this puts us in an awkward situation; because we can choose to create a collection that works with Windows Forms, or with WPF, but not both. ASP.NET works with either collection base class. Silverlight and WP7 don’t even have the concept of a BindingList, and only support ObservableCollection types. Table 16 lists which types work in which UI technologies. UI technology Collection type WPF ObservableCollection BindingList (partial functionality) Silverlight ObservableCollection WP7 ObservableCollection Windows Forms BindingList ASP.NET Web Forms ObservableCollection BindingList ASP.NET MVC ObservableCollection BindingList WCF service ObservableCollection BindingList asmx service ObservableCollection BindingList Windows Workflow ObservableCollection BindingList Console ObservableCollection BindingList Table 16. Collection types and UI technologies In summary, ObservableCollection works everywhere except Windows Forms, while BindingList doesn’t work in any modern XAML-based UI technology. In CSLA 4 the default collection base types inherit from ObservableCollection, including:  BusinessListBase  ReadOnlyListBase  DynamicListBase  NameValueListBase Using CSLA 4: Creating Business Objects Page 80 Rev 1.1 These types can be used to support all UI technologies except Windows Forms. If you are building a collection that must be data bound to a Windows Forms UI, you’ll need to use the alternative base types provided by CSLA 4:  BusinessBindingListBase  ReadOnlyBindingListBase  DynamicBindingListBase It is not ideal that a business layer developer must choose a collection type based on the UI technology that will be using the collection. Unfortunately, Microsoft’s shift from BindingList to ObservableCollection leaves us with no realistic alternative. BusinessBindingListBase The BusinessBindingListBase type supports the editable list stereotype in exactly the same way as the BindingListBase class. Other than using a different base class in your business collection code, your collection code will be the same. Any code that interacts with a BusinessBindingListBase subclass will find some differences in terms of supported events and data binding behaviors, because this is ultimately a subclass of BindingList, which supports Windows Forms data binding. DynamicBindingListBase The DynamicBindingListBase type supports the editable list stereotype in exactly the same way as the DynamicListBase class. Other than using a different base class in your business collection code, your collection code will be the same. Any code that interacts with a DynamicBindingListBase subclass will find some differences in terms of supported events and data binding behaviors, because this is ultimately a subclass of BindingList, which supports Windows Forms data binding. ReadOnlyBindingListBase The ReadOnlyBindingListBase type supports the editable list stereotype in exactly the same way as the ReadOnlyListBase class. Other than using a different base class in your business collection code, your collection code will be the same. Any code that interacts with a ReadOnlyBindingListBase subclass will find some differences in terms of supported events and data binding behaviors, because this is ultimately a subclass of BindingList, which supports Windows Forms data binding. At this point, in the ebook you should understand the stereotypes supported by CSLA 4, along with the base classes you will use to implement business classes for each stereotype. The code in your business classes relies on the property implementation, metastate, serialization and other concepts discussed in Chapters 1 and 2. I’ll now conclude the ebook by discussing how you can implement and use business rules in editable objects, and authorization business rules in editable and read-only objects. Using CSLA 4: Creating Business Objects Page 81 Rev 1.1 Using CSLA 4: Creating Business Objects Page 82 Rev 1.1 Chapter 4: Business Rules One of the primary features of CSLA 4 is the new business rules system that allows you to write several different types of rules. The goal is to allow you to easily maintain all your business logic in the business layer, which is composed of business domain objects created using the stereotypes discussed in Chapter 3. The editable child and root object stereotypes support all the types of rule I’ll discuss in this chapter. Most other stereotypes only support authorization rules, and some stereotypes don’t support rules at all (CriteriaBase doesn’t support any rules for example). Table 17 lists the types of rules you can implement using the CSLA 4 rules system. Type of rule Description Validation Validation rules check one or more properties to see if the values are valid according to the rule. These rules may be synchronous or asynchronous and can indicate the property is in error, has a warning, or should display an informational message. DataAnnotations validation The System.ComponentModel.DataAnnotations namespace includes a ValidationAttribute base class that allows you to create custom validation attributes. These are supported by CSLA 4 and are executed along with other validation rules. Business Business rules alter one or more property values based on the algorithm or behavior implemented by the rule. These rules can take input values, do processing, and alter property values as a result. They may be synchronous or asynchronous. Per-object business and validation Per-object business and validation rules are like normal business and validation rules, but they are not attached to any specific property. Instead they are attached to the business object itself. This type of rule is usually complex or expensive in terms of computation or data access. Property and method authorization Per-property authorization rules check to see if the current user is allowed to read a property, write to a property, or execute a method. These rules are checked on access to the property or method, and allow or deny access to the user. Business type authorization Per-type authorization rules check to see if the current user is allowed to create, get, edit, or delete instances of the business object type. These rules are checked by the data portal to determine whether the user is allowed to perform the requested action. Table 17. Types of business rule supported by CSLA 4 In most cases rules are executed automatically by CSLA .NET. For example, when a user attempts to get or set a property value the appropriate authorization rules are executed to see if the user Using CSLA 4: Creating Business Objects Page 83 Rev 1.1 should be allowed to perform the operation. When a property is changed in an editable object the business and validation rules associated with that property are executed, along with rules associated with properties designated as being dependent on the changed property. This includes any DataAnnotations attributes. They are considered to be validation rules, no different from other CSLA 4 validation rules. What is interesting about DataAnnotations attributes is that they are sometimes also executed by certain UI technologies. For example, ASP.NET MVC and the Silverlight DataForm control both execute DataAnnotations attributes on your behalf. The point of having CSLA .NET execute them, is that this way you know for sure that they are executed, regardless of the type of UI or UI control you use to build your application. The CSLA 4 rules system is designed to be easy to understand and use, and yet very powerful and flexible. Most applications have rules that are pretty straightforward, and will be easy to implement. Other applications may want to leverage external rules engines, or build rules in the form of workflows or other technologies. These things are possible, but are obviously more complex than “normal” rules. In this chapter I’ll start each topic by discussing the straightforward approach, and then I’ll discuss the concepts you’ll need to understand to implement more advanced and complex scenarios. I’ll discuss business and validation rules first, followed by authorization rules. While all rules are very similar, there are enough differences between these types of rule that I’ll cover them separately. Business and Validation Rules Business and validation rules are implemented and executed in the same way within CSLA 4. Validation rules indicate whether a property has an error, warning, informational message, or is valid. Business rules alter property values and don’t validate property values. You should use validation rules or DataAnnotations attributes to validate any properties in your editable objects. This will lead to very consistent and maintainable code for validation. As you’ll see in the rest of the Using CSLA 4 ebook series, the validation rules you implement in your business objects can be expressed to the user in every type of UI technology, with the exception of certain ASP.NET applications. So for the most part this means you should expect to write your validation logic one time in the business classes, and not have to replicate that logic in the presentation layer. You should use business rules to alter property values in your objects based on any sort of rule, algorithmic process, or calculation. Business rules can do something as simple as upper-casing a string value, or as complex as performing a tax, price, or discount calculation. Business and validation rules can interact with other business objects if necessary. For example, you might implement a validation rule that determines whether an object’s id value already exists in the database. To do this your rule will use a command object to interact with the server, and if the value exists will probably return a warning or error message to indicate that the property value already exists. Another example would be a business rule that uses a read-only object to retrieve some values from a massive database table, so the rule can use those values as part of its algorithmic processing. Using CSLA 4: Creating Business Objects Page 84 Rev 1.1 Because rules might use other objects, some rules will be asynchronous. Remember that Silverlight requires that all server interaction be asynchronous, and so any rule that uses an object which must execute code on the server must support that asynchronous operation. The way rules are implemented in CSLA 4 allows you to use the same basic coding structure for synchronous and asynchronous rules, though there are certain limitations on asynchronous rules to help you avoid all the potential complexity that comes with asynchrous and parallel processing. Associating Rules with Properties and Types Business and validation rules may be associated with properties or business types. Most rules are associated with properties, but some rules apply to many properties (or to no properties) and so must be associated with a business type instead of a specific property. There are two ways to associate rules with properties or types. You can create the rules as DataAnnotations attributes and apply the attribute to your business class or property, or you can create the rule as a CSLA 4 IBusinessRule and associate the rule to a property or business class by overriding the AddBusinessRules method in your editable root or child class. Using DataAnnotations Attributes If your rule is a DataAnnotations attribute it will be a subclass of ValidationAttribute. I’ll discuss how to create such rules later in this chapter, but to use these attributes is as simple as applying the attribute to your class or property. For example: public static readonly PropertyInfo NameProperty = RegisterProperty(p => p.Name); [Required] public string Name { get { return GetProperty(NameProperty); } set { SetProperty(NameProperty, value); } } The Required attribute is a standard attribute from the DataAnnotations namespace, and indicates that this is a required property. None of the standard DataAnnotations validation attributes are designed to apply to a business class, but you might create your own validation attributes that are designed for that purpose. In such a case, you’ll apply the attribute to your class declaration. These attributes are included in the list of validation rules for the object by CSLA .NET by the implementation of the AddBusinessRules method found in the BusinessBase class. If you override AddBusinessRules you should make sure to call the base implementation to ensure validation attributes are used properly by CSLA .NET. Using AddBusinessRules If your rule is a CSLA .NET rule it will implement IBusinessRule or be a subclass of BusinessRule, both of which are found in the Csla.Rules namespace. I’ll discuss how to create such rules later in this chapter. You associate these rules with your properties or business class by overriding the AddBusinessRule method in your editable root or child business class. For example: Using CSLA 4: Creating Business Objects Page 85 Rev 1.1 protected override void AddBusinessRules() { base.AddBusinessRules(); BusinessRules.AddRule(new Csla.Rules.CommonRules.Required(NameProperty)); BusinessRules.AddRule(new MyClassRule()); } This example adds a Required rule from the Csla.Rules.CommonRules namespace, and associates the rule with the Name property. Notice the use of the static metadata field NameProperty. Also notice that a rule is an object, and this code creates an instance of that rule object. This one rule instance is shared across all instances of the business type. This code creates a single Required rule instance, and that instance is reused by every business object created for this specific business class. Every rule can have a primary property to which the rule applies or is associated. Rules that require a primary property should require that property be provided to the rule’s constructor. The Required rule follows this recommendation, requiring the primary property be specified as the rule is created. The example also adds a MyClassRule rule to the business class itself. Notice that the MyClassRule doesn’t require that a primary property be provided to the constructor, so the rule isn’t associated with any particular property. This tells CSLA .NET to associate the rule with the business type itself. Rule Priorities By default rules are all added at the same priority and you can’t predict the order in which the rules will be executed. This isn’t always ideal, because you might need certain rules to run before other rules. As you add rules to your properties or business type in your AddBusinessRules override you can specify priority values for each rule. For example: protected override void AddBusinessRules() { base.AddBusinessRules(); BusinessRules.AddRule(new Csla.Rules.CommonRules.Required(NameProperty) { Priority = 0 }); BusinessRules.AddRule(new ToUpper(NameProperty) { Priority = 1 }); } In this example I’m associating two rules with the Name property. The Required rule is at priority 0, which is the default. The ToUpper rule is at priority 1, and so it will run after the Required rule. Priority values are an int, and rules are executed from smallest to largest value, with the default being 0. Multiple rules can have the same priority, and by default all rules are at priority 0. Rules within each priority will run in an indeterminate order. This means you can’t predict the order in which the rules will be run. If you need to ensure that one rule runs before another rule you should explicitly set the rule priorities. Using CSLA 4: Creating Business Objects Page 86 Rev 1.1 Asynchronous rules are always started after all synchronous rules. All asynchronous rules are started at the same time, and they’ll complete in an indeterminate order. There is no provision in CSLA .NET for controlling the order of execution of asynchronous rules. It is a good idea to add asynchronous rules at priority 1 or higher. This will prevent the asynchronous rules from starting if any of the synchronous validation rules at priority 0 (the default priority) fail. In most cases you won’t want to run the asynchronous rules unless all the basic validation rules have passed. I’ll discuss short-circuiting rules in the next section of this chapter. All DataAnnotations validation attributes are always at priority 0. The DataAnnotations attributes have no concept of priority, so there’s no way to specify different priorities for these attributes. Short-Circuiting Rules Closely related to the idea of rule priority is short-circuiting. The idea here is that you might want to run your inexpensive rules first, and only run more expensive rules if the inexpensive ones don’t mark the property as invalid. For example, a Required rule is quite inexpensive because it only checks to see if a string value has a non-zero length. But that same property might have a rule that runs code on the application server to talk to the database to verify the property value. That’s clearly more expensive because it requires interacting with servers and the database. There’s probably no value in verifying an empty value against the database, so if the Required rule fails you don’t want to run the more expensive rule. By default, CSLA .NET will execute all rules with priority 0 or less (so all rules with negative priorities and priority 0). Rules at priority 1 and higher will only execute if all validation rules at priority 0 or smaller pass. If the property has been marked as invalid by the time priority 1 rules would run, they won’t be executed. The priority at which automatic short-circuiting starts is controlled by the ProcessThroughPriority value, and you can change that value in the AddBusinessRules override: protected override void AddBusinessRules() { BusinessRules.ProcessThroughPriority = 5; base.AddBusinessRules(); } In this example I’ve changed the value to 5, so in instances of this particular business class rules at priority 5 or less will always execute, and those at priority 6 and higher will only run if no validation rule has failed. One common use of short-circuiting is to prevent rules from executing if the user isn’t even allowed to write to the property. I’ll discuss authorization rules later in this chapter, but for this discussion it is enough to understand that it is possible to prevent a user from writing to a property. The following rule uses the CanWriteProperty method on the Target object to determine whether the user is authorized to write to the specified property: public class StopIfNotCanWrite : Csla.Rules.BusinessRule { public StopIfNotCanWrite(Csla.Core.IPropertyInfo property) Using CSLA 4: Creating Business Objects Page 87 Rev 1.1 : base(property) { } protected override void Execute(RuleContext context) { var target = (Csla.Core.BusinessBase)context.Target; if (!target.CanWriteProperty(context.Rule.PrimaryProperty)) context.AddSuccessResult(true); } } If the user isn’t authorized, the AddSuccessResult method is invoked, passing a parameter value of true to trigger explicit short-circuiting. In that case, no more rules will be executed for this property. Adding this rule to a property at a priority of -1 will cause it to run before all the rules added at the default priority of 0 or higher: protected override void AddBusinessRules() { base.AddBusinessRules(); BusinessRules.AddRule(new StopIfNotCanWrite(OrderDateProperty) { Priority = -1 }); } At this point, you should understand how to associate rules with properties or business classes, as well as how to control the order of rule execution using priorities and short-circuiting. Rule Sets Some applications are designed for use in shared hosting environments. Such an application runs on a server, and it is used by users who don’t work for the same organization. This model is quite common in software as a service scenarios, where the application is centrally hosted, but is made available to numerous unrelated customers. In such a case it is possible that different customers will have different business rule requirements. A given business object might have one set of rules for one customer, and a different set of rules for another customer. To accommodate this type of situation, CSLA .NET has a concept called rule sets. In my examples so far, all the calls to the AddRule method in the AddBusinessRules override have added rules to the default rule set. Because most applications will have only one set of rules, this is a good default behavior. If your application has multiple rule sets, you’ll need to specify the rule set before calling the AddRule method. And you’ll need to add all the rules for each rule set individually. For example: protected override void AddBusinessRules() { base.AddBusinessRules(); // add rules to default rule set BusinessRules.AddRule(new Csla.Rules.CommonRules.Required(NameProperty) { Priority = 0 }); BusinessRules.AddRule(new ToUpper(NameProperty) { Priority = 1 }); // add rules to rule set A BusinessRules.RuleSet = "RuleSetA"; BusinessRules.AddRule(new Csla.Rules.CommonRules.Required(NameProperty) { Priority = 0 }); BusinessRules.AddRule(new ToUpper(NameProperty) { Priority = 1 }); // add rules to rule set B Using CSLA 4: Creating Business Objects Page 88 Rev 1.1 BusinessRules.RuleSet = "RuleSetB"; BusinessRules.AddRule(new Csla.Rules.CommonRules.Required(NameProperty) { Priority = 0 }); BusinessRules.AddRule(new ToUpper(NameProperty) { Priority = 1 }); // use default rule set BusinessRules.RuleSet = "default"; } While this code is setting the same rules into each rule set, a real application would load only appropriate rules into each rule set based on your business requirements. The first two rules are added to the default rule set, because the RuleSet property hasn’t been specified. Then I change the RuleSet property to “RuleSetA” and add a couple rules to that rule set. The same is done for “RuleSetB”. Finally the RuleSet property is set to “default” so the application will use the default rule set. The RuleSet property controls the rule set used by this specific business object, allowing you to change the rule set for each specific business object. You will typically do this based on the state of the business object. For example, business objects created or retrieved for Customer A will use the rule set for that customer, while business objects created or retrieved for Customer B will use a different rule set. You’ll normally set the RuleSet property for an object as part of its data access code when the object is created or retrieved. Executing Rules Business and validation rules are associated with either a property or to a business object. Most rules are associated with properties. These rules only apply to the editable object stereotype, and so are only available to objects that subclass BusinessBase. Per-Property Rules Rules that are associated with properties are automatically executed by the SetProperty and SetPropertyConvert methods. If you follow the property implementation guidelines from Chapter 1, this means your rules will run automatically as properties are changed. The LoadProperty and LoadPropertyConvert methods do not execute rules. It is possible to suppress rules so they aren’t automatically executed by SetProperty or SetPropertyConvert. I’ll discuss how to suppress rule execution later in this chapter. If you are using one of the DataPortal_XYZ data access models (covered in the Using CSLA 4: Data Access ebook) the rules are also automatically invoked when the business object is created. The base implementation of DataPortal_Create runs all rules associated with all properties and the business type as the object is created. Dependent Properties The SetProperty and SetPropertyConvert methods automatically execute business and validation rules when a property’s setter is called. It is possible to tell CSLA .NET that a relationship exists between properties in your object, indicating that some properties are dependent on other properties. After rules for a property are executed, any rules associated with dependent properties are also executed. Using CSLA 4: Creating Business Objects Page 89 Rev 1.1 For example, in a change password dialog you’ll often require that the user enter their new password twice. And you’d have a rule that checks to make sure both password values match. If the user mis-types their new password they could fix the value by retyping the password in either of the input controls of the UI. This means the NewPassword1 and NewPassword2 properties of your business object are dependent on each other. Any time rules are run for one property, they should be run for the other as well. As another example, in a SalesOrderEdit class you might have a CreditLimitOverride property. If this value is set, it means the user has overridden the customer’s normal credit limit, and you might require that they enter another value to confirm that they meant to perform the override. This ConfirmOverride property would be dependent on the CreditLimitOverride property. Rules can affect multiple properties, even though each rule has exactly zero or one primary properties. I’ll discuss this more when I show you how to create your own rules. If a rule affects multiple properties, CSLA .NET will automatically consider all affected properties to be dependent on the primary property. For example: protected override void AddBusinessRules() { base.AddBusinessRules(); BusinessRules.AddRule( new CalculateTotal(Value1Property, Value2Property, Value3Property)); } This CalculateTotal rule requires that three properties be provided in its constructor. Let’s assume it adds Value1 and Value2 and puts the result in Value3. This means Value3 is affected by this rule, so when rules are executed for the Value1 property, all the rules for the Value3 property will also be executed. You should understand that all rules for the primary property are executed first, in priority order. Then rules are run for each dependent property, one property at a time. The rules for each dependent property are also executed in priority order. Any asynchronous rules for each property are started after all synchronous rules for that property are complete. Because asynchronous rules are asynchronous, their executions will likely overlap, and you need to take that into consideration when implementing any asynchronous rules. CSLA .NET provides a rule designed specifically to set up dependency relationships between properties. In many cases, dependencies are automatically detected because properties affected by a rule become dependent properties. But sometimes you’ll want to explicitly designate a dependency relationship by using the Dependency rule: protected override void AddBusinessRules() { base.AddBusinessRules(); BusinessRules.AddRule( new Csla.Rules.CommonRules.Dependency(CreditLimitOverrideProperty, ConfirmOverrideProperty)); } The Dependency rule is found in the Csla.Rules.CommonRules namespace, and it requires a primary property, along with a list of dependent properties. In this example the primary property is CreditLimitOverride, and the dependent property is the ConfirmOverride property. This means Using CSLA 4: Creating Business Objects Page 90 Rev 1.1 that any time rules are executed for CreditLimitOverride, the rules for ConfirmOverride will be executed as well. Per-Type Rules Rules associated with a business type are only automatically executed when you use one of the DataPortal_XYZ data access models (covered in the Using CSLA 4: Data Access ebook). In this case the rules are automatically invoked when the business object is created. The base implementation of DataPortal_Create runs all rules associated with all properties and the business type as the object is created. Other than this one scenario, you always need to manually cause the execution of per-type rules by calling either CheckRules or CheckObjectRules on the BusinessRules object as discussed in the next section of this chapter. Manually Executing Rules There are times when you will need to explicitly trigger the execution of some or all the rules for your object. Some examples include:  After loading the object with data from the database, when you don’t trust the data from the database  When a data portal request from a client arrives at your web or application server and you want to rerun rules because you are concerned the client intetrigty has been compromised  On a ChildChanged event when a parent object needs to run rules based on changes to a child object As I’ve mentioned, the BusinessBase class has a protected property named BusinessRules. This BusinessRules object includes methods you can use to invoke rules as described in Table 18. Method Description CheckRules Executes all rules for the object and for all properties of the object CheckRules(property) Executes rules for the specified property, followed by rules for any dependent properties CheckObjectRules Executes per-type rules for the object Table 18. Methods used to manually execute rules You can use these methods when you need to manually execute rules within your object. It is also possible to use the ICheckRules interface from the Csla.Core namespace to invoke rules on other objects. This interface is intended for use by UI framework developers building UI code for block-mode interfaces (such as web or service interfaces). You may find other uses for this interface, but you must be careful because this does break encapsulation and widespread use (or misuse) of this interface can quickly lead to very unmaintainable code. Using CSLA 4: Creating Business Objects Page 91 Rev 1.1 The ICheckRules interface has a CheckRules method you can call, and it calls the CheckRules method of BusinessRules for the specific business object. It runs all per-type and property rules for the object. Executing Rules in Parent Objects There are times when you’ll want to execute a rule in a parent object based on the fact that a child object has been changed. For example, a SalesOrderEdit parent object may need to recalculate its total cost value as its child LineItemEdit objects are added, removed, and changed. To do this, you should implement the rule and associate it with a property of the parent business type. But you also need to make sure the rule is executed each time a child object is changed, and to do that you’ll need to override the OnChildChanged method in the parent business class. For example: protected override void OnChildChanged(Csla.Core.ChildChangedEventArgs e) { base.OnChildChanged(e); BusinessRules.CheckRules(SalesOrderEdit.LineItemsProperty); } The OnChildChanged method (and corresponding ChildChanged event) execute each time a child object is changed within the object graph. You can use the ChildChangedEventArgs parameter to get specifics about the change, which can be useful when you only want to call CheckRules when certain child objects are changed. In this example, when OnChildChanged is invoked the code calls CheckRules to run all rules associated with the LineItems property, ensuring that those rules are executed each time a child object is changed. Suppressing Rule Execution Sometimes you’ll need to prevent the normal execution of business, validation, and authorization rules. The two primary scenarios where it is important to suppress rules are during data access or object persistence, and in block mode interfaces such as most web and service interfaces. In the data access scenario, you are typically setting or getting property values as those values are loaded from the database or stored into the database. You’ll rarely want to run rules during this process because the data in the database is often assumed to be valid, or because the rules were already run as the user interacted with the business object. Re-running the rules during persistence leads to complexity and performance issues. In the block mode interface scenario, the user has typically entered many values in a web UI, or a whole set of values has been provided as part of a call to your service. Either way, you have a whole set of values that will be placed into the properties of your business object. It is often more efficient to temporarily suppress all rule checking, load all the property values, and to then explicitly run all the rules for the object. There are three ways you can prevent or suppress rules from running when SetProperty or SetPropertyConvert are invoked. These are listed in Table 19. Using CSLA 4: Creating Business Objects Page 92 Rev 1.1 Technique Description BypassPropertyChecks The BusinessBase class has a protected member called BypassPropertyChecks that is designed for use in a using block. All code within a using (BypassPropertyChecks) code block will have rule execution suppressed, so no business, validation, or authorization rules will be run. using (BypassPropertyChecks) { // set properties here without executing // any rules } This technique is designed for use within data access code and I’ll use this technique in the Using CSLA 4: Data Access ebook. If you are using what are called DataPortal_XYZ methods you’ll write code like I’m showing here. If you are using the ObjectFactory data access technique the ObjectFactory base class from the Csla.Server namespace also has a BypassPropertyChecks method to provide similar functionality. ICheckRules The BusinessBase class implements the ICheckRules interface from the Csla.Core namespace. This interface defines methods that allow code to suppress rule execution, resume rule execution, and to force execution of all business and validation rules. ((Csla.Core.ICheckRules)_customer).SuppressRuleChecking(); try { // set properties here without executing // any rules } finally { ((Csla.Core.ICheckRules)_customer).ResumeRuleChecking(); ((Csla.Core.ICheckRules)_customer).CheckRules(); } The purpose of the ICheckRules interface is to support UI frameworks, specifically for block mode interfaces such as those created with ASP.NET Web Forms, ASP.NET MVC, asmx services, and WCF services. The code I’m showing here would typically be written in the code handing a web postback, a controller method, or in a service implementation. SuppressRuleChecking The BusinessBase class has a protected member called BusinessRules that you will use to interact with the business rules for each object. The BusinessRules object has a SuppressRuleChecking property you can set to true to suppress rules, and false for normal operation. BusinessRules.SuppressRuleChecking = true; try { // set properties here without executing // any rules } finally { BusinessRules.SuppressRuleChecking = false; Using CSLA 4: Creating Business Objects Page 93 Rev 1.1 } This SuppressRuleChecking property is used by the BypassPropertyChecks and ICheckRules techniques, so you can consider this to be the lowest level technique. Table 19. Techniques for suppressing business rule execution The most common case where you will suppress rule checking is during object persistence as you load and save data from your object. The BypassPropertyChecks technique is commonly used to address that requirement. Somewhat less common is where a block mode UI needs to suppress and trigger rule execution using ICheckRules. In any case, you can use these techniques as required by your application to suppress and trigger rule execution. You should now understand how rules are associated with properties, property dependencies, and how and when rules are executed. I’ll move on to discuss how you create business rules and validation rules. Implementing Business and Validation Rules Business and validation rules use the same base types and follow the same implementation structure. The difference is that business rules change property values, while validation rules return error, warning, or information results. Structure of a Rule A business or validation rule is implemented as a class that implements the IBusinessRule interface or inherits from the BusinessRule base class defined in the Csla.Rules namespace. While there are many ways you can implement rules and a lot of advanced features at your disposal, the basic structure of a rule class is pretty consistent. For example, here’s a rule that must be associated with a business object property: public class MyRule : Csla.Rules.BusinessRule { public MyRule(Csla.Core.IPropertyInfo primaryProperty) : base(primaryProperty) { InputProperties = new List { PrimaryProperty }; } protected override void Execute(Csla.Rules.RuleContext context) { object input = context.InputPropertyValues[PrimaryProperty]; // implement rule here } } Here is a similar rule that changes the value of the primary property associated with the rule: public class MyRule : Csla.Rules.BusinessRule { public MyRule(Csla.Core.IPropertyInfo primaryProperty) : base(primaryProperty) { Using CSLA 4: Creating Business Objects Page 94 Rev 1.1 InputProperties = new List { PrimaryProperty }; } protected override void Execute(Csla.Rules.RuleContext context) { object input = context.InputPropertyValues[PrimaryProperty]; // implement rule here context.AddOutValue("new value"); } } You can use the AddOutValue method shown here to have the rule change the primary property’s value when the rule completes. The AddOutValue method can also be used to change other property values. In that case you must add those properties to the AffectedProperties list in the rule’s constructor. I’ll discuss the AffectedProperties list later in this chapter. While most rules are associated with a property, that’s not true of all rules. Here’s another example, of a rule that doesn’t require that it be associated with a property: public class MyRule : Csla.Rules.BusinessRule { protected override void Execute(Csla.Rules.RuleContext context) { // implement rule here } } Because no primary property is required by this rule’s constructor, the rule may or may not be associated with a specific property. That decision is deferred to your code in the AddBusinessRules override of the business class. The rule classes inherit from the BusinessRule base class. You could choose to implement the IBusinessRule interface manually, though this is an advanced scenario that should be extremely uncommon. When a rule requires an associated property, or other input values, it will implement a constructor to require those values. Rules that don’t require such input won’t have an explicit constructor. The implementation of the rule is always in the Execute method, which is provided with a RuleContext parameter. This parameter contains input values and provides mechanisms for rules to provide output results. RuleContext Parameter Central to any business or validation rule implementation is the RuleContext parameter that is provided to the Execute method. The RuleContext parameter value contains the members listed in Table 20. Member Description Rule Gets a reference to the business rule object Target Gets a reference to the business object; defaults to null for async rules unless you set ProvideTargetWhenAsync to true in the rule’s constructor Using CSLA 4: Creating Business Objects Page 95 Rev 1.1 InputPropertyValues Gets a dictionary containing input property values from the business object; the properties provided are defined in the InputProperties list you set in the rule’s constructor OutputPropertyValues Gets a dictionary containing output property values resulting from this rule; you’ll normally use the AddOutValue method instead of directly altering this dictionary GetChainedContext Gets a chained RuleContext object you can provide to a rule you invoke from your rule (I’ll discuss rule chaining later in this chapter) AddErrorResult Adds a validation error result to the output of the rule AddWarningResult Adds a validation warning result to the output of the rule AddInformationResult Adds a validation informational result to the output of the rule AddSuccessResult Adds a validation success result to the output of the rule AddOutValue Adds a property value to the output of the rule; these values will be used to update the properties once the rule is complete; all properties used with AddOutValue must be added to the AffectedProperties list in the rule’s constructor Complete Indicates that the asynchronous rule is complete so CSLA .NET knows to process the rule’s results; not necessary for synchronous rules Table 20. Members provided by the RuleContext type When creating business rules you’ll typically use the following members:  InputPropertyValues  AddOutValue  Complete (async only) When creating validation rules you’ll typically use the following members:  InputPropertyValues  AddErrorResult  AddWarningResult  AddInformationResult  AddSuccessResult  Complete (async only) Using CSLA 4: Creating Business Objects Page 96 Rev 1.1 If you are willing to create strongly coupled rules that are directly aware of the business object type you can make use of the Target property to get a reference to the business object. This should be avoided to minimize coupling between your rules and your business types. Additionally, the Target property can only be used on the UI thread and any use of this property on a background thread will lead to cross-thread exceptions or nasty threading bugs. The GetChainedContext method is used to create a new RuleContext object that can be passed to another rule you explicitly invoke from within your rule’s Execute method. This allows one rule to invoke other rules through a process called rule chaining. I’ll discuss rule chaining later in this chapter. I mentioned two base types that are important for implementing rules: IBusinessRule and BusinessRule, both from the Csla.Rules namespace. IBusinessRule Interface All business rule types must implement the IBusinessRule interface. This interface is used by the CSLA .NET business rules subsystem to interact with all business rules. You can implement this interface directly if you choose, but I recommend using the BusinessRule base class for most rules. The IBusinessRule interface defines the members listed in Table 21. Member Description PrimaryProperty Defines the primary property to which the rule is associated; if null then the rule is associated with a business type. RuleName Gets the unique name of the rule as a rule:// URI. Priority Gets the priority of the rule. IsAsync Gets a value indicating whether the Execute method will run asynchronous code. ProvideTargetWhenAsync Gets a value indicating whether the RuleContext should include a Target value for an async rule. The default is false, because use of the Target value in async code will cause serious problems that are hard to identify and debug. Execute The method that is invoked when the rule is executed. This is the method that implements the rule. InputProperties Gets a list of IPropertyInfo fields defining the property values that should be provided to the Execute method through the RuleContext parameter. AffectedProperties Gets a list of properties that are affected by this rule. These are considered to be dependent properties, and their rules are run after the synchronous rules for the primary property are complete. If your rule sets output values with the AddOutVaue method, the output properties must be added to the AffectedProperties list. Table 21. Members defined in IBusinessRule interface Using CSLA 4: Creating Business Objects Page 97 Rev 1.1 If you do choose to manually implement the IBusinessRule interface, I strongly recommend you carefully examine the BusinessRule class to see how each member is implemented. That should provide you with insight into how your code must work. BusinessRule Base Class The simplest way to create a business rule is to inherit from the BusinessRule base class in the Csla.Rules namespace. This class implements the IBusinessRule interface and provides a simple and accessible way to create business rules. The BusinessRule class implements the members listed in Table 21, along with a couple extra methods: LoadProperty and ReadProperty. These methods are extremely useful when implementing rules that need to get and set property values on the business object. The BusinessRule base class provides three major features:  Automatic generation of a unique RuleName value  LoadProperty method  ReadProperty method I’ll discuss each feature. RuleName Property Perhaps the most important behavior is that the BusinessRule base class automatically creates a valid RuleName value for each rule object. The RuleName property must contain a rule:// URI that uniquely identifies each specific rule object. Normally this URI will look something like those listed in Table 22. URI Description rule://ruleType/null Rule associated with a business type rule://ruleType/null?p1=v1 Rule associated with a business type, where the rule has additional property values rule://ruleType/primaryProperty Rule associated with a specific property rule://ruleType/primaryProperty?p1=v1 Rule associated with a specific property, where the rule has additional property values Table 22. Examples of rule:// URI values The ruleType value is the assembly qualified type name of the business rule class, so it can be quite long and could contain invalid characters for a URI. This value is “fixed” by the RuleUri class in the Csla.Rules namespace so it doesn’t contain any invalid characters, and won’t exceed the maximum length for a URI value. The primaryProperty value is the name of the primary property to which this rule is associated. If the rule is associated with a business type instead of a property, the value will be “null”. The RuleUri object that contains the URI for the RuleName property is a protected member of the BusinessRule base class. You can access this value through the RuleUri property. Using CSLA 4: Creating Business Objects Page 98 Rev 1.1 ReadProperty and LoadProperty Methods In the rule examples you’ve seen so far, I’ve been using the InputProperties and AddOutValue techniques to get and set property values on the business object. For most rules this is a good approach, because that technique works for both synchronous and asynchronous rules. Also, that technique doesn’t require that the rule have any specific knowledge about the business class because the rule interacts with the property values it has been provided through the RuleContext parameter. Alternately, you might choose to implement your rule to directly interact with the business object and its properties. This can be more direct and efficient, but can lead to tight coupling between your rule implementation and a specific business class. To do this, you’ll use the Target property of the RuleContext parameter within the Execute method of your rule. You might try something like this alternative to the ToUpper rule example I used earlier: protected override void Execute(Csla.Rules.RuleContext context) { var target = (ProductEdit)context.Target; target.Name = target.Name.ToUpper(); } This won’t actually work, however. It is important to remember that the property getter and setter code in a normal editable object uses GetProperty and SetProperty, and so all authorization, business, and validation rules are executed automatically. Your rule is almost certainly executing because the primary property was changed. In this case, if your code directly sets the primary property, (in this example the Name property) you’ll end up in a recursive loop that will ultimately cause a stack overflow exception. To avoid this issue you must not directly use the property getter or setter to get or set property values from the business object. The purpose of the ReadProperty method provided by the BusinessRule class is to allow you to get property values of the business object without triggering authorization rules. The purpose of the LoadProperty method is to allow you to set property values of the business object without triggering authorization, business, or validation rules. The correct implementation to directly interact with the business object’s properties is this: protected override void Execute(Csla.Rules.RuleContext context) { var target = (ProductEdit)context.Target; var name = (string)ReadProperty(target, ProductEdit.NameProperty); LoadProperty(target, ProductEdit.NameProperty, name.ToUpper()); } Notice how the ReadProperty method is used to get the property value from the target object, and then the LoadProperty method is used to set the property with a new value. If you are implementing a rule that is tightly coupled to a specific business class, this is a good technique to efficiently get and set properties from that object. You should make sure that no code on any background thread ever tries to interact with the Target value or its properties. Using CSLA 4: Creating Business Objects Page 99 Rev 1.1 Remember that direct interaction with the Target parameter from the RuleContext parameter can only occur on the main UI thread. Accessing the Target value from a background thread will result in cross-thread exeptions or other complex multi- threading bugs. For most rules you should use the InputProperties and AddOutValue technique to avoid tight coupling and to provide consistent coding for both synchronous and asynchronous rules. Rule State Your rule might require that extra parameter values be provided to the rule as it is created. For example, here’s a rule that adds some value to a numeric property: public class AddValue : Csla.Rules.BusinessRule { public int ValueToAdd { get; private set; } public AddValue(Csla.Core.IPropertyInfo primaryProperty, int valueToAdd) : base(primaryProperty) { InputProperties = new List { PrimaryProperty }; ValueToAdd = valueToAdd; RuleUri.AddQueryParameter("ValueToAdd", valueToAdd.ToString()); } protected override void Execute(Csla.Rules.RuleContext context) { var value = (int)context.InputPropertyValues[PrimaryProperty]; context.AddOutValue(PrimaryProperty, value + ValueToAdd); } } In this rule, I have a ValueToAdd property that is set through the constructor, and that property is used by the rule as it executes. It is critical that you understand that instance properties or fields in a rule object can not be changed after the rule object has been created. You must initialize instance properties and fields as the object is created, and never change them from that point forward. This restriction exists because each rule object is shared by all instances of the business type with which the rule is associated. If you did change a property or field in a rule, you’d instantly affect all business objects using that rule, and the result would almost certainly be inconsistent behavior that would be difficult to identify, debug and fix. Earlier in this chapter I discussed the RuleName property and how it must be unique for each rule. If your rule requires extra parameter values, you must ensure that those values are included in the rule:// URI that defines the RuleName property, and you do this by adding your parameter values to the RuleUri property of the BusinessRule base class. Keep in mind that you could associate more than one instance of the AddValue rule to any property, so to ensure that the rule name is unique, you must include this parameter value as part of the RuleName, which means it must be part of the RuleUri value. The constructor adds this value to the RuleUri: Using CSLA 4: Creating Business Objects Page 100 Rev 1.1 RuleUri.AddQueryParameter("ValueToAdd", valueToAdd.ToString()); If you forget to add your parameters to the RuleUri, you run the risk of having more than one rule with the same name associated with a property in your business object. That will lead to inconsistent behaviors as rules are executed. Such inconsistencies will typically only be discovered if you implement comprehensive unit testing for your properties and their rules. It is technically still possible to have two rules with the same name associated with a property if both rules have exactly the same parameter values. This is considered to be an extreme edge case, and if you encounter such a scenario you will need to devise your own way to identify each rule separately. In summary, the two things you must remember when implementing instance level properties or fields in a rule is that the property or field value must be added to the RuleUri, and that the property or field value must not be changed once the object has been initialized. Now I’ll switch gears and talk about a couple more advanced features of rules: rule chaining and asynchronous rules. Rule Chaining Rule chaining enables one rule to invoke another rule. There are various reasons why you might use rule chaining, including:  Implementing a gateway rule  Implementing a selector rule  Devising your own model for rule invocation Of these, the simplest and most common is the idea of a gateway rule. A gateway rule is a rule that checks a condition to decide whether it should invoke the “real rule” or inner rule. I’ll use the gateway rule as an example of rule chaining because this is the most common scenario. For example, your business object might have a property that is required for existing objects, but isn’t required for a new object. The rule is a simple required rule such as Csla.Rules.CommonRules.Required, but it should only be executed if the business object’s IsNew property is false. You can use a gateway rule to check IsNew, and to invoke the Required rule only when appropriate: public class IsNotNew : Csla.Rules.BusinessRule { public Csla.Rules.IBusinessRule InnerRule { get; private set; } public IsNotNew(Csla.Core.IPropertyInfo primaryProperty, Csla.Rules.IBusinessRule innerRule) : base(primaryProperty) { InputProperties = new List { PrimaryProperty }; InnerRule = innerRule; RuleUri.AddQueryParameter("rule", System.Uri.EscapeUriString(InnerRule.RuleName)); } protected override void Execute(Csla.Rules.RuleContext context) { var target = (Csla.Core.ITrackStatus)context.Target; Using CSLA 4: Creating Business Objects Page 101 Rev 1.1 if (!target.IsNew) { var chainedContext = context.GetChainedContext(InnerRule); InnerRule.Execute(chainedContext); } } } It is important to understand that the gateway rule must ensure that its InputProperties list includes all property values that will be required by the rule it invokes. The chained RuleContext generated by the GetChainedContext method includes the property values specified by the outer rule’s InputProperties list. One way to automate this process is by replacing the code in the constructor with this variation: public IsNotNew(Csla.Core.IPropertyInfo primaryProperty, Csla.Rules.IBusinessRule innerRule) : base(primaryProperty) { InputProperties = new List { PrimaryProperty }; InnerRule = innerRule; RuleUri.AddQueryParameter("rule", System.Uri.EscapeUriString(InnerRule.RuleName)); // merge InnerRule input property list into this rule's list if (InnerRule.InputProperties != null) InputProperties.AddRange(InnerRule.InputProperties); // remove any duplicates InputProperties = new List(InputProperties.Distinct()); } Notice how the InputProperties list (if any) from the InnerRule object is merged into the InputProperties list of the IsNotNew gateway rule. The business class will associate this IsNotNew rule with a property in its AddBusinessRules override like this: protected override void AddBusinessRules() { base.AddBusinessRules(); BusinessRules.AddRule( new IsNotNew(CityProperty, new Csla.Rules.CommonRules.Required(CityProperty))); } In this example, the IsNotNew rule is associated with the CityProperty, and the inner rule is also provided. The inner rule is the Required rule from Csla.Rules.CommonRules. Based on the implementation of the Execute method in the IsNotNew rule, you can see that the Required rule will only execute if the business object’s IsNew property is false. You can use this same concept to build outer rules that invoke one or more inner rules. The important thing is to ensure that you create a unqiue chained context for each inner rule you invoke, and to ensure that the outer rule’s InputProperties list contains any properties required by the inner rules. This capability for one rule to invoke other rules can be used to create gateway rules as I’ve shown here, but also to create rules that select which other rules to run, thus allowing you to create your own rule execution model when necessary. Asynchronous Rules Business and validation rules can be synchronous or asynchronous. Generally speaking, all rules follow the same code structure, but there are some important differences between synchronous and asynchronous rules. Using CSLA 4: Creating Business Objects Page 102 Rev 1.1 Asynchronous, parallel, or multi-threaded coding is complex. You should use caution, and make sure you completely understand all the consequences before creating an asynchronous rule. Asynchronous rules aren’t automatically asynchronous. CSLA .NET doesn’t run the rule on a background thread for you. Instead, an asyncrhronous rule is a rule that is allowed to execute asynchronous, parallel, or multi-threaded operations within the rule implementation. It is your code in the rule implementation that will call an asynchronous service, or will run some code on a background thread (usually using the BackgroundWorker component). And it is your code that is responsible for ensuring that the completion notification runs on the original calling thread (typically the UI thread). In other words, “asynchronous rules” are potentially asynchronous, and it is up to you whether the rule is asynchronous or not. It is important to note that CSLA .NET treats asynchronous rules differently from synchronous rules, because the framework assumes the rule will be asynchronous. You indicate that a rule is asynchronous by setting the IsAsync property to true in the rule’s constructor: public BusinessRuleClass(IPropertyInfo primaryProperty) : base(primaryProperty) { IsAsync = true; } If you set IsAsync to true as shown here, you are allowed to invoke asynchronous operations in your Execute method implementation. One of the most common scenarios is to invoke an async factory method on a business class. Here’s a rule that checks to see if a product number already exists in the database: public class ProjectExists : Csla.Rules.BusinessRule { public ProjectExists(Csla.Core.IPropertyInfo primaryProperty) : base(primaryProperty) { IsAsync = true; InputProperties = new System.Collections.Generic.List { PrimaryProperty }; } protected override void Execute(Csla.Rules.RuleContext context) { var id = (Guid)context.InputPropertyValues[PrimaryProperty]; Project.Exists(id, (result) => { if (result) context.AddWarningResult("Project already exists"); context.Complete(); }); } } The Project.Exists method is an asynchronous factory method that invokes the data portal to make an asynchronous call to the application server to see if the project id already exists in the database. Notice how the callback handler is a lambda expression that checks the result value and adds a warning result if the value is true. Using CSLA 4: Creating Business Objects Page 103 Rev 1.1 Perhaps most importantly, notice the call to the Complete method of the RuleContext parameter. You must call Complete on the RuleContext when the asynchronous operation is complete. If you don’t call Complete, CSLA .NET will never know that your rule has completed. The Complete method must be called in cases of success or failure. When the asynchronous operation is complete, call the Complete method. I’ve already discussed the difference in execution times between synchronous and asynchronous rules. Synchronous rules are invoked according to priority order and are subject to short-circuiting. Asynchronous rules are started after all synchronous rules for a property have been executed, and they run in an indeterminate order. If you have multiple asynchronous rules, they will run in parallel and will complete in an indeterminate order based on how long each rule takes to complete. Synchronous rules have access to the business object instance, and can directly interact with the object and its properties. By default asynchronous rules do not have access to the business object instance, and must rely on CSLA .NET to provide the rule with copies of any property values required by the rule. Similarly, asynchronous rules can provide a dictionary of property values to be changed, and CSLA .NET changes those property values after the rule completes. An asynchronous rule with a list of AffectedProperties will cause rules for those properties to be executed when Complete is called on the RuleContext, and after any output values have been updated on the target object. The reason for these restrictions is that CSLA .NET is not threadsafe, which means you can’t have multiple threads interacting with an object at the same time. Some or all code in an asynchronous rule may be running on a background thread, so it is important that such code not interact directly with the business object or its properties. Using BackgroundWorker The most common reason to create an asynchronous rule is because the rule needs to call an asynchronous factory method or the asynchronous data portal. Another common scenario is where a rule needs to implement some algorithm that will take a substantial amount of time (seconds or minutes) to complete. In this case, you’ll explicitly execute the long-running code on a background thread. The .NET 4 framework includes some nice features for implementing tasks on background threads in the System.Threading.Tasks namespace. Unfortunately these features are not available in Silverlight or WP7, so if you are writing code for all current platforms you’ll need to avoid using the Task features of .NET 4. I recommend using the BackgroundWorker component for several reasons:  It provides easy access to background threads in the .NET thread pool  It is available on .NET, Silverlight, and WP7  It automatically puts completion callbacks onto the UI thread Using CSLA 4: Creating Business Objects Page 104 Rev 1.1 The Csla.Threading namespace includes an enhanced BackgroundWorker component that handles some threading issues specific to CSLA .NET and the data portal. You can use the BackgroundWorker from System.ComponentModel or from Csla.Threading as you choose, but if you need your background thread to have access to the current user identity or other context data from Csla.ApplicationContext you should consider using the one from Csla.Threading. Either way the rule code follows the same structure: public class LongRunningRule : Csla.Rules.BusinessRule { public LongRunningRule() { IsAsync = true; } protected override void Execute(Csla.Rules.RuleContext context) { var bw = new Csla.Threading.BackgroundWorker(); bw.DoWork += (o, e) => { // implement long-running task here // on background thread e.Result = "some result value"; }; bw.RunWorkerCompleted += (o, e) => { var result = e.Result; // process result here on UI thread context.Complete(); }; bw.RunWorkerAsync(); } } As with any rule, you can use the InputProperties list and InputPropertyValues dictionary to safely provide copies of business object property values to the Execute method and code running on the background thread. The code in the rule should use the AddOutValue method to return any output values, or the various validation methods on the RuleContext object to return validation results. Those methods can be called on the background or UI thread. If you use AddOutValue to return an output value for any property other than the primary property, you must remember to add those properties to the AffectedProperties list in the rule’s constructor. The Complete method of RuleContext must be called on the UI thread, which means it is called in the RunWorkerCompleted event handler. Creating a Dynamic Rule It is also possible to create a rule that dynamically determines, at runtime, whether to be synchronous or asynchronous as the rule is constructed. In the rule’s constructor you can apply logic to decide whether to set the IsAsync property to true or false. You can then use that value in your Execute method to execute the right code. Here’s the LongRunningRule example as a dynamic rule: public class LongRunningRule : Csla.Rules.BusinessRule { public LongRunningRule() { Using CSLA 4: Creating Business Objects Page 105 Rev 1.1 // decide whether to be async or not IsAsync = true; } protected override void Execute(Csla.Rules.RuleContext context) { if (this.IsAsync) { var bw = new Csla.Threading.BackgroundWorker(); bw.DoWork += (o, e) => { RuleImplementation(e); }; bw.RunWorkerCompleted += (o, e) => { ProcessResult(e.Result); context.Complete(); }; bw.RunWorkerAsync(); } else { var e = new System.ComponentModel.DoWorkEventArgs(null); RuleImplementation(e); ProcessResult(e.Result); } } private void RuleImplementation(System.ComponentModel.DoWorkEventArgs e) { // implement rule here e.Result = "some result value"; } private void ProcessResult(object result) { // process result here on UI thread } } I’ve pulled the rule implementation and the processing of results out into separate private methods, and then those methods are invoked through a BackgroundWorker or directly, depending on the value of the IsAsync property. This all depends on the constructor containing some code (not shown) that determines whether this particular instance of the rule should be synchronous or asynchronous. Asynchronous rules complicate ASP.NET code (Web Forms, MVC, or services). You might use this technique to implement rules that are asynchronous on a smart client, but synchronous when running on the server. In that case, the constructor would look like this: public LongRunningRule() { #if SILVERLIGHT IsAsync = true; #else if (HttpContext.Current != null) IsAsync = false; else IsAsync = true; #endif } Using CSLA 4: Creating Business Objects Page 106 Rev 1.1 If HttpContext.Current contains a value, the code is running in ASP.NET. With this implementation when the code is running on a server the rule will run synchronously, otherwise it will run asynchronously. Synchronous Execute in an Asynchronous Rule Another way to approach the scenario where a rule should be synchronous or asynchronous depending on context; is to build an asynchronous rule, and then decide whether to run synchronously or asynchronously in the Execute method. The one drawback to this approach is that CSLA .NET will always treat the rule as async, because the IsAsync property will always be true. The Execute method itself can run synchronously if that makes more sense, and that can be more efficient if you know at runtime that the “background task” will take a very short amount of time to run. Here’s the same example rule, but with the decision to be synchronous or asynchronous being made in the Execute method instead of the rule’s constructor: public class LongRunningRule : Csla.Rules.BusinessRule { public LongRunningRule() { IsAsync = true; } protected override void Execute(Csla.Rules.RuleContext context) { // decide whether to be async or not bool runAsync = ???; if (runAsync) { var bw = new Csla.Threading.BackgroundWorker(); bw.DoWork += (o, e) => { RuleImplementation(e); }; bw.RunWorkerCompleted += (o, e) => { ProcessResult(e.Result); context.Complete(); }; bw.RunWorkerAsync(); } else { var e = new System.ComponentModel.DoWorkEventArgs(null); RuleImplementation(e); ProcessResult(e.Result); context.Complete(); } } private void RuleImplementation(System.ComponentModel.DoWorkEventArgs e) { // implement rule here e.Result = "some result value"; } private void ProcessResult(object result) { // process result here on UI thread } } Using CSLA 4: Creating Business Objects Page 107 Rev 1.1 There are two areas of difference from the previous implementation. First, notice that the constructor always sets IsAsync to true, and that the first thing the Execute method does is determine whether the rule should run asynchronously or not. How you make that decision is up to your specific business requirements and the type of algorithm you are implementing, so I’ve left a “???” placeholder in the example code. Second, notice that the synchronous implementation now calls the Complete method on the RuleContext object. This is necessary because CSLA .NET will always treat this rule as being asynchronous because IsAsync is always true. All asynchronous rules (even if they don’t run anything on a background thread) must call Complete when they are done, or CSLA .NET won’t know that the rule has completed. The conclusion you should draw from this is that an “asynchronous rule” is only potentially asynchronous. If IsAsync is true, CSLA .NET will treat the rule as an asynchronous rule, but you can decide whether to execute code on a background thread or not as you choose. Remember that if IsAsync is true, you must call the Complete method on the RuleContext object to tell CSLA .NET when the rule is done running. Directly Accessing the Target Property It is possible for an asynchronous rule to explicitly indicate that it needs access to the business object instance by setting the rule’s ProvideTargetWhenAsync property to true in the rule’s constructor. public BusinessRuleClass(IPropertyInfo primaryProperty) : base(primaryProperty) { ProvideTargetWhenAsync = true; IsAsync = true; } When you set this property your rule implementation will be provided with a reference to the business object. It is up to you to ensure that no code running on a background thread interacts with the object or its properties. If you violate this restriction you may introduce bugs that are very difficult to find, troubleshoot, or resolve. Setting ProvideTargetWhenAsync to true is dangerous, and should only be done if you understand the full ramifications of your decision. Here’s the ProjectExists rule using the Target value: public class ProjectExists : Csla.Rules.BusinessRule { public ProjectExists(Csla.Core.IPropertyInfo primaryProperty) : base(primaryProperty) { IsAsync = true; ProvideTargetWhenAsync = true; } protected override void Execute(Csla.Rules.RuleContext context) { var target = (Project)context.Target; var id = (Guid)ReadProperty(target, Project.IdProperty); Project.Exists(id, (result) => Using CSLA 4: Creating Business Objects Page 108 Rev 1.1 { if (result) context.AddWarningResult("Project already exists"); context.Complete(); }); } } The reason this is acceptable, is that I’m only using the Target value on the UI thread. I don’t use the Target value in the callback lambda code, which might be running on a background thread (though when using the data portal even that code does run on the UI thread). You should implement most rules as synchronous rules, because they are generally simpler and more efficient. When you need to make asynchronous calls to services or through the data portal, or if you have a rule that implements a long-running algorithm that can run on a background thread you will implement asynchronous rules. Implementing Business Rules Business rules are rules that encapsulate algorithmic processing, calculations, or simple changes to property values. If you need a rule that changes the value of one or more properties of your object then you are looking for a business rule. I’ll walk through a simple example of a rule first, and then go through the various implementation options in detail. Business rules are implemented as a class, and they typically subclass the BusinessRule class from the Csla.Rules namespace. For example: public class ToUpper : Csla.Rules.BusinessRule { public ToUpper(Csla.Core.IPropertyInfo primaryProperty) : base(primaryProperty) { InputProperties = new List { PrimaryProperty }; } protected override void Execute(Csla.Rules.RuleContext context) { var input = context.InputPropertyValues[PrimaryProperty].ToString(); if (!string.IsNullOrEmpty(input)) context.AddOutValue(PrimaryProperty, input.ToUpper()); } } This simple rule will uppercase any string property value. The rule is a subclass of the BusinessRule class. The BusinessRule class implements the IBusinessRule interface from the Csla.Rules namespace, and all rules must implement this interface. The BusinessRule class provides some convenient behaviors common to most rules, and so is usually simpler than implementing the IBusinessRule interface manually. The rule also has a constructor which requires that a primaryProperty parameter be provided. Some rules can be associated with business types, but most will require a primary property. This constructor ensures that this rule is associated with a specific property. Notice that the primaryProperty parameter is of type IPropertyInfo from the Csla.Core namespace. This interface is the base type for all property info metadata fields, and is the correct type to use any time you need a property info metadata field. Using CSLA 4: Creating Business Objects Page 109 Rev 1.1 Within the constructor, the code uses the InputProperties property from the BusinessRule base class. This is a protected property that contains a list of the property values that should be copied out of the business object and made available to the rule when it is executed. The values of any properties you put into this list will be available to the rule every time it runs. The core of the rule is the Execute method. The BusinessRule base class already defines this method, so the ToUpper rule overrides the method to implement the specific rule behavior. protected override void Execute(Csla.Rules.RuleContext context) { var input = context.InputPropertyValues[PrimaryProperty].ToString(); if (!string.IsNullOrEmpty(input)) context.AddOutValue(PrimaryProperty, input.ToUpper()); } For this simple rule, the Execute method gets the input value from the InputPropertyValues dictionary in the context parameter. This value is available because the rule’s constructor added the primary property to the InputProperties list as the rule was created. If the input value contains a string value, the AddOutValue method is used to provide an output value for the primary property. The property value will be set to this output value after the rule completes. Notice that the output value is the uppercased input value. The result is a rule that takes a string property value as input and sets that property to the same string, but uppercased. Using the Target Property A variation on the implementation of the ToUpper rule is to use the Target property of the RuleContext to directly interact with the business object. This will tightly couple the rule to the business type, but may be appropriate in scenarios where a rule is being implementing for only one specific type of business object. The rule would look like this: public class ToUpper : Csla.Rules.BusinessRule { protected override void Execute(Csla.Rules.RuleContext context) { var target = (ProductEdit)context.Target; var name = (string)ReadProperty(target, ProductEdit.NameProperty); LoadProperty(target, ProductEdit.NameProperty, name.ToUpper()); } } Notice how the Target property is cast to a specific business type, and then the ReadPropery method from the BusinessRule base class is used to read the property value without triggering authorization rules. Similarly, the LoadProperty method is used to update the property value. While this implementation is somewhat more direct than the previous examples, you can easily see how the code in the Execute method is tightly coupled to the ProductEdit business type, so this rule isn’t generally useful for other business types. Using CSLA 4: Creating Business Objects Page 110 Rev 1.1 Interacting with the Entire Object Graph One direct benefit to using the Target property of the context object is that you can interact with the entire business object, not just the property values provided through the InputPropertyValues dictionary of the RuleContext object. If your rule needs to interact with the business object’s parent, children, or other objects in the object graph you will be able to access those other objects through the Target property. Here’s an example of a rule that gets the reference to the business object’s parent object: public class AddParentQuantity : Csla.Rules.BusinessRule { protected override void Execute(Csla.Rules.RuleContext context) { var target = (ProductLocation)context.Target; var parent = (ProductEdit)target.Parent; var quantity = (double)ReadProperty(parent, ProductEdit.QuantityProperty); LoadProperty(parent, ProductEdit.QuantityProperty, quantity + 1); } } Because this code has direct access to the Target property, it can use the business object’s Parent property to access its parent. Given access to the parent object, that object’s properties can be read and loaded with values as shown in the example. You can use the same concept to access child objects or any other objects in the object graph. At this point, you should understand how to create business rules that get and set properties of the business object or other objects in the object graph. Implementing Validation Rules Validation rules are rules that return an error, warning, information, or success result value based on the implementation of the rule. Validation rules don’t change the values of properties of the business object or other objects in the object graph. You can implement validation rules as classes, very much like the business rules I’ve discussed earlier in this chapter. Alternatively, you can implement validation rules as DataAnnotations attributes that subclass the ValidationAttribute base class. Broken Rules Collection Editable objects maintain a list of broken validation rules for the object and the object’s properties. Rules are added and removed from this list based on the results returned as each validation rule is invoked. The list of broken rules is maintained in a BrokenRulesCollection and the BusinessBase base class has a public BrokenRulesCollection property you can use within the object or in your UI code to access the list of broken rules. You can use LINQ queries to easily sort and filter the information. For example, this query returns a list of all broken rules for the City property: var rulesForProperty = from r in this.BrokenRulesCollection where r.Property == CityProperty.Name select r; Using CSLA 4: Creating Business Objects Page 111 Rev 1.1 As I discussed in Chapter 1, the IsSelfValid property is based on this broken rules collection. If the collection is empty, the object has no broken rules and so IsSelfValid returns true. Otherwise it returns false. You can’t directly add or remove items from the broken rules collection. Instead, you implement validation rules and as they execute their results are added and removed from the collection. Validation Rule Classes Standard validation rules are implemented following the same structure and concepts as with business rules. Nearly everything I’ve discussed so far in this chapter applies to validation rules as well as business rules. Again, the only real difference is that validation rules return error, warning, information, or success results instead of changing property values. Here’s a simple validation rule implementation: public class ValidPrefix : Csla.Rules.BusinessRule { public string Prefix { get; private set; } public ValidPrefix(Csla.Core.IPropertyInfo primaryProperty, string prefix) : base(primaryProperty) { InputProperties = new List { PrimaryProperty }; Prefix = prefix; RuleUri.AddQueryParameter("prefix", prefix); } protected override void Execute(Csla.Rules.RuleContext context) { var text = (string)context.InputPropertyValues[PrimaryProperty]; if (!string.IsNullOrEmpty(text) && !text.StartsWith(Prefix)) context.AddErrorResult( string.Format("Value must start with {0}", Prefix)); } } This rule has a constructor that requires a primary property, and a parameter with the prefix required for the property value. As with business rules, notice that the parameter value is added to the RuleUri to help ensure this rule has a unique RuleName value. Unique RuleName values are more important for validation rules than business rules, because this is the name used to identify the rule within the broken rules collection. The Execute method checks to see if the input value starts with the prefix. If it doesn’t, the AddErrorResult method of the RuleContext object is used to add an error result to the output of the rule. Once the rule completes, any output results, such as this error, are added to the broken rules collection. If the rule doesn’t return any result, it is assumed that the rule was successful and any existing broken rules entries for this rule are removed from the broken rules list. Rule Severity Rules can return results with different severity:  Error Using CSLA 4: Creating Business Objects Page 112 Rev 1.1  Warning  Information  Success Error severity indicates that the broken rule should prevent the object from being saved. Broken rules of this severity appear as validation rule violations in all data binding technologies, and seamlessly integrate into any standard .NET or Silverlight validation schemes. For example, Error severity rules are automatically displayed by the Windows Forms ErrorProvider control, the ASP.NET MVC validation behaviors, and the WPF and Silverlight validation mechanisms. This automatic display of validation errors is only true for Error severity rules. Microsoft’s data binding technologies have no concept of validation severity, and so they won’t automatically display information for any other severities. CSLA .NET includes UI helper components for several of the UI technologies to make it easy to display Warning and Information severity rules to the user. For example, the PropertyInfo control for Silverlight, WPF, and WP7 and the PropertyStatus control for Silverlight and WPF make it easy to display rules of any severity to the user. Warning severity indicates that the broken rule is merely a warning. The business object can still be saved, but the user should be notified that there is some concern about the property value or the state of the object. Information severity indicates that the broken rule is purely an informational message. The business object can be saved and the user should be shown the message. Success severity is special, in that all it does is indicate success with no message for display to the user. Earlier I mentioned that the default is success, so in most cases there’s no need to explicitly indicate success by calling AddSuccessResult. This is one example of our discussion about short- circuiting and rule priorities earlier in this chapter. It is possible for a rule to explicitly indicate that no subsequent rules should execute. A rule can explicitly trigger short-circuiting. All the RuleContext methods that add validation rules for error, warning, information, and success have overloads that allow you to specify a Boolean value indicating that rule processing should be short-circuited. In the case that a rule is silently successful, but still needs to cause short-circuiting, your rule will call AddSuccessResult with a parameter of true to prevent any subsequent rules from executing. DataAnnotations Attributes While most validation rules will be implemented as subclasses of the BusinessRule base class, you can also implement validation rules as validation attributes by subclassing the ValidationAttribute base class in the System.ComponentModel.DataAnnotations namespace. Rules implemented this way aren’t as powerful or flexible as CSLA .NET rules, because they can’t specify priorities, trigger short-circuiting, run asynchronously, or return anything but Error severity results. On the other hand, applying validation rules as an attribute on a business class property is very convenient and results in readable code. For simple, synchronous validation rules I’ll often implement the rules as validation attributes. Using CSLA 4: Creating Business Objects Page 113 Rev 1.1 Remember that any validation attributes are treated as normal validation rules by CSLA .NET, so they run along with all other priority 0 rules when CheckRules is called. The primary difference is one of implementation. Here’s an example of a simple rule implemented as a validation attribute: [AttributeUsage(AttributeTargets.Property)] public class ValidPrefix : System.ComponentModel.DataAnnotations.ValidationAttribute { public string Prefix { get; private set; } public ValidPrefix(string prefix) { Prefix = prefix; } protected override System.ComponentModel.DataAnnotations.ValidationResult IsValid( object value, System.ComponentModel.DataAnnotations.ValidationContext validationContext) { var text = (string)value; if (!string.IsNullOrEmpty(text) && !text.StartsWith(Prefix)) return new System.ComponentModel.DataAnnotations.ValidationResult( string.Format("Value must start with {0}", Prefix)); else return null; } } This rule is implemented as a .NET attribute, and it inherits from ValidationAttribute so it can participate in the normal DataAnnotations behaviors as supported by ASP.NET MVC and the Silverlight DataForm control. The rule defines a constructor to require the prefix it will require for the property value. It is applied to a property in a business class like this: public static readonly PropertyInfo MyNameProperty = RegisterProperty(c => c.Name); [ValidPrefix("SOP")] public string Name { get { return GetProperty(MyTextProperty); } set { SetProperty(MyTextProperty, value); } } The ValidationAttribute base class defines a protected method named IsValid that the rule overrides to provide its implementation. This IsValid method takes two parameters, the property value to be validated and a ValidationContext parameter with extra information. For this simple rule the value parameter is checked to see if it starts with the required prefix. If not, a ValidationResult object containing the error message is returned. To indicate success, return a null value. You should be aware that the ValidationContext parameter includes an ObjectInstance property, which is similar to the Target property of the RuleContext parameter in standard CSLA .NET rules. You can use this ObjectInstance property to access other properties of the business object, though doing so will couple your rule to the business type and will reduce the ability to reuse your rule. Accessing Rule Information and Results You may need to get a list of the rules associated with a business type, or the results of any validation rules associated with a business object or its properties. CSLA .NET provides access to this Using CSLA 4: Creating Business Objects Page 114 Rev 1.1 information through its own types and properties, and through data binding interfaces defined by the .NET Framework and Silverlight runtime. Get a List of Rules for a Type You can get a list of the business rules associated with an editable business object and all of its properties by using the GetRuleDescriptions method on the business object’s BusinessRules property. The results of this method can be accessed using the RuleUri type from the Csla.Rules namespace. GetRuleDescriptions Method The GetRuleDescriptions method on the BusinessRules property returns an array of string values. Each value is in URI format, with a rule:// URI prefix. The method is called within an editable business class like this: var rules = BusinessRules.GetRuleDescriptions(); The result is a string array containing rule:// URI values. Each URI contains several fields of information, including:  Rule type name  Primary property name  Dictionary of rule arguments A complete list of elements can be found in Table 22. As I discussed earlier, the rule type name can be extremely long, and may be divided into multiple parts of a “virtual path”. The URI specification has limits on the length of its elements, and CSLA .NET will automatically break the rule’s type name into elements that fit within those limits. The dictionary of rule arguments uses the name of each argument as a key, and provides the value associated with that argument name. It is the responsibility of the rule to add any custom argument values to the rule’s URI description. For example, here’s a rule that requires a max argument value: public class MyRule : Csla.Rules.BusinessRule { private int _max; public MyRule(Csla.Core.IPropertyInfo primaryProperty, int max) : base(primaryProperty) { _max = max; InputProperties = new List { PrimaryProperty }; RuleUri.AddQueryParameter("max", _max.ToString()); } protected override void Execute(Csla.Rules.RuleContext context) { object input = context.InputPropertyValues[PrimaryProperty]; // implement rule here } } Using CSLA 4: Creating Business Objects Page 115 Rev 1.1 Notice the call to the AddQueryParameter method of the RuleUri property provided by the BusinessRule base calss. This method is used in the rule’s constructor to add the custom argument value to the rule description. It is very important that all argument values be added to the rule description. The rule description must be unique, and without the argument values, the rule description may not be unique. The RuleUri type from the Csla.Rules namespace can be used to parse each of the elements of the string array returned as a result of the GetRuleDescriptions method. RuleUri Type The RuleUri type is similar to the Uri type provided by the .NET Framework. It accepts a rule:// URI, and parses that value so you can easily access the various parts of the URI. For example, the following code gets the list of rules for an object, and filters the values to get the list of rules for the Name property: var rules = BusinessRules.GetRuleDescriptions(); var ruleUriList = rules.Select(r => new RuleUri(r)); var nameRules = ruleUriList.Where(r => r.PropertyName == NameProperty.Name); This code uses a LINQ expression to convert the string array into a list of RuleUri objects. It then uses another LINQ expression to filter the list of RuleUri objects by the PropertyName property. Get Rule Results from an Object The results of validation rules are automatically exposed to all .NET UI technologies through standard data binding interfaces defined by the .NET Framework or Silverlight runtime. In many cases you can simply rely on the standard data binding behaviors to allow your application to access the results of an object’s validation rules. I’ll discuss the data binding interfaces that apply to WPF, Silverlight, ASP.NET MVC, and WP7 in subsequent books in the Using CSLA 4 ebook series. The interfaces include:  IDataErrorInfo  INotifyDataErrorInfo It is also possible to manually access the validation rule results by accessing the list of “broken rules” associated with an editable business object. As validation rules are executed, their results are maintained in a list of broken validation rules. Every editable business object maintains a list of the broken rules associated with the object and its properties. BrokenRulesCollection Property The BusinessBase base class in the Csla namespace exposes a public property named BrokenRulesCollection. This property provides access to a read-only snapshot of the complete broken rules collection for the business object. Using CSLA 4: Creating Business Objects Page 116 Rev 1.1 The result is a BrokenRulesCollection object, which is a collection of BrokenRule objects. The BrokenRulesCollection object is a subclass of the ObservableCollection type defined by the .NET Framework. This means that the collection is bindable in all UI technologies with the exception of Windows Forms. If you want to display the list of broken rules to the user, you may be able to bind this collection directly to your UI. You can also use LINQ queries to interact with the collection. For example, this code filters the list of broken rules by property name: var nameRules = customer.BrokenRulesCollection.Where(r => r.Property == CustomerEdit.NameProperty.Name); This code assumes the customer field references a CustomerEdit business object. It accesses the BrokenRulesCollection property and applies a LINQ Where clause to filter the results to get the broken rules for the Name property. The BrokenRule type exposes the properties listed in Table 23. Property Description Property Name of the property to which the rule is attached; may be null for a per-object rule RuleName Name of the rule as a rule:// URI Description Friendly description of the rule, for display to the user Severity RuleSeverity value of the rule (Error, Warning, or Information) Table 23. Properties available on a BrokenRule object You can use the RuleUri type from the Csla.Rules namespace to parse the RuleName property. GetBrokenRules Method The BusinessRules object for a business object exposes a GetBrokenRules method. This method returns the same BrokenRulesCollection object as the BrokenRulesCollection property of the BusinessBase class. In fact, the implementation of the BrokenRulesCollection property in the BusinessBase class uses the GetBrokenRules method of the BusinessRules object to get its result. GetAllBrokenRules Method The BusinessRules type also exposes a GetAllBrokenRules method. This is a static method that accepts a business object as a parameter. The purpose of this method is to combine the broken rules collections for all business objects in an entire object graph into a single result. For example, you might have an object graph with an OrderEdit editable root. Assuming that OrderEdit object contains a collection of LineItemEdit objects, you might want to get a consolidated list of all broken rules for the order and its line items. This can be done using the GetAllBrokenRules method: var allRules = BusinessRules.GetAllBrokenRules(order); Using CSLA 4: Creating Business Objects Page 117 Rev 1.1 The GetAllBrokenRules method returns a BrokenRulesTree object. This is a list of BrokenRulesNode objects. Table 24 lists the properties implemented by the BrokenRulesNode type. Property Description Object Reference to the business object associated with this node BrokenRules Reference to the BrokenRulesCollection for the business object associated with this node Parent Reference to the parent BrokenRulesNode object; null if the current object is the root, otherwise the parent node corresponds to the parent business object of the current business object Node Node key value (for internal use) Table 24. Properties available on a BrokenRulesNode object Each business object in the object graph gets one entry in the BrokenRulesTree list, so each BrokenRulesNode object corresponds to one business object in the object graph. You can use LINQ queries to interact with the results. For example, the following code returns a list of business objects that have Error severity broken rules: var allRules = BusinessRules.GetAllBrokenRules(order); var errors = from r in allRules where r.BrokenRules.Any(x => x.Severity == RuleSeverity.Error) select r.Object; The GetAllBrokenRules method is typically used to get a list of all broken rules for an object graph for the purpose of populating some display in the UI to show the user a consolidated view of all broken rules associated with their current task. At this point, you should understand how to create business and validation rules, how to associate them with properties or business types, and how they are executed automatically or manually as your business objects are used by the application. You’ll see more examples of these concepts in subsequent ebooks in the series. In those ebooks I’ll also demonstrate how validation rules integrate with the various data binding technologies and CSLA .NET helper controls for each type of UI. Authorization Rules Authorization rules are slightly different from business and validation rules, though they are as similar as possible. As I discuss how to use and implement authorization rules I will assume that you already understand how business and validation rules are used and implemented. Authorization is the process of determining whether the current user is allowed to perform various actions. CSLA .NET allows you to apply authorization rules to the following actions:  Read a property  Set a property  Execute a method  Create a business object Using CSLA 4: Creating Business Objects Page 118 Rev 1.1  Retrieve a business object  Edit (save) a business object  Delete a business object Some of these rules apply to members of a specific business object instance, and so are per- property or per-method rules, while other rules apply to the type of object rather than any specific instance. For example, the rules that control whether the current user can create or retrieve a CustomerEdit object must apply to the business type because the rules are applied before any object instance exists. On the other hand, a rule controlling whether the current user can read a property is applied as the user tries to read a property from a specific object instance and so that rule runs within the context of that object instance. The per-property and per-method actions are:  Read a property  Set a property  Execute a method The per-type actions are:  Create a business object  Retrieve a business object  Edit (save) a business object  Delete a business object The most important thing to remember is that authorization rules are business logic, not security. Authorization rules control whether the current user is allowed to perform certain actions based on the business rules surrounding those actions. Sometimes this decision is as simple as determining whether the user is or isn’t in a certain group or role. Other times the decision must take into account the state of the business object, the state of the application as a whole, or other information from the user’s profile or claims. CSLA .NET provides two pre-built rules that work with the standard .NET role-based authorization model. These rules are IsInRole and IsNotInRole and are found in the Csla.Rules.CommonRules namespace. You may find these rules to be sufficient for your authorization requirements, or you may need to implement your own authorization rules to meet your application requriements. For example, your application might not use roles at all, but instead might use other information from the user’s profile to determine what actions the user can and can’t perform. I’ll discuss authentication and loading user identity information in the Using CSLA 4: Security ebook, but in most cases you can assume that your authorization rules will have access to various user profile or claims information as necessary. Using CSLA 4: Creating Business Objects Page 119 Rev 1.1 One authorization rule can be associated with any action. So you can associate exactly one authorization rule with the read the City property action, and one rule with the write the City property action. The restriction to one rule per action is to help ensure that authorization doesn’t become a performance issue. Due to the way data binding works in most smart client UI technologies, per- property authorization rules are run frequently, and I chose to restrict the implemention to one rule per action to minimize any performance impact. Authorization rules are permissive by default. This means that if you don’t specify a rule for an action, all users are authorized to perform that action. For example, if you don’t explicitly indicate which users can and can’t read a property, then all users can read that property. I’ll start by showing how rules are associated with properties, methods, and business types, then I’ll discuss how authorization rules are executed, and finally I’ll walk through the process of creating your own custom authorization rules. Associating Rules with Properties and Types Per-property and per-method authorization rules are associated with your business type’s properties and methods by overriding AddBusinessRules, just like business and validation rules. Per-type authorization rules are associated with the business type by implementing a static method named AddObjectAuthorizationRules in your business class. Using AddBusinessRules Per-property and per-method authorization rules are associated with the properties and methods of your business type by overriding the AddBusinessRules method and calling the AddRule method of the BusinessRules object. This process is essentially the same as associating business and validation rules with your properties. To associate rules to properties and methods you’ll write code like this in your business class: protected override void AddBusinessRules() { base.AddBusinessRules(); BusinessRules.AddRule(new Csla.Rules.CommonRules.IsInRole( Csla.Rules.AuthorizationActions.ReadProperty, CityProperty, "User", "Supervisor")); BusinessRules.AddRule(new Csla.Rules.CommonRules.IsInRole( Csla.Rules.AuthorizationActions.WriteProperty, CityProperty, "Supervisor")); BusinessRules.AddRule(new Csla.Rules.CommonRules.IsInRole( Csla.Rules.AuthorizationActions.ExecuteMethod, TestMethod, "Supervisor")); } In this example, I’m using the IsInRole rule to indicate that the User and Supervisor roles are allowed to read the City property, but only the Supervisor role is allowed to write to that property. Similarly, only the Supervisor role is allowed to execute the Test method. Your AddBusinessRules override will typically contain a mix of business, validation, and authorization rule associations. I normally try to either organize the AddRule calls by property or by rule type, you should determine an organizational approach that works for you and standardize on that coding pattern. Using CSLA 4: Creating Business Objects Page 120 Rev 1.1 Using AddObjectAuthorizationRules Per-type authorization rules are often executed before any instance of the business type has been created. For example, the very first time a user attempts to create a CustomerEdit object, the rule controlling whether they are allowed to create that object is run – before the object is created. This means the per-type authorization rules for a business type must be defined before the create or retrieve actions can be authorized. The AddBusinessRules method executes when the first instance of a business type has been created. Unfortunatly, that method is run too late to define per-type rules. The solution is that CSLA .NET looks for a static method named AddObjectAuthorizationRules in your business class and this method is invoked before the business type is used. In pure .NET code, this method can be private in scope, but due to the restrictions on reflection in Silverlight and WP7, the method must be public in those environments. I generally make the method public in all cases for consistency. You should never call this method explicitly. CSLA .NET will call this method automatically to initialize your authorization rules one time during the life of each AppDomain. To associate rules to your business type you’ll write code like this in your business class: public static void AddObjectAuthorizationRules() { Csla.Rules.BusinessRules.AddRule(typeof(ProductEdit), new Csla.Rules.CommonRules.IsInRole( Csla.Rules.AuthorizationActions.CreateObject, "Supervisor")); Csla.Rules.BusinessRules.AddRule(typeof(ProductEdit), new Csla.Rules.CommonRules.IsInRole( Csla.Rules.AuthorizationActions.GetObject, "User", "Supervisor")); Csla.Rules.BusinessRules.AddRule(typeof(ProductEdit), new Csla.Rules.CommonRules.IsInRole( Csla.Rules.AuthorizationActions.EditObject, "Supervisor")); Csla.Rules.BusinessRules.AddRule(typeof(ProductEdit), new Csla.Rules.CommonRules.IsInRole( Csla.Rules.AuthorizationActions.DeleteObject, "Supervisor")); } I’m using the IsInRole rule, and associating it with the create, get, edit, and delete actions for the ProductEdit business type. In this example, the Supervisor role is allowed to perform all four actions, but the User role is only allowed to get (retrieve) objects of type ProductEdit. Because AddObjectAuthorizationRules is a static method, the AddRule method I’m calling is a static method on the BusinessRules type in the Csla.Rules namespace. These per-type authorization rules are enforced by the data portal, so it is important that users avoid the use of the new keyword to create business objects, and instead invoke factory methods (which use the data portal) to create or retrieve instances of business objects. If you do use the new keyword to create instances of your business objects, you shouldn’t be surprised when the authorization rules aren’t applied. Using CSLA 4: Creating Business Objects Page 121 Rev 1.1 Rule Sets I discussed how rule sets are used for business and validation rules. They work the same way for authorization rules. Within your AddBusinessRules method you’ll use code like this: protected override void AddBusinessRules() { base.AddBusinessRules(); // associate rules for default rule set BusinessRules.AddRule(new Csla.Rules.CommonRules.IsInRole( Csla.Rules.AuthorizationActions.ReadProperty, CityProperty, "User", "Supervisor")); // associate rules for rule set A BusinessRules.RuleSet = "RuleSetA"; BusinessRules.AddRule(new Csla.Rules.CommonRules.IsInRole( Csla.Rules.AuthorizationActions.ReadProperty, CityProperty, "NormalUser", "SuperUser")); // use default rule set BusinessRules.RuleSet = "default"; } In this example, the default rule set uses the User and Supervisor roles, while RuleSetA has different roles defined for its users; and so the roles supplied to the rule are different. Similar code is used in the AddObjectAuthorizationRules method: public static void AddObjectAuthorizationRules() { // associate rules for default rule set Csla.Rules.BusinessRules.AddRule(typeof(EditableProperties), new Csla.Rules.CommonRules.IsInRole( Csla.Rules.AuthorizationActions.CreateObject, "Supervisor")); // associate rules for rule set A Csla.ApplicationContext.RuleSet = "RuleSetA"; Csla.Rules.BusinessRules.AddRule(typeof(EditableProperties), new Csla.Rules.CommonRules.IsInRole( Csla.Rules.AuthorizationActions.CreateObject, "SuperUser")); // use default rule set Csla.ApplicationContext.RuleSet = "default"; } Notice that in this case, the current rule set is controlled by the RuleSet property on the Csla.ApplicationContext object. This property controls which rule set is used for per-type rules for the current user application-wide. For per-type rules, you don’t control the rule set for each business object or type; you control it for the entire application by changing this global property value. The ApplicationContext object is user-specific, so even in a multi-user server scenario the RuleSet property only affects the current user. That way, each user can be using a different rule set. Executing Rules Like business and validation rules, authorization rules are normally executed automatically. The GetProperty, GetPropertyConvert, SetProperty, and SetPropertyConvert methods automatically check authorization rules before doing any other work. The data portal automatically checks authorization rules before any create, fetch, save, or delete operations. Using CSLA 4: Creating Business Objects Page 122 Rev 1.1 The one exception is the ExecuteMethod action, which you must explicitly invoke at the top of any method you implement. I discussed this in Chapter 1, and here’s the code example from that chapter: public static readonly MethodInfo TestMethod = RegisterMethod(typeof(EditableProperties), "Test"); public void Test() { CanExecuteMethod(TestMethod); // do some work here } The first line of code in the Test method is a call to CanExecuteMethod, which executes any authorization rule associated with the Test method to find out if the user is authorized to perform this action. Manually Executing Rules It is possible to explicitly check the authorization rules for all actions. In fact, the methods to perform those checks are all publicly available so they can be used by code inside your business class, but also by code in the UI. A good UI developer will show the user visual cues to help indicate that the user isn’t authorized to view or change a property value, or perform actions linked to UI controls like buttons. To do this, the UI developer might disable or hide controls. The authorization rules are in the business objects, and they shouldn’t be replicated in the UI code, but the UI code does need some way to determine the results of those authorization rules. The idea is that the authorization rules associated with the properties, methods, and business types in your business layer should be available to any code in the interface control and business layer. To make this possible, CSLA .NET defines public interfaces and methods that allow any code to execute the authorization rules. Per-Property and Per-Method Rules The per-property and per-method rules can be executed through the IAuthorizeReadWrite interface in the Csla.Security namespace. This interface is implemented by the BusinessBase and ReadOnlyBase base classes, and so is available on all editable and read-only object stereotypes. You can use the IAuthorizeReadWrite interface like this: var iarw = (Csla.Security.IAuthorizeReadWrite)_product; bool canExecute = iarw.CanExecuteMethod("Test"); bool canRead = iarw.CanReadProperty("Name"); bool canWrite = iarw.CanWriteProperty("Name"); Notice that this interface uses string literal values for the names of properties and methods. This is because most UI code is driving off data binding information, and so won’t have access to the static metadata fields describing each property or method. The BusinessBase and ReadOnlyBase base classes also expose public methods you can use directly. These methods have overloads that accept either an IPropertyInfo metadata field or a string literal: ProductEdit obj = _obj; bool canExecute = obj.CanExecuteMethod(ProductEdit.TestMethod); Using CSLA 4: Creating Business Objects Page 123 Rev 1.1 bool canRead = obj.CanReadProperty(ProductEdit.CityProperty); bool canWrite = obj.CanWriteProperty(ProductEdit.CityProperty); You can use the interface or the public methods as appropriate for your application needs. Per-Type Rules The per-type rules can be executed through the BusinessRules class in the Csla.Rules namespace. This class exposes a set of HasPermission methods that are static and public, so any code in your application can check the per-type rules. You can use these static methods like this: bool canCreate = Csla.Rules.BusinessRules.HasPermission( Csla.Rules.AuthorizationActions.CreateObject, typeof(ProductEdit)); bool canGet = Csla.Rules.BusinessRules.HasPermission( Csla.Rules.AuthorizationActions.GetObject, typeof(ProductEdit)); bool canEdit = Csla.Rules.BusinessRules.HasPermission( Csla.Rules.AuthorizationActions.EditObject, typeof(ProductEdit)); bool canDelete = Csla.Rules.BusinessRules.HasPermission( Csla.Rules.AuthorizationActions.DeleteObject, typeof(ProductEdit)); As you can see from the example code, you can use this technique to execute the per-type rules for any of the four per-type actions on a business type. You can also use an overload of these methods to specify the rule set to use when checking the rule: bool canCreate = Csla.Rules.BusinessRules.HasPermission( Csla.Rules.AuthorizationActions.CreateObject, typeof(ProductEdit), "RuleSetA"); Finally, for the edit and delete actions you might have an object instance you can supply to the rule: bool canEdit = Csla.Rules.BusinessRules.HasPermission( Csla.Rules.AuthorizationActions.EditObject, obj); When I discuss how you can implement per-type authorization rules, you’ll see how this object instance is made available to the rule’s code. Obviously, you’ll never have an object instance to provide to a create or get action, but you’ll often have an instance for edit and delete actions. Suppressing Rule Execution Authorization rules are suppressed using the same techniques listed in Table 19, which I discussed earlier in this chapter. To summarize, you can use one of the following techniques:  BypassPropertyChecks property  ICheckRules interface  SuppressRuleChecking property The scenarios where each technique should be used for authorization rules are the same as those for business and validation rules. Please refer to Table 19 for more information. At this point, you should understand how to associate authorization rules with properties, methods, and business types; as well as how these rules are executed, and how you can suppress rule execution. I’ll move on to discuss how to create custom authorization rules. Using CSLA 4: Creating Business Objects Page 124 Rev 1.1 Implementing Authorization Rules The role-based authorization supported by the CSLA .NET IsInRole and IsNotInRole rules is built directly on the support for role-based authorization provided by the .NET framework. Many applications can use this role-based model without the need for custom rules. There are cases where simple role-based rules aren’t sufficient. For example:  The state of the business object must be taken into account  Other information about the user must be taken into account  A claims-based authorization model is being used instead of a role-based model  A permissions-based authorization model is being used instead of a role-based model Remember that authorization rules are business rules, not security rules. So it is quite common to need a custom rule to control whether the current user can read or write a property value based on the state of other properties in the object. For example, you might have a property that can only be changed if the object is new, so your custom rule needs to take the object’s IsNew property into account as part of its behavior. Structure of a Rule Authorization rules follow a consistent structure. They must implement the IAuthorizationRule interface from the Csla.Rules namespace, but they’ll usually subclass the AuthorizationRule class to make the process simpler. Here’s the basic structure of a rule: public class MyAuthorizationRule : Csla.Rules.AuthorizationRule { public string AllowedRole { get; private set; } public MyAuthorizationRule( Csla.Rules.AuthorizationActions action, Csla.Core.IMemberInfo element, string allowedRole) : base(action, element) { AllowedRole = allowedRole; } protected override void Execute(Csla.Rules.AuthorizationContext context) { if (Csla.ApplicationContext.User.IsInRole(AllowedRole)) context.HasPermission = true; else context.HasPermission = false; } } Authorization rules are implemented as a class, and they normally inherit from the AuthorizationRule base class. They also must have a constructor, because every rule must at least be associated with an action from the AuthorizationActions type, and most rules are also associated with a property or method. The IMemberInfo interface from the Csla.Core namespace allows this rule to be associated with either a property (IPropertyInfo) or a method (IMethodInfo). In this example, the rule also requires an allowedRole parameter; which is the one role that is allowed to perform the action. That value is stored in an instance-level property or field of the object. Using CSLA 4: Creating Business Objects Page 125 Rev 1.1 The implementation of the rule itself goes in the Execute method; which is passed an AuthorizationContext parameter containing information about the action, member, and business object for which this rule is running. The AuthorizationContext is also used to return the results of the rule through its HasPermission property. This simple rule checks to see if the current user is in the correct role, and the HasPermission property of the AuthorizationContext parameter is set accordingly. AuthorizationContext Parameter The AuthorizationContext parameter passed to the Execute method of the rule is at the heart of the rule implementation. Table 25 lists the members of the AuthorizationContext type. Member Description Rule Provides a reference to the authorization rule Target Provides a reference to the business object; this value will be null if there is no object instance (such as for create and retrieve actions) TargetType Gets the type of the business object HasPermission Set this to the result of the rule: true indicates the user should be allowed to perform the action, false means the user doesn’t have permission to perform the action Table 25. Members defined by the AuthorizationContext type At the very least, every authorization rule must set the HasPermission property of the AuthorizationContext parameter to the result of the rule. Rule implementations can also make use of the Target and TargetType properties to apply behaviors that are specific to a business object or business type. The TargetType value is always available, but it is important to recognize that the Target property might be null. While per-property and per-method rules always have a business object to populate the Target property, not all per-type actions will have a business object. For certain, you know that the CreateObject and GetObject actions will not have a business object, because these rules run before an object is created to determine whether the user is allowed to create or get the object. Any rule implementation that uses the Target property must check the value to see if it is null before proceeding. Here’s an example where I’m using the Target property in an Execute method: protected override void Execute(Csla.Rules.AuthorizationContext context) { var target = context.Target as ProductEdit; bool isNew = false; if (target != null) isNew = target.IsNew; if (isNew && Csla.ApplicationContext.User.IsInRole(AllowedRole)) context.HasPermission = true; Using CSLA 4: Creating Business Objects Page 126 Rev 1.1 else context.HasPermission = false; } I cast the Target property to my business type, then I check to see if the value is null, and only interact with it if it contains a valid reference. You can combine the information from the AuthorizationContext parameter with the properties available from the AuthorizationRule base class and the current user’s identity from the User property of the Csla.ApplicationContext object to implement powerful and flexible authorization rules. IAuthorizationRule Interface All authorization rules must implement the IAuthorizationRule interface from the Csla.Rules namespace. Typically, rules will subclass the AuthorizationRule base class that already implements this interface, but in some advanced scenarios you may choose to implement the interface directly. The members of the interface are listed in Table 26. Member Description Action Gets the AuthorizationActions value specifying the action this rule is authorizing Element Gets the IMemberInfo metadata field for the property or method with which this rule is associated; this value is null if the rule is associated with a business type CacheResult Gets a Boolean value indicating whether the result of this rule can be cached by the calling code Execute This is the method that is invoked when the rule is executed Table 26. Members defined by the IAuthorizationRule interface The Action and Element properties define the association for each rule. There can be exactly one rule for each action/element pair. For example, there can be one rule for the ReadProperty action of the Name property on a business type. The CacheResult property indicates whether the result of the rule can be cached by the calling code. Remember that most of the time rules are executed automatically by BusinessBase, ReadOnlyBase, or other CSLA .NET base classes. The BusinessBase and ReadOnlyBase classes implement caching of per-property and per- method authorization results as a performance optimization. For role-based rules this is fine, because the cache is automatically discarded any time the identity of the current user is changed. But for some custom rules, especially those that are sensitive to the state of the business object, the result must not be cached. You’ve seen how the Execute method contains the implementation of the rule. The result of the Execute method is provided through the HasPermission property of the AuthorizationRules parameter. Using CSLA 4: Creating Business Objects Page 127 Rev 1.1 AuthorizationRule Class Most of the time, you will create authorization rules by subclassing the AuthorizationRule base class. This class implements the IAuthorizationRule interface and provides some basic behaviors that make it simpler to implement a rule. The AuthorizationRule class maintains the Action and Element values required by the IAuthorizationRule interface, and has constructors that require at least the Action value be provided as the object is created. The Element value is optional, because per-type rules won’t specify this value. The base class also defines a CacheResult property, with a default value of true. You should override the CacheResult property and return a value of false if your rule’s result shouldn’t be cached. It also defines the Execute method as an abstract virtual method, so your rule class must override this method in its implementation. Rule State In my example rule class, I included an instance-level property: public string AllowedRole { get; private set; } The restrictions on instance-level properties and fields in authorization rules are the same as for business and validation rules. Once the object has been initialized, you must never change the values of any instance-level properties or fields. Remember that each authorization rule instance is shared by all instances of the business type to which the rule is associated. If you change the value of any instance-level properties or fields in an authorization rule, you will instantly affect all the business objects to which that rule is associated. This can lead to bugs that are very difficult to locate and fix. Authorization rules are generally much simpler than business or validation rules. They return a Boolean result indicating whether the current user is allowed to perform the specified action, so they don’t have all the options and complexity of rule priorities, severities, or asynchronous behaviors. At this point, you should understand how to associate authorization rules with properties, methods, and business types, as well as how the rules are executed, and how you can create your own custom rules. Using CSLA 4: Creating Business Objects Page 128 Rev 1.1 Conclusion This book has provided you with the information necessary to use the CSLA .NET framework to create the business domain objects that make up a business layer for your application. These domain objects encapsulate behavior, and contain the state necessary to implement that behavior. You have learned how to implement business rules, validation rules, and authorization rules, along with public properties that provide access to select elements of the object’s state. This is the second in a series of ebooks. Subsequent ebooks will explore n-tier deployment and the data portal, data access, security, and how to use a business layer composed of domain objects to create various types of application interface.

下载文档,方便阅读与编辑

文档的实际排版效果,会与网站的显示效果略有不同!!

需要 8 金币 [ 分享文档获得金币 ] 0 人已下载

下载文档

相关文档