Rockford Lhotka
    CTO at Magenic
    Author, speaker, software architect
    Creator of the CSLA .NET framework

Home
Blog
CSLA .NET
Magenic
Speaking
Publications
About me
Contact me

Login

Version 2.1 Change Log

 

This document is the change log for version 2.1 of CSLA .NET.

 

Breaking change summary:

  • The calling semantics for DataPortal.Create<T>() and DataPortal.Fetch<T>() (with no criteria at all) are different. They now invoke DataPortal_Create() and DataPortal_Fetch() (with no criteria parameter) respectively

this affects everyone

  • The Overridable/virtual DataPortal_Create() methods declared in BusinessBase and BusinessListBase have changed their signature

this affects virtually everyone

  • Per-type validation rules requires code changes when moving from 2.0 to 2.1

this affects virtually everyone

  • Per-type authorization rules may require code changes when moving from 2.0 to 2.1

this may affect your code

  • Csla.DataPortalException now includes the original exception message text in its message text to assist in debugging

this is unlikely to break your code

  • The Parent property in BusinessBase is now of type IParent

this is unlikely to break your code

  • The RunLocal attribute is no longer inherited from base class methods when the methods are overridden by a subclass

this is unlikely to break your code

  • The order in which OnDeserialized() is called has changed

this is unlikely to break your code

 

Known issues:

CslaDataSource

It does not work to add a CslaDataSource to a page by choosing to “add a new data source” from within the GridView or DetailsView controls. Attempting to do this will result in an exception that prevents the control from displaying properly. I have been unable to resolve this issue, but there is a viable workaround.

You must manually add a CslaDataSource control to your page, either using drag-and-drop from the Toolbox, or by typing the tag into the page. Then you can configure the assembly/type information in the data source control. Then you can choose this data source control as the data source for your GridView or DetailsView control.

 

Changes and Enhancements:

 

Csla – Validation Rules (per-type rules)

Add support for non-instance validation rules. In 2.0 the validation rules are loaded per-object instance in AddBusinessRules(). While this offers optimal flexibility, it can be expensive in terms of memory and load times because every instance has to load its own rules. The new approach allows you to optionally create a set of rules from the Shared/static constructor in your business class. These rules are then reused across every instance – thus reducing load times and memory overhead.

The result is that the Validation Rules region in a business object can (when opting to use this new feature) look like this:

  Protected Overrides Sub AddBusinessRules()

    ' add a per-type validation rule

    ValidationRules.AddRule( _

      AddressOf Csla.Validation.CommonRules.StringRequired, _

      "Name")

  End Sub

 

  Protected Overrides Sub AddInstanceBusinessRules()

    ' add a per-instance validation rule

    ValidationRules.AddInstanceRule( _

      AddressOf Csla.Validation.CommonRules.StringRequired, _

      "City")

  End Sub

 

Breaking change. The fact that AddBusinessRules() and AddRule() now add per-type rules may break your code. If you use rule methods that are not Shared/static, or if you dynamically have different rules per instance of your class, then you will need to change your AddBusinessRules() to AddInstanceBusinessRules() and your AddRule() calls to AddInstanceRule() in those particular cases.

 

Csla – Validation Rules (strongly typed rule methods)

Add support for strongly typed rule methods through the use of generics. The new ValidationRules.AddRule(Of T,R)() method allows you to add a rule method, specifying the type for the target object and for the RuleArgs parameter. The new RuleHandler(Of T,R) delegate defines the same rule method signature as before, but requires strongly typed target and e parameters.

 

This change does not affect existing rule methods or how they work. It is a purely optional new feature.

 

Csla – Validation Rules (severity)

Add the concept of Severity to business rules. Within a rule method the code can now set e.Severity to indicate whether the broken rule reflects an Error, Warning or Information.

 

Only Error severities cause the object’s IsValid property to return False.

 

Csla – Validation Rules (priority and short-circuiting)

Add the concepts of Priority and short-circuiting to business rule processing.

 

When rules are added, there are new overloads of AddInstanceRule() and AddRule() that allow the rule’s priority to be specified. Rules are processed in order from 0 to x, where x is some larger number. So 0 is the highest (and default) priority, while 100 would be much lower. Rule priorities are per-property, so each property’s rules are run by priority.

 

Short-circuiting is implemented by setting the new ValidationRules.ProcessThroughPriority property. This property defaults to 0, meaning that all rules of priority 0 will be processed, but any lower priority rules are only processed if no previous rule has been broken. In other words, if a priority 0 rule is broken, all priority 0 rules will process, but no priority 1 or lower rules will be processed. Only rules with Error severity cause short-circuiting.

 

