Rockford Lhotka's Blog

Home | Lhotka.net | CSLA .NET

 Tuesday, April 27, 2010

Here’s a really nice forum post discussing how one CSLA user combined the Reactive (Rx) framework with the CSLA data portal to streamline async calls.

Tuesday, April 27, 2010 3:03:54 PM (Central Standard Time, UTC-06:00)  #    Disclaimer  |  Comments [0]  | 
 Monday, April 26, 2010

I’m looking at how to provide information around .NET 4, Silverlight 4 and CSLA 4.

It is a challenging consideration. Many people like books, and I do to. I could create an Expert 2010 Business Objects book, which would take around 6 months. That means it would be out just about in time for Silverlight 5 to ship. Given the rate of Silverlight releases, conventional books (at least of the ~750 page variety) are too slow.

A lot of people have found my PDF ebooks to be very useful. Each one was around ~140 pages, and there’s no print/shipping/production time. This means they come out much faster – but they are still essentially static content once published, just like a conventional book.

The Silverlight and Core 3.8 video series have also been very popular. Not everyone likes to read, and there’s some real value in watching how things are done in a more dynamic manner. Also, videos take less time to produce than either an ebook or conventional book, so they are potentially more responsive. On the other hand, some people really dislike video content and prefer books.

I think it is safe to say that I’ll continue to produce video content, as that addresses an important set of users and is relatively fast to produce.

But I also think text content is important. It has a different price point, and offers advantages around consumption that just aren’t there with videos.

So the question is how to make text content timely, and at least a little less static – while still recognizing the realities of the amount of work involved.

Most conventional publishers now offer a “beta program” of some sort for their books. Basically you buy the book before it exists, and you get access to draft copies of chapters as they arrive. Once the chapters go through the edit process, you then get access to the final versions.

I’m thinking about doing something along that line for CSLA 4. Create ebook content, perhaps with each chapter as a separate PDF. Put this content on http://download.lhotka.net like I do with the videos, and then refresh the PDF files as they go through the revision process.

To be clear, this would be the “How to Use CSLA 4” book, not the “How to Build a .NET Framework” book. The focus would be on using the various features of CSLA to construct a reusable business layer, and how to use the data portal to achieve flexible n-tier deployment and scalability. Nor would it dive into specific UI technologies – I’d do separate ebooks for those.

Basically it will be like the Core 3.8 video series, but in ebook form, and targeting CSLA 4 of course.

Thoughts?

Monday, April 26, 2010 12:36:35 PM (Central Standard Time, UTC-06:00)  #    Disclaimer  |  Comments [17]  | 

I’ll be at the Chicago code camp this coming Saturday, May 1.

I’ll be talking about CSLA 4, walking through the new business, validation and authorization rules features, along with many of the other new features in version 4 (like the new LINQ support, ASP.NET MVC components, XAML components, etc).

If you are in the Chicago area, I encourage you to come to the code camp – there are a lot of good speakers and it will be a great time!

Also, on May 1 is the Iowa code camp, and at least a couple people from Magenic (Jason Bock and Brent Edwards) will be speaking at that event.

Monday, April 26, 2010 8:33:33 AM (Central Standard Time, UTC-06:00)  #    Disclaimer  |  Comments [0]  | 
 Saturday, April 24, 2010

I’m well into the redesign of the authorization system in CSLA .NET. The new system is integrated into the new business rules system, and so is no longer a separate concept. To me this makes sense, since authorization rules are just a specific type of business rule.

Some of the basic concepts from CSLA .NET 2.0 to 3.8 are still there. Per-property authorization:

  • CanReadProperty()
  • CanWriteProperty()
  • CanExecuteMethod()

Per-type authorization (which is a little different now) where you can ask:

  • Can the user create an instance of this type?
  • Can the user get/fetch an instance of this type?
  • Can the user edit/save an instance of this type?
  • Can the user delete an instance of this type?

And the new per-instance authorization where you can ask:

  • Can the user create the instance you already have? (silly, but possible)
  • Can the user get/fetch the instance you already have? (silly, but possible)
  • Can the user edit/save this instance?
  • Can the user delete this instance?

The actual authorization checks occur by executing an IAuthorizationRule rule object. CSLA provides the AuthorizationRule base class to make it easy to implement most rules, and includes IsInRole and IsNotInRole rules. For example, here’s the IsInRole rule:

/// <summary>
/// IsInRole authorization rule.
/// </summary>
public class IsInRole : AuthorizationRule
{
  private List<string> _roles;

  /// <summary>
  /// Creates an instance of the rule.
  /// </summary>
  /// <param name="action">Action this rule will enforce.</param>
  /// <param name="roles">List of allowed roles.</param>
  public IsInRole(AuthorizationActions action, List<string> roles)
    : base(action)
  {
    _roles = roles;
  }

  /// <summary>
  /// Creates an instance of the rule.
  /// </summary>
  /// <param name="action">Action this rule will enforce.</param>
  /// <param name="element">Member to be authorized.</param>
  /// <param name="roles">List of allowed roles.</param>
  public IsInRole(AuthorizationActions action, Csla.Core.IMemberInfo element, List<string> roles)
    : base(action, element)
  {
    _roles = roles;
  }