Short-circuiting is also available from within a rule method, where the rule method’s code can set e.StopProcessing to True. This causes an immediate stop to processing of rules for the property, regardless of priority or severity (though it does only take effect if the rule returns False, indicating it is broken).

 

Csla – Validation Rules (dependant properties)

Added the concept of dependant properties, so when the rules for one property are checked, the rules for its dependant properties are checked as well. This is done by calling a new method within AddBusinessRules():

 

public override void AddBusinessRules()

{

  // call ValidationRules.AddRules() like normal

  ValidationRules.AddDependantProperty("x", "y");

}

 

In this example, property y is dependant on x. So when CheckRules("x") is called, the rules for y will also be checked. This is true when PropertyHasChanged() is called for property x as well.

 

The result of this change is that there is no longer a need to manually call CheckRules() on dependant properties from within a property’s set block. Instead, the dependency information is centralized into AddBusinessRules().

 

 

Csla – Validation Rules (getting a list of rules)

Added a method to ValidationRules to allow the business object to retrieve a string array containing all ToString() values for all the RuleMethod objects associated with the object. These ToString() methods now always return strings like the following:

 

  rule://method/property

  rule://method/property?maxValue=10

 

A business object may choose to expose these values to the UI use them in other ways. The intended purpose behind this change is to enable documentation of the rules associated with an object at runtime.

 

 

Csla – Authorization Rules (per-type rules)

Add support for non-instance authorization rules. In 2.0 the authorization rules are loaded per-object instance in AddAuthorizationRules(). While this offers optimal flexibility, it can be expensive in terms of memory and load times because every instance has to load its own rules. The new approach allows you to optionally create a set of rules from the Shared/static constructor in your business class. These rules are reused across every instance – thus reducing load times and memory overhead.

  Protected Overrides Sub AddAuthorizationRules()

    ' add a per-type rule

    AuthorizationRules.AllowWrite( _

      "Name", "Supervisor")

  End Sub

 

  Protected Overrides Sub AddInstanceAuthorizationRules()

    ' add a per-instance rule

    AuthorizationRules.AllowInstanceWrite( _

      "City", "Supervisor")

  End Sub

 

Breaking change. The fact that AddAuthorizationRules() and the methods such as AllowRead() now add per-type rules may break your code. If you use authorization rules methods that are dynamically different rules per instance of your class, then you will need to change your AddAuthorizationRules() to AddInstanceAuthorizationRules() and your methods such as AllowWrite() to InstanceAllowWrite() in those particular cases.

 

Csla – Authorization Rules (minimize serialized byte stream)

Reload the authorization roles on deserialization rather than serializing the roles with the business object. In 2.0 AddAuthorizationRules() is called just once as the object is created, and the roles are serialized along with the object. In 2.1 AddAuthorizationRules() is called any time the object is created or deserialized, and the roles are not serialized with the object. This is better, because it minimizes traffic over any network at the cost of a little CPU time (which is typically a much cheaper resource).

 

 

Csla – Authorization Rules (cached results in BusinessBase)

The default implementations of CanReadProperty() and CanWriteProperty() in BusinessBase now cache the results of these method calls. The cache is used on subsequent calls unless the current principal object changes, in which case the cache is flushed and authorization rules are rechecked.

 

The purpose for this change is performance. CanReadProperty(), in particular, is called very often when using data binding, and this change avoids rechecking the authorization rules on every call at the cost of some memory consumption to store and maintain the cache.

 

As always, if you dislike the default implementation in BusinessBase, you can override these two methods and provide your own implementation.

 

 

Csla - SortedBindingList

Implement ICancelAddNew to support better parity with BindingList<T>.

 

 

Csla – FilteredBindingList

Create a new FilteredBindingList class, which is similar to SortedBindingList, but does a filtering operation instead of a sorting operation. Filtering is not part of IBindingList, and so there’s no automatic .NET support or UI gestures for filtering, but this class allows you to apply a filter to a collection/array/list by calling an ApplyFilter() method, which works much like ApplySort() does in SortedBindingList.

 

As with SortedBindingList, FilteredBindingList is a dynamic view into the original list/collection/array. In other words, changes to items in the FilteredBindingList actually occur in the items in the original list, and changes to items in the original list immediately affect the view.

 

FilteredBindingList does act differently from SortedBindingList in some ways. Specifically, FilteredBindingList doesn’t reapply the filter any time the list is changed. Doing so would make the view useless in any in-place editing scenarios in grid controls. For example, adding a new item would be impossible, because as soon as it was added it would be filtered out of the view. Due to this, the filter is only reapplied on a complete reset of the original list, or when ApplyFilter() is explicitly called by your code.

 

 

Csla – EditableRootListBase

Create a new EditableRootListBase class, which provides for a collection of editable root objects (inherit from BusinessBase<T>). This class allows you to create a collection that can be fetched using the data portal, loading itself with root objects (not child objects).

 

This list class automatically handles insert/update/delete operations on the items it contains. Remove() and RemoveAt() cause immediate deletion of the specified object through the data portal (automatic calls to Delete() and Save(), followed by the item being removed from the list as well). If the “child” item is edited through IEditableObject (such as within an in-placed edit process in Windows Forms grid controls), when the “row” edit is complete EditableRootList will automatically call Save() on the object, triggering an insert or update operation.

 

Additionally, you can then call a SaveItem() method on the collection to force individual items to be properly saved through the data portal (such that the resulting new object from the Save() operation replaces the previous one in the list).

 

 

Csla – Core\IParent

Add Csla.Core.IParent interface for use in implementing EditableRootListBase.

 

Potential breaking change. IParent is now used by BusinessBase instead of IEditableCollection, so the Parent property in BusinessBase is now of a different type.

 

 

Csla – Core\IEditableCollection

Deprecated: Csla.Core.IEditableCollection remains in the framework, but is not used anywhere. I am leaving it in the framework to minimize breaking changes, but you should avoid using this interface, as I expect to eliminate it in CSLA .NET 3.0.

 

 

Csla – BusinessListBase

Add a virtual/Overridable method, EditChildComplete(), which is automatically called when a child object’s ApplyEdit() is complete. You can override this method in your collection classes to be notified when a child object has been edited and the user has accepted the edits. This can be useful if you need to recheck business rules across all child objects when one child object has been altered (to do so, you’ll need to add a Friend/internal method on your child class to allow the collection to trigger a CheckRules() call).

 

 

Csla – BusinessListBase

Include a PropertyDescriptor object in the ListChanged event that is raised when a child object’s PropertyChanged event is raised. This matches the default behavior provided by the BindingList(Of T) for these events.

 

 

Csla – BusinessListBase

Change IsDirty so it only considers objects in DeletedList if they are not new (IsNew is false). New objects in DeletedList do not cause the object to be considered as changed.

 

 

Csla – BrokenRulesCollection

Add a ToArray() method that returns a string array of the broken rule descriptions.

 

 

Csla – Csla\Core\ISavable

Add an ISavable interface, which is implemented by BusinessBase<T> and BusinessListBase<T,C>. This interface allows the new EditableRootListBase to handle the saving of objects, and also provides a way by which UI developers can create base form classes or other UI framework classes to manage the saving of business objects in a generalized manner.

 

 

Csla – BusinessBase, BusinessListBase

Raise a Saved event in the Save() method. This event includes a SavedEventArgs parameter, which provides a reference to the new version of the object that is a result of the save operation. The sender parameter is, of course, the original object instance, so a handler of the event has access to both the old and new object references and can take appropriate steps (like rebinding a UI, etc).

 

 

Csla – ApplicationContext.LocalContext

Add a LocalContext property to Csla.ApplicationContext. LocalContext works like ClientContext and GlobalContext, but it does not get moved across the network by the data portal. This means that both the client and server have a LocalContext that is truly local to each location. LocalContext exists at the thread or HttpContext level and so is local to a user’s session on a server.

 

You can envision using LocalContext to globally transfer objects such as database connections or ADO.NET transaction objects throughout an entire data access session on a server. I’m sure other uses may be found as well, but this one is a key motivator for the addition of the functionality.

 

 

Csla – Data portal and BusinessBase and BusinessListBase

Breaking change. The calling semantics for DataPortal.Create() and DataPortal.Fetch(), when called with no criteria parameter, are now different.

 

In version 2.0.2 I allowed both Create() and Fetch() to be called with no criteria parameter. At that time, I routed both these calls to DataPortal_Create(object) and DataPortal_Fetch(object) respectively. This is confusing, since you’d expect that having no parameter on the call would invoke a method that accepts no parameter.

 

So now, DataPortal.Create() invokes DataPortal_Create() and DataPortal.Fetch() invokes DataPortal_Fetch().

 

DataPortal.Create(Nothing/null) invokes DataPortal_Create(object) and DataPortal.Fetch(Nothing/null) invokes DataPortal_Fetch(object).

 

DataPortal.Create(criteria) invokes DataPortal_Create(criteria), falling back to DataPortal_Create(object) if no strongly typed match is found, and DataPortal.Fetch(criteria) invokes DataPortal_Fetch (criteria), falling back to DataPortal_Fetch (object) if no strongly typed match is found.

 

In short, the data portal methods now follow normal .NET calling conventions when invoking the DataPortal_Create/Fetch methods.

 

I also changed the virtual/Overridable DataPortal_Create(object) method declarations in BusinessBase and BusinessListBase to DataPortal_Create(), because that’s the more common default behavior.

 

 