  /// <summary>
  /// Rule implementation.
  /// </summary>
  /// <param name="context">Rule context.</param>
  protected override void Execute(AuthorizationContext context)
  {
    if (_roles.Count > 0)
    {
      foreach (var item in _roles)
        if (Csla.ApplicationContext.User.IsInRole(item))
        {
          context.HasPermission = true;
          break;
        }
    }
    else
    {
      context.HasPermission = true;
    }
  }
}

At its most basic a rule is composed of some simple items:

  • An action (read/write property, execute method, create/get/edit/delete type/instance)
  • An optional target element (property/method)
  • An Execute() method that actually implements the rule

If your rule needs more information that’s fine – it is your class, so you can just add more properties. Just remember that a rule object is used across all instances of a business object type, so (just like with other business rules) the Execute() method absolutely must not change instance-level state in the rule object.

The Execute() method is passed a context object. The context object contains some simple properties:

  1. Target (optional reference to business object)
  2. HasPermission (true/false result returned by rule)

The Target property may be null. For per-property rules it will not be null, and for per-instance rules it will not be null. But for per-type rules it will be null. The way CSLA itself (especially the data portal) invokes rules is always per-property or per-type. The thing is, when possible even the per-type checks include the Target reference (typically only for CanEditObject checks). So you can use it if it is there, but you need to handle the case where it is null.

But the real point here, is that you can write your own authorization rule that has nothing to do with roles. You could use permissions, claims, random numbers – whatever you want to use to decide whether the user can or can’t perform the requested action.

Authorization rules are added in the AddBusinessRules() override in your business class – just like other business rules:

protected override void AddBusinessRules()
{
  BusinessRules.AddRule(new IsInRole(
    AuthorizationActions.WriteProperty,
    NameProperty,
    new List<string> { “Administrator” });
}

The rule set concept from other business rules applies here too – so you can have different authorization rules for different users/contexts/etc.

Finally, you can invoke rules as follows:

// per-type
bool result = Csla.Rules.BusinessRules.HasPermission(
  AuthorizationActions.CreateObject, typeof(Customer));
// per-instance
bool result = Csla.Rules.BusinessRules.HasPermission(
  AuthorizationActions.EditObject, _myCustomer);

The existing IAuthorizeReadWrite interface continues to operate as in 3.x, so you can use that to invoke CanReadProperty/CanWriteProperty/CanExecuteMethod as before.

Friday, April 23, 2010 11:07:27 PM (Central Standard Time, UTC-06:00)  #    Disclaimer  |  Comments [13]  | 
 Monday, April 19, 2010

Jason Bock just blogged with some interesting work he’s been doing around custom code analysis rules for CSLA 4:

Using Custom CodeAnalysis Rules in Your CSLA Projects

Monday, April 19, 2010 9:34:11 AM (Central Standard Time, UTC-06:00)  #    Disclaimer  |  Comments [0]  | 

I have put the demos from my Visual Studio 2010 launch talks online:

http://www.lhotka.net/files/devconn/VS2010Launch.zip

BasicVM and LiveDemo are literally the code I created on stage, so they are nothing to look at and probably have little value if you weren’t at the talks. The other demos are more complete and may be useful – especially UsingBxf, which shows some interesting UI proto-framework concepts in a pretty straightforward manner.

BasicVM

Demo created on stage to illustrate the most basic MVVM concepts.

Collective

Demo/prototype of a knowledge base/forum application that makes use of a pre-release version of CSLA 4, MVVM and other Silverlight concepts. It is a 3-tier physical deployment (Silverlight client, web/app server, SQL Server database) architecture using many CSLA .NET concepts.

This code uses the Bxf (“Basic XAML Framework”) to implement its shell and viewmodel code in a way that can be unit tested.

This project has a dependency on a pre-release version of CSLA 4.

LiveDemo

Demo created on stage to illustrate the basic use of the Visual Studio 2010 XAML designer.

UsingBxf

Demo showing simple use of the “Basic XAML Framework” (Bxf) in WPF. The same Bxf code works in Silverlight as well. The Bxf code and this demo show how to implement a basic UI shell that shows content and a status bar. For more advanced uses of Bxf see the Collective demo.

This project has a dependency on a pre-release version of CSLA 4.

VSL01

Demo showing some concepts and implementation around MVVM.

VSL02

Demo showing some concepts and implementation around MVVM.

Monday, April 19, 2010 8:59:44 AM (Central Standard Time, UTC-06:00)  #    Disclaimer  |  Comments [0]  | 

Here’s a link to an article I wrote on using code-gen vs building a more dynamic runtime:

http://msdn.microsoft.com/en-us/library/ff621668.aspx

Update: There doesn't appear to be a code download on the Microsoft site. Click here to download the code.

Sunday, April 18, 2010 11:30:24 PM (Central Standard Time, UTC-06:00)  #    Disclaimer  |  Comments [2]  | 
 Friday, April 16, 2010

I have put CSLA 4 Preview 4 online for download.

CSLA 4 requires Visual Studio 2010, .NET 4 and (optionally) Silverlight 4.

There are a lot of changes in CSLA 4 - make sure to read the change log!

This will be the last preview, and it includes all planned CSLA 4 functionality except:

  • New authorization rules system
  • INotifyDataErrorInfo support (SL only)
  • Class level DataAnnotation attributes

My intent is to finish these three items within the next two weeks, and then enter a beta phase.

Your feedback now and through the beta phase is absolutely invaluable to the quality of the release. If you are using or planning to use .NET 4 or Silverlight 4 please take a look at the preview and let me know your thoughts and any bugs you find by replying to this thread on the forum.

Friday, April 16, 2010 10:41:48 PM (Central Standard Time, UTC-06:00)  #    Disclaimer  |  Comments [0]  | 
 Friday, April 09, 2010

In preparation for the Twin Cities Code Camp I put together a short list showing the biggest and most important changes in CSLA 4:

  • Solution/project structure
    • Projects/assemblies better organized
    • Client profile support
    • Setup project
  • Business objects
    • List types now ObservableCollection<T>
    • New RegisterProperty() overloads
    • Automatic use of description/display attributes
    • CommandBase/CriteriaBase/CslaIdentity generic
    • Parent property public
    • Widespread use/requirement of Csla.Core.IPropertyInfo
  • Business rules
    • New business rule system
    • DataAnnotations
  • Authorization rules (coming)
    • New authz rule system
    • Integrated into business rule system
  • LINQ to CSLA
    • Remove 3.x implementation
    • Add LinqObservableCollection and ToSyncList()
  • ASP.NET MVC
    • CslaModelBinder
    • Controller
    • IModelCreator
  • Silverlight
    • IDataErrorInfo
    • INotifyDataErrorInfo (coming)
    • Browsable(false)
  • XAML
    • MVVM (ViewModelBase/ViewModel)
    • TriggerAction
    • Unified PropertyStatus/BusyAnimation
  • Data portal
    • Open up data portal for SL
    • Pluggable proxy objects to dynamically switch app servers
    • Compressed WCF example for .NET
    • Impersonation allowed via WCF
    • Client culture flows from SL client to app server
  • Reflection
    • MethodCaller uses lambda expressions
Friday, April 09, 2010 10:53:36 PM (Central Standard Time, UTC-06:00)  #    Disclaimer  |  Comments [0]  | 
 Tuesday, April 06, 2010

Software design and development is called many things. Sometimes engineering, sometimes art, sometimes magic. And sometimes science, though for some reason that is less common in my experience.

Which is a little odd, since a lot of us have Computer Science degrees, which are usually a Bachelors or Masters of Science.

Today I applied science in a way so obvious it just kind of hit me that we really do science, at least from time to time.

I’m working on the CSLA 4 business rules system, trying to exercise it to locate bugs and issues, as well as writing some blog posts (here and here) on the topic. I was writing a post about some thoughts I have around dependent properties, and it occurred to me that the current code doesn’t work like I think it should.

That’s a hypothesis. An assertion that something does (or in this case doesn’t) work a certain way.

So I wrote a test to establish that the current implementation doesn’t work the way I think it should. And I ran my test. And the test passed.

Now this isn’t good. My hypothesis was a negative, and my test was a positive. In other words, I wrote the test for the way I thought it should work, and I’m pretty convinced that the current implementation doesn’t work that way. So why did my test pass?

At this point I could do two things. I could say “well, it works, so I should be happy”, or I could be a scientist and figure out how or why it is working.

There are lots of parallels to this in real life. Especially in medicine, but in many other aspects of life as well. People observe something that appears to work (a herb seems to fix headaches or whatever), a technique that is referred to as being “evidence-based”. Some people just accept it and live with the ill-defined and unsubstantiated belief that the result is actually real and repeatable or reliable. Scientists don’t necessarily reject that it works, but they have the need to understand why it works, and to ensure it is actually repeatable.

In my case I spent a couple hours digging into how and why my test was passing. I found two things. First, my test was slightly faulty. Second, there really is a bug in my current implementation. In other words my original hypothesis is correct.

This was all really subtle. The current implementation half-works, but only because of a bug in the flawed implementation – it should have completely failed. In other words, the implementation is flawed, but a bug in the flawed implementation made it half-work. It is like a double-bug… And my test wasn’t thorough enough to fully exercise the scenario, so it didn’t catch the failure, so my testing methodology was also to blame.

Ultimately my application of science has been beneficial, though it revealed a bunch more work I need to do.

The cool thing though, is that this reinforces how important science is in daily life, for computing and everything else. I surely hope that the medicines I take, and the airplanes in which I fly and the cars I drive go through rigorous scientific testing – because the last thing I want is to trust my life to evidence-based and unsubstantiated beliefs that everything is rosy…

Tuesday, April 06, 2010 1:25:57 PM (Central Standard Time, UTC-06:00)  #    Disclaimer  |  Comments [4]  | 
 Monday, April 05, 2010

I knew I was forgetting something when I wrote the big post covering the new CSLA .NET business rule system. And it was rule chaining, which really is a big feature.

In CSLA 3.8 a rule could return one value: true or false. This is because a rule was a Boolean method. Oh sure, you could return some limited data through the RuleArgs (Description, Severity, StopProcessing), but this was really all part of one result.

In CSLA 4 a rule can return many results. This means that one rule can invoke other rules, and all their consolidated results are returned as the result of the one rule. Or you could create a rule that invoked an external rules engine, returning all results of the engine as a result of your rule. Or you could create a rule that invoked a workflow (as in Windows Workflow Foundation 4), again consolidating the results of the workflow as a result of your rule.

One way to use this is as an escape hatch. If you don’t like the way CSLA manages rules, just write your own “rule” that is actually a replacement rule engine, and allow CSLA to invoke your rule engine every time a property changes.

But even if you don’t go so far as to write your own rule engine, there are various interesting patterns that fit neatly within the existing CSLA 4 rule model.

For example, it is quite common for people to want to invoke a rule only if other properties of the object are in a certain state. Maybe you only want to require a value if the object is new, but not if it already exists. To do this, you can create a gate rule

public class NewGateRule : BusinessRule
{
  public NewGateRule(Csla.Core.IPropertyInfo primaryProperty)
    : base(primaryProperty)
  { }

  private IBusinessRule _innerRule =
    (IBusinessRule)new Csla.Rules.CommonRule.Required(PrimaryProperty);

  protected override void Execute(RuleContext context)
  {
    var obj = context.Target as Csla.Core.ITrackStatus;
    if (obj != null && obj.IsNew)
      _innerRule.Execute(context.GetChainedContext(required));
  }
}

This rule creates an instance of the Required rule, but only executes that inner rule if the target business object’s IsNew property is true. So the real rule is only invoked if the gate condition is met.

Notice the use of the GetChainedContext() method, which takes the RuleContext passed to the NewGateRule and creates a chained RuleContext that is appropriate for the inner rule. This technique automatically consolidates the results of the inner rule together with the outer rule (and any other chained rules) so when the NewGateRule completes all the results are automatically processed.

The same coding technique could be used to invoke many other rules if desired, each one having its own chained RuleContext. And chaining can be nested, so you could have a gate rule execute another gate rule that executes the real rule and that should work fine.

Keeping in mind that rules are just types, it is possible to do something slightly more dynamic – though your metadata does need to include not just the full type name of the rule, but also a way to describe the constructor parameters required. But this pseudo-code illustrates the concept:

public class DynamicRule : BusinessRule
{
  public DynamicRule(Csla.Core.IPropertyInfo primaryProperty, string innerRule)
    : base(primaryProperty)
  {
    var t = Type.GetType(innerRule);
    object[] args = new object[] { primaryProperty };
    _innerRule = (IBusinessRule)Activator.CreateInstance(t, args);
  }

  private IBusinessRule _innerRule;

  protected override void Execute(RuleContext context)
  { 
    _innerRule.Execute(context.GetChainedContext(required));
  }
}

In this case the rule expects the full type name of the real rule to be provided. It uses that type information to dynamically create an instance of the real rule, and then executes it using a chained RuleContext. It should be clear that this basic concept could be extended to create a rule that dynamically loads an entire set of rules (perhaps using MEF) and executes those rules using chaining.

Monday, April 05, 2010 10:51:46 PM (Central Standard Time, UTC-06:00)  #    Disclaimer  |  Comments [0]  | 

The first preview of the new CSLA 4 business rules subsystem will be available soon from http://www.lhotka.net/cslanet/download.aspx.

Of course there are a lot of other changes to CSLA .NET in this preview, so make sure to carefully read the change log. Although there are a lot of breaking changes, most of them have pretty minimal impact on people who are already using the 3.8 coding style for classes. Except for the business rules – that impacts everyone.

This is a major change to the way business and validation rules work, with some pretty amazing new capabilities as a result.

The next preview will roll authorization rules into this as well, and that’ll be the last major change for CSLA 4.

The business rule changes apply to both Silverlight and .NET – as always the idea is that the vast majority of your business code should be the same regardless of platform.

I’d like to summarize the primary changes from 3.8 to 4 in regards to business and validation rules.

Simple changes

The simplest change is that the ValidationRules property in BusinessBase is now named BusinessRules. Also, the Csla.Validation namespace has been replaced by the Csla.Rules namespace.

If you are using DataAnnotations validation attributes in 3.8, they continue to work in 4 without change.

That was the easy part. Now for the interesting changes.

Rule classes

Rule methods are replaced by rule classes. This means a 3.8 rule like this:

private static bool MyRule<T>(T target, RuleArgs e) where T : Customer
{
  if (target.Sales < 10)
  {
    e.Description = "Customer has low sales";
    e.Severity = RuleSeverities.Information;
    return false;
  }
  else
    return true;
}

becomes a class like this:

private class MyRule : Csla.Rules.BusinessRule
{
  protected override void Execute(RuleContext context)
  {
    var target = (Customer)context.Target;
    if (target.Sales < 10)
      context.AddInformationResult("Customer has low sales");
  }
}

It is the same basic rule, but packaged just a little differently. Rule types must implement IBusinessRule, but it is easier to inherit from BusinessRule, which provides a set of basic functionality you’ll typically need when implementing a rule.

The most important thing to understand is that the RuleContext parameter provides input and output for the Execute() method. The context parameter includes a bunch of information necessary to implement the rule, and has methods like AddErrorResult() that you use to indicate the results of the rule.

There’s one coding convention that you must follow: the protected properties from BusinessRule must never be changed in Execute(). I wish .NET had the ability to create an immutable type – a class where you could initialize properties as the object is created, and then ensure they are never changed after that point. But such a concept doesn’t exist, at least in C# or VB. But that is what you must do with rule objects. You can set the properties of the rule object as it is created. After that point, if you change properties of the rule object you are going to cause bugs. So do not change the properties of a rule in Execute() and you’ll be happy.

AddRule Method

In the Customer business class you still override AddBusinessRules(), and that code looks like this:

protected override void AddBusinessRules()
{
  base.AddBusinessRules();
  BusinessRules.AddRule(new MyRule { PrimaryProperty = SalesProperty });
}

The new AddRule() method accepts an instance of an IBusinessRule instead of a delegate like in 3.8. This one instance of the rule is used for all instances of the business object type. In other words, exactly one instance of MyRule is created, and it is used by all instances of Customer. You must be aware of this when creating a rule class, because it means you can never alter instance-level fields or properties after the rule is initialized. If you do alter an instance-level field or property, you’ll affect the rule’s behavior for all Customer objects in the entire application – and that’d probably be a very bad thing.

Most rule methods that require a primary property will actually require it on the constructor. For example, look at the Required and MaxLength rules:

protected override void AddBusinessRules()
{
  base.AddBusinessRules();
  BusinessRules.AddRule(new MyRule { PrimaryProperty = SalesProperty });
  BusinessRules.AddRule(new Csla.Rules.CommonRules.Required(NameProperty));
  BusinessRules.AddRule(new Csla.Rules.CommonRules.MaxLength(NameProperty, 20));
}

The result is the same either way, but when you are writing your rules it is typically best to explicitly require a primary property on the constructor if you plan to use the primary property value. Later I’ll talk about per-object rules that have no primary property.

You should also notice that the properties are always Csla.Core.IPropertyInfo, not string property names. The new business rule system requires that you use PropertyInfo fields to provide metadata about your properties. This syntax has been available since CSLA 3.5, and for my part I haven’t created a business class without them for years. This is one part of a larger effort to eliminate the use of string property names throughout CSLA and CSLA-based business code.

Invoking Rules

In the previous AddRule() call, the PrimaryProperty property of the rule is set to SalesProperty. This is what links the rule to a specific property. So when the Sales property changes, this rule will be automatically invoked. As in 3.8, all rules are invoked (by default) when an object is created through the data portal, and can be invoked by calling BusinessRules.CheckRules(). And you can invoke rules for a property with BusinessRules.CheckRules(SalesProperty) – though you should notice that CheckRules() now requires an IPropertyInfo, not a string property name.

If you don’t provide a PrimaryProperty value as shown in this example, the rule method is associated with the business type, not a specific property. This means that the rule will run when CheckRules() is called with no parameter, or when the new CheckObjectRules() method is called to invoke rules attached to the business type.

In summary, there are now three CheckRules() overloads:

  1. CheckRules() – checks all rules for an object
  2. CheckObjectRules() – checks all rules not associated with a specific property (object rules)
  3. CheckRules(property) – checks all rules for a specific property

The same concepts of priority and short-circuiting from 3.8 apply in 4.

InputProperties and InputPropertyValues

You can write that same rule just a little differently, making it unaware of the Target object reference:

public class MyRule : Csla.Rules.BusinessRule
{
  public MyRule(Csla.Core.IPropertyInfo primaryProperty)
    : base(primaryProperty)
  {
    InputProperties = new List<Csla.Core.IPropertyInfo> { primaryProperty };
  }

  protected override void Execute(RuleContext context)
  {
    var sales = (double)context.InputPropertyValues[PrimaryProperty];
    if (sales < 10)
      context.AddInformationResult("Sales are low");
  }
}

This is a little more complex and a lot more flexible. Notice that the rule now requires a primary property, which is actually managed by the BusinessRule base class. Also notice that it adds the primaryProperty value to a list of InputProperties. This tells CSLA that any time this rule is invoked, it must be provided with that property value.

In the Execute() method notice how the sales value is retrieved from the context by using the InputPropertyValues dictionary. This dictionary contains the values from the business object based on the properties in the InputProperties list. A rule can require many property values be provided. Of course the rule will only work with objects that actually have those properties!

Because this rule will work with any object that can provide a double property value, the class is now public and could be placed in a library of rules.

The AddRule() method call in Customer must change a little:

protected override void AddBusinessRules()
{
  base.AddBusinessRules();
  BusinessRules.AddRule(new MyRule(SalesProperty));
}

As before, the PrimaryProperty property is being set, but this time it is through the constructor instead of setting the property directly. This is because the rule now requires the primary property value, and can only work if it is supplied.

Business Rules: Rules that modify data

The CSLA 4 rules system formally supports business rules. Really 3.8 did too, but it was a little confusing with the terminology, which is why ValidationRules is now called BusinessRules – to avoid that confusion.

The RuleContext object now has an AddOutValue() method you can use to provide an output value from your rule:

context.AddOutValue(NameProperty, “Rocky”);

When the rule completes, any out values are updated into the business object using the LoadProperty() method (so there’s no circular loop triggered).

This AddOutValue() technique is safe for synchronous and asynchronous rules, because the business object isn’t updated until the rule completes and control has returned to the UI thread.

If you are creating a synchronous rule, the RuleContext does have a Target property that provides you with a reference to the business object, so you could set the property yourself – just remember that setting a property typically triggers validation, which could cause an endless loop. Overall, it is best to use AddOutValue() to alter values.

Validation Rules: Errors, Warnings and Information

As in 3.8, CSLA 4 allows validation rules to provide an error, a warning or an informational message. The RuleContext object has three methods:

  1. AddErrorResult()
  2. AddWarningResult()
  3. AddInformationalResult()

Each of these methods takes a string parameter which is the human-readable text to be displayed. A rule should only call one of these methods. If the rule calls them more than once, only the last one will have effect.

If AddErrorResult() is called, the rule is returning a failure result, and the business object will become invalid (IsSelfValid will be false).

If no method is called, or AddWarningResult() or AddInformationalResult() is called the rule is returning a success result and the business object will not become invalid. This is basically the same as the 3.8 behavior.

Dependent Properties

The concept of dependent properties is quite different in the new system. Instead of an AddDependentProperty() method, a rule now indicates the properties it affects. This can be coded into the rule itself or specified when AddRule() is called. Either way, what’s happening is that every rule object maintains a list of AffectedProperties for that rule.

When a rule completes a PropertyChanged event is raised for every property in AffectedProperties, as well as any properties altered through AddOutValue().

It is also important to realize that, with an async rule, all properties in AffectedProperties will be marked busy when the rule starts, which typically means the user will see a busy animation (if you are using the PropertyStatus control) to show that the property has some outstanding operation running.

Rule Sets

This is a major new feature of the rules system that is designed to support web applications where multiple customers/clients/organizations are sharing the same virtual root and application. In that case it might be necessary to have different business rules for each customer/client/organization. Rule sets are a way to accomplish this goal.

When you call BusinessRules.AddRule(), the rules are added to a rule set. Normally they are added to the default rule set, and if you only need one set of rules per type of object you can just be happy – it works. But if you need multiple sets of rules per type of object (such as one set of rules for customer A and a different set for customer B), then you’ll want to load each set of rules and attach those rules to your business object type.

This is done by changing the BusinessRules.RuleSet property. For example:

protected override void AddBusinessRules()
{
  base.AddBusinessRules();

  // add rules to default rule set
  BusinessRules.AddRule(...);
  BusinessRules.AddRule(...);
  BusinessRules.AddRule(...);

  // add rules to CustA rule set
  BusinessRules.RuleSet = "CustA";
  BusinessRules.AddRule(...);
  BusinessRules.AddRule(...);
  BusinessRules.AddRule(...);

  // add rules to CustB rule set
  BusinessRules.RuleSet = "CustB";
  BusinessRules.AddRule(...);
  BusinessRules.AddRule(...);
  BusinessRules.AddRule(...);

  // use default rule set
  BusinessRules.RuleSet = “default”;
}

This code loads rules into three rule sets: default, CustA and CustB. It is up to you to set the RuleSet property to the correct value for each business object instance.

To set the rule set, set the BusinessRules.RuleSet property, then make sure to call BusinessRules.CheckRules() to apply the new set of rules.

The RuleSet value is serialized along with the state of the business object, so you can (and typically will) set the RuleSet property in your DataPortal_Create() and DataPortal_Fetch() methods, and that rule set will be used through the life of the object. I expect the most common scenario will be to set the RuleSet based on the identity of the current user, or some context value in Csla.ApplicationContext.ClientContext.

Per-Object Rules

Finally, it is possible to have rules that are not attached to a specific primary property. These rules are attached to the business object. For example:

protected override void AddBusinessRules()
{
  base.AddBusinessRules();
  BusinessRules.AddRule(new MyObjectRule());
}

private class MyObjectRule
{
  protected override void Execute(RuleContext context)
  {
    var target = (Customer)context.Target;
    // check rules here
  }
}

Notice that the AddRule() method has no primary property specified. Because there’s no primary property, the rule is attached to the business object itself. Normally this type of rule is a private rule in the business class, and uses the business object’s properties directly. But you can specify input and affected properties, as well as provide output values as discussed earlier.

Per-object rules are run in two cases:

  • You call BusinessRules.CheckRules()
  • You call BusinessRules.CheckObjectRules()

Per-object rules are not run automatically when properties change. So if you don’t invoke them, they won’t run.

Async Rules

One of my primary goals in designing the new rule system is to provide a lot of consistency between sync and async rules. To this end, both sync and async rules are constructed the same way: by implementing IBusinessRule or subclassing BusinessRule.

But there are some extra restrictions on async rules. Most notably, async rules must get their input values through the RuleContext, and must provide any output values through RuleContext. To help enforce this, the context.Target property is always null in an async rule. This should help prevent the rule from trying to interact directly with the business object.

The reason this is so important, is that I assume an async rule will run some of its code on a background thread. Most of CSLA (and .NET) is not threadsafe, so having multiple threads interact with a business object will cause problems. If the async rule uses the RuleContext as a message to get and return values, CSLA can help ensure processing occurs on the correct thread.

Here’s a simple async rule. It is a little silly, in that all it does is ToUpper() a string value, but it should give you the idea:

public class AsyncToUpper : Csla.Rules.BusinessRule
{
  public AsyncToUpper(Csla.Core.IPropertyInfo primaryProperty)
    : base(primaryProperty)
  {
    IsAsync = true;
    InputProperties = new List<Csla.Core.IPropertyInfo> { primaryProperty };
  }

  protected override void Execute(Csla.Rules.RuleContext context)
  {
    var bw = new System.ComponentModel.BackgroundWorker();
    bw.DoWork += (o, e) =>
    {
      var value = (string)context.InputPropertyValues[PrimaryProperty];
      context.AddOutValue(PrimaryProperty, value.ToUpper());
    };
    bw.RunWorkerCompleted += (o, e) =>
    {
      if (e.Error != null)
        context.AddErrorResult(e.Error.Message);
      context.Complete();
    };
    bw.RunWorkerAsync();
  }
}

The rule indicates that it is async by setting IsAsync to true in its constructor. This tells CSLA that the rule expects to run some or all of its logic on a background thread, so CSLA does some extra work for you. Specifically, CSLA marks the primary property and any AffectedProperties as busy before the rule starts and not busy when it ends. It also sets up the rule invocation so its results are handled through an async callback within CSLA. And it makes sure the RuleContext.Target property is null.

Next, notice that the InputProperties list is initialized in the rule’s constructor. This is the list of property values the rule requires to operate, and these property values will be provided through the RuleContext to the Execute() method in the InputPropertyValues dictionary.

The Execute() method itself is using a BackgroundWorker to run some code on a background thread. You can use BackgroundWorker or the async DataPortal and they’ll work fine. The one big requirement here is that whatever you use must ensure that the competed callback is on the UI thread. It is your responsibility to make sure the completed callback is on the UI thread (if any). The BackgroundWorker and data portal do this for you automatically. If you use some other async technology you must take steps to make sure this is done correctly.

The AddOutValue() method is used to provide the output value. Remember that the actual business object property isn’t affected until after the rule completes, which is when CSLA is handling the results of your rule on the UI thread (where it can safely update the business object).

The RunWorkerCompleted event is used to handle any async errors. You’d do the same thing with the data portal, handling the FetchCompleted event (or similar). It is important to remember that exceptions occurring on a background thread are lost unless you carry them through. The code shown here is following what I typically do, which is to add an error result from the rule in the case of an async exception.

One last thing to keep in mind: there is exactly one instance of your rule object being used by all instances of a business type. Because of this, the Execute() method must be atomic and stateless. To put it another way, you should never, ever, ever alter any instance-level fields or properties of the rule object from the Execute() method. If you do alter an instance-level field or property of the rule object, that change will affect all business objects, not just the one you are running against right now. And with async rules you’ll run into race conditions and other nasty multi-threading issues. This is really the same restriction I mentioned earlier with sync rules – don’t change rule properties in Execute() – but it is so important that I wanted to reiterate the point here too.

While there are some restrictions on how you construct an async rule, I am pretty happy with how similar sync and async rules are to implement. In fact, all the async concepts (input values, AddOutValue()) work just fine with sync rules too.

Moving from 3.8 to 4

While the new business rules system is somewhat different from the 3.8 implementation, the process of moving from 3.8 to 4 isn’t terribly painful.

  • Every rule method must become a rule class. This is a pretty mechanical process, but obviously does require some work
  • Every use of ValidationRules must be changed to BusinessRules
  • Every AddRule() call will be affected, which is another mechanical change
  • Dependent properties become AffectedProperties on each AddRule() method call

I was able to get the entire unit test codebase changed over in less than 8 hours – and that included changes not only for the business rules, but also for the data portal and several other breaking changes. I don’t mean to trivialize the effort required, but the changes are mostly mechanical in nature, so it is really a matter of plowing through the code to make the changes, which is mostly repetitive and boring, not really hard.

Summary

I’m using the major version change from 3 to 4 as an opportunity to make some fairly large changes to CSLA. Changes that enable some key scenarios needed by Magenic customers, and requested by people on the forum explicitly or implicitly. Some effort will be required to upgrade, but I suspect most people will find it well worth the time.

The big changes are:

  • Rule sets
  • Per-object rules
  • Rule objects instead of rule methods
  • Common model for sync and async rules

I’m pretty excited about these changes, and I hope you find them useful!

Monday, April 05, 2010 12:40:05 PM (Central Standard Time, UTC-06:00)  #    Disclaimer  |  Comments [19]  | 
 Friday, April 02, 2010

This is the first of a series of blog posts I’m putting together as I prepare for the next preview release of CSLA 4. That preview release is coming soon, there are just a couple more things to wrap up, but I want to have the preview out by the .NET 4 launch, which is April 12.

CSLA 4 includes a lot of changes, including some changes to the data portal. This post covers the highlights.

DataPortal methods

Some are relatively small, but might impact existing code.

One example is the removal of the non-generic DataPortal method overloads. Create() is gone in favor of Create<T>().

Another example is that DataPortal_Fetch(object) is no longer a virtual method in the base classes, which means if you are overriding this method you’ll need to remove the override keyword in your code.

The reason for removing the non-generic DataPortal methods is that it makes the criteria parameter for Create/Fetch/Delete more flexible. It is now possible to use any serializable object as a criteria. On .NET this means anything that can be serialized with the BinaryFormatter and/or NDCS. On Silverlight this means anything that can be serialized with MobileFormatter. And this includes primitive types. In short, you can now do this:

var cust = DataPortal.Fetch<Customer>(123);

This eliminates the need for the SingleCriteria<> type, and generally simplifies most data portal calls.

Proxy factory

A more significant change, though one that has no impact unless you want to use it, is the ability to create your own proxy factory. By default the data portal loads the client-side proxy based on the type you specify in your config file – so you can switch between local, WCF, Remoting and other proxies through configuration.

But it is now possible to create a proxy factory by implementing IDataPortalProxyFactory. This factory is responsible for creating the data portal proxy object based on the type of business object being sent through the data portal. Add a key in your config file for CslaDataPortalProxyFactory to specify the assembly qualified type of your implementation and CSLA will use that instead of the default behavior.

The idea is that some applications need to use different app servers or different protocols for different business objects. If you create your own proxy factory, you get the opportunity to examine the business object type (probably using reflection) so you can decide which proxy should be used to talk to an app server for that particular type of object.

Server exceptions

(not implemented yet, but in progress)

Server exceptions are always returned to the client wrapped in a DataPortalException. This is not changing, because the DataPortalException is what allows CSLA to provide you with the full stack trace and other useful information from the server.

However, it is now possible to replace the original server exception with a new exception in your override of DataPortal_OnDataPortalException() or the InvokeError() method of a factory object.

Silverlight flexibility

The Silverlight data portal was not as flexible as the .NET data portal. Specifically, it wasn’t possible to create your own proxy/host channel that totally replaced the WCF proxy/host provided by CSLA. In CSLA 4 you can now create completely custom proxy/host channels for the Silverlight data portal. This means you could, for example, create one that didn’t use WCF at all, but instead used raw TCP sockets. Or more likely, it means you can create a proxy/host that uses a custom third-party WCF binding.

A related change is to the MobileFormatter, which now has the ability to provide you with completely serialized data (the default), or to provide you with the serialized data in an intermediate DTO form. If you get the DTO form, you are getting an object graph that consists only of objects which can be serialized using the DataContractSerializer (or similar serializers). This opens up a lot of flexibility, because it means you can apply other serializers (perhaps from third parties) to the DTO graph. In some cases this

Design time data

VS10 has a new design time data model, so the old design time data behaviors have been removed from the data portal. This won’t break any code, but if you were using the design time data generation concept, it is now gone and your methods won’t be invoked.

.NET compression sample

There’s a new sample for .NET that shows how to use a standard compression library (of your choice) to compress the data that goes over the data portal. I think it is probably best to get a WCF binding that does the compression, but if you want an alternative this sample shows you how to do it.

Overall the data portal continues to offer the same n-tier and network transport flexibility it has always provided, but these changes make common scenarios easier to implement, and enable some important advanced scenarios that have been frequently requested.

Friday, April 02, 2010 11:36:03 PM (Central Standard Time, UTC-06:00)  #    Disclaimer  |  Comments [1]  | 
On this page....
Search
Archives
Feed your aggregator (RSS 2.0)
October, 2014 (1)
August, 2014 (2)
July, 2014 (3)
June, 2014 (4)
May, 2014 (2)
April, 2014 (6)
March, 2014 (4)
February, 2014 (4)
January, 2014 (2)
December, 2013 (3)
October, 2013 (3)
August, 2013 (5)
July, 2013 (2)
May, 2013 (3)
April, 2013 (2)
March, 2013 (3)
February, 2013 (7)
January, 2013 (4)
December, 2012 (3)
November, 2012 (3)
October, 2012 (7)
September, 2012 (1)
August, 2012 (4)
July, 2012 (3)
June, 2012 (5)
May, 2012 (4)
April, 2012 (6)
March, 2012 (10)
February, 2012 (2)
January, 2012 (2)
December, 2011 (4)
November, 2011 (6)
October, 2011 (14)
September, 2011 (5)
August, 2011 (3)
June, 2011 (2)
May, 2011 (1)
April, 2011 (3)
March, 2011 (6)
February, 2011 (3)
January, 2011 (6)
December, 2010 (3)
November, 2010 (8)
October, 2010 (6)
September, 2010 (6)
August, 2010 (7)
July, 2010 (8)
June, 2010 (6)
May, 2010 (8)
April, 2010 (13)
March, 2010 (7)
February, 2010 (5)
January, 2010 (9)
December, 2009 (6)
November, 2009 (8)
October, 2009 (11)
September, 2009 (5)
August, 2009 (5)
July, 2009 (10)
June, 2009 (5)
May, 2009 (7)
April, 2009 (7)
March, 2009 (11)
February, 2009 (6)
January, 2009 (9)
December, 2008 (5)
November, 2008 (4)
October, 2008 (7)
September, 2008 (8)
August, 2008 (11)
July, 2008 (11)
June, 2008 (10)
May, 2008 (6)
April, 2008 (8)
March, 2008 (9)
February, 2008 (6)
January, 2008 (6)
December, 2007 (6)
November, 2007 (9)
October, 2007 (7)
September, 2007 (5)
August, 2007 (8)
July, 2007 (6)
June, 2007 (8)
May, 2007 (7)
April, 2007 (9)
March, 2007 (8)
February, 2007 (5)
January, 2007 (9)
December, 2006 (4)
November, 2006 (3)
October, 2006 (4)
September, 2006 (9)
August, 2006 (4)
July, 2006 (9)
June, 2006 (4)
May, 2006 (10)
April, 2006 (4)
March, 2006 (11)
February, 2006 (3)
January, 2006 (13)
December, 2005 (6)
November, 2005 (7)
October, 2005 (4)
September, 2005 (9)
August, 2005 (6)
July, 2005 (7)
June, 2005 (5)
May, 2005 (4)
April, 2005 (7)
March, 2005 (16)
February, 2005 (17)
January, 2005 (17)
December, 2004 (13)
November, 2004 (7)
October, 2004 (14)
September, 2004 (11)
August, 2004 (7)
July, 2004 (3)
June, 2004 (6)
May, 2004 (3)
April, 2004 (2)
March, 2004 (1)
February, 2004 (5)
Categories
About

Powered by: newtelligence dasBlog 2.0.7226.0

Disclaimer
The opinions expressed herein are my own personal opinions and do not represent my employer's view in any way.

© Copyright 2014, Marimer LLC

Send mail to the author(s) E-mail



Sign In