Csla – DataPortal\Client\DataPortal

Breaking change. Include the text of the original exception message in the exception message of any DataPortalException objects thrown from the client-side data portal.

 

Perhaps the most common source of support questions on the forum is due to people getting the generic DataPortalException with text like “DataPortal.Update failed.” Too many people fail to look at the BusinessException property to find the real exception.

 

By returning the original exception message as part of the top-level exception’s message, I hope to help debugging.

 

However, if you were explicitly watching for text like “DataPortal.Fetch failed.” in the exception message then your code will break. You can change your equality test to a StartsWith() check:

ex.Message.StartsWith("DataPortal.Fetch failed.")

to overcome this issue.

 

 

Csla – DataPortal\Client\DataPortal

Breaking change. Change the code so the RunLocal attribute is not automatically inherited from any base class. In other words, a subclass must re-apply RunLocal for it to take effect when overriding a base class method. While this is technically a bug fix, because this was the intended behavior to start with, it could break some existing code that relies on RunLocal being inherited from base class method declarations.

 

 

Csla – Security\IAuthorizeReadWrite

Add an IAuthorizeReadWrite interface, which is implemented by Csla.Core.BusinessBase and Csla.Core.ReadOnlyBase. This interface exposes Boolean methods: CanReadProperty(string) and CanWriteProperty(string).

 

The purpose behind adding this interface is to enable easier creation of UI frameworks that might need to interrogate business objects about authorization rules in a standardized manner. It also helps to simplify some code in the Csla\Windows\ReadWriteAuthorization control.

 

 

Csla – SmartDate

Add a Shared/static SetDefaultFormat() method that you can call to change the global default format for all SmartDate values created after this method has been called. The default provided by CSLA .NET continues to be “d”, which is a culture-sensitive short date format.

 

 

Csla – SmartDate

Add an EmptyValue enumerated value to allow code to more clearly specify whether an empty value is MaxDate or MinDate.

 

 

Csla – SmartDate

Add a new overload for ToString() to allow specification of the format string, thus better matching DateTime.

 

 

Templates – EditableRoot

Fix the Get factory method so it calls DataPortal.Fetch(). Also add the noinlining attributes to the property get/set code.

 

 

Csla – BusinessBase

Change OnUnknownPropertyChanged() so it raises a PropertyChanged event for property "" rather than for all properties on the object. This causes data binding to refresh all properties, without the overhead of raising all those events.

 

 

Csla – BusinessBase, BusinessListBase, EditableRootListBase

Potential breaking change. Call OnDeserialized() as the first thing after deserialization, then do other initialization work. This change is to make code-gen easier, and specifically to be more consistent with how Initialize() is called when objects are created.

 

 

Csla – Core\ExtendedBindingList, BusinessListBase, Core\ReadOnlyBindingList, EditableRootListBase

Add Csla.Core.ExtendedBindingList<T>, which adds a RemovingItem event to the normal BindingList<T>. BusinessListBase, ReadOnlyBindingList and EditableRootListBase now inherit from this new base class, and so raise this new event.

 

 

Csla – Web\IReportTotalRowCount

You implement this interface in a collection class if you want to implement paging. This interface requires that you return the total row count of the collection, even if the collection itself only contains a subset of the total rows. By using a Criteria object, you can indicate the page size and page number, allowing DataPortal_Fetch() to retrieve just one page of data – but the TotalRowCount property from this interface must return the total possible number of rows of data.

 

 

Csla – Web\CslaDataSource

Implement support for paged and sorted data sources. There are now properties on the control so you can indicate whether the underlying data source supports paging or sorting. This way, if you bind to a SortedBindingList, for instance, you can indicate that the data source supports sorting to expose sorting support at a higher level.

 

Coupled with the new IReportTotalRowCount interface, the ability to indicate that a data source supports paging provides a relatively easy way to create a paged list that works property when bound to a grid control.

 

 

ProjectTracker – Project

Change the validation rules to be per-type instead of per-instance.

 

Also, change the validation of Started and Ended properties to use the new dependency feature of ValidationRules, simplifying the property set blocks.

 

 

ProjectTracker – Resource, ResourceAssignments, ResourceAssignment

Change DataPortal_Insert() and DataPortal_Update() to use the new ApplicationContext.LocalContext feature to pass the open connection from the root object through to the child objects. This avoids having to pass the connection as a parameter through each level, but still allows all the objects to easily share the same connection.

 

 

ProjectTracker – Role

Change the validation rules to use a combination of per-type and per-instance rule methods. While this is not a recommended approach (picking one or the other – preferably per-type – is much better), I thought it important to illustrate this concept.

 

(Updated 9/26/2006)