Logging CSP violations to Exceptionless with NWebsec

Logging CSP violations to Exceptionless with NWebsec

Anyone that creates web applications should use Content-Security-Policy (CSP) to protect their applications against injection attacks by specifying where content in the web pages can be loaded from.

If you’re unfamiliar with CSP you should read An Introduction to Content Security Policy by Mike West, one of the Chrome developers. You’ll also find information about CSP on the Mozilla Developer Network.

In this article we will look into how simple it is to log CSP Violations in order to find the correct settings for your application using NWebsec and Exceptionless.

NWebsec is a very nice security library by Andre Klingsheim that allows you to handle configuration of CSP in web.config or in code. Exceptionless is a real-time error, feature and log reporting for all kinds of applications and can either be hosted on-site og use the SAAS provisioning by Exceptionless.

Source code is available at https://github.com/jonnybee/CSPLogging

I start out by creating a new ASP.NET MVC project and add the following NuGet packages:

– NWebsec.Mvc
– Exceptionless.Mvc

Assuming you may already have your own webapplication already running, we will use the Content-Security-Policy-Report-Only to report CSP violiatins without actually making the browser enforce the CSP. When your baseline is set it is just a matter og changing the name in config to Content-Security-Policy and the browsers will now starte to
enforce the policies.

You may use both Content-Security-Policy and Content-Security-Police-Report-Only in parallel but only Content-Security-Policy will make the browsers enforce policies.

Note: Each violation in the users browser will be logged as a separate call back to your website (report-url).

Configure NWebsec

by adding this configuration section in web.config to confiure Content-Security-Policy-Report-Only.
Note that the configuration also sets the report-uri option to enable the builtin handler.

<securityHttpHeaders><content-Security-Policy-Report-Only enabled="true">
<default-src self="true"/>
<script-src self="true">
<add source="nwebsec.codeplex.com" />
<add source="scripts.nwebsec.com" />
</script-src>
<style-src unsafeInline="false" self="true" />
<img-src self="true">
<add source="images.nwebsec.com"/>
</img-src>
<object-src none="true" />
<media-src none="true" />
<frame-src none="true" />
<font-src none="true" />
<connect-src none="true" />
<frame-ancestors none="true" />
<report-uri enableBuiltinHandler="true"/>
</content-Security-Policy>
</securityHttpHeaders>

Configure Exceptionless

If you do not have an account on Exceptionless already head on to Exceptionless and create a free account and add a “Project”.

Follow the instructions and select your project type (ASP.NET MVC) and make sure to update the
the exceptionless apiKey=”API_KEY_HERE” section located in the project’s web.config with your Exceptionless API key.

Add logging to Exceptionless

So we now have the CSP configred along with Exceptionless and a corresponding “project” in Exceptionless.
To round up the logging part in code we need to add this in Global.asax.cs:

protected void NWebsecHttpHeaderSecurityModule_CspViolationReported(object sender, CspViolationReportEventArgs e)
{
var report = e.ViolationReport;
var directive = report.Details.ViolatedDirective.Split(' ').FirstOrDefault();

ExceptionlessClient.Default.CreateLog($"ContentSecurityPolicy:{directive}",
$"Violation:{report.Details.BlockedUri}", Exceptionless.Logging.LogLevel.Warn)
.AddObject(report.Details)
.Submit();
}

Run you application and open Exceptinless in a new tab , login and view the “Log messages” folder.
Exceptionless Log Messages

and when you use the detailed view you get the entire post details:
Exceptionless log detail view

Now we can start working on adding the necessary configurations in the config, rerun the application and then verify that the violations are gone. This is a great learning experience into what is beeing used in your application as well as a very efficient security practice to avoid f.ex spyware/adware in the users browser from injection content into your application on the client side.

For this sample MVC app the required configuration is:

<securityHttpHeaders>
<content-Security-Policy-Report-Only enabled="true">
<default-src none="true"/>
<script-src self="true">
<add source="ajax.googleapis.com"/>
<add source="ajax.aspnetcdn.com"/>
<add source="cdn.rawgit.com"/>
</script-src>
<style-src unsafeInline="true" self="true" />
<img-src self="true"/>
<object-src none="true" />
<media-src none="true" />
<frame-src none="true" />
<font-src self="true" />
<form-action self="true"/>
<connect-src self="true" />
<frame-ancestors none="true" />
<report-uri enableBuiltinHandler="true"/>
</content-Security-Policy>
</securityHttpHeaders>

This post has a good intro to CSP and MVC:
An introduction to Content Security Policy – Mike West

You may still see a violation from the Visual Studio Browser Link as this uses an arbitrary port number in development.

I hope this inspired you to start using Exceptionless for logging purposes. We use it when introducing CSP and setting the baseline for our web application and found it very useful. After the baseline was set we also get to see how much adware/spyware there is out there.

There is a lot more that you can use Nwebsec to strengthen the security of your website so if you are not using this already I recommend to take a deeper look into this library.

Reference links:

– Exceptionless
– NWebsec docs – Configuring CSP
NWebsec Demo
An introduction to Content Security Policy – Mike West
CSP – Mozilla Developer Network
– Content Security Policy (CSP) for ASP.NET MVC – Muhammad Rehan Saeed

Entity Framework and Performance Improvements

Lately I have been looking into some performance problems in one of our projects and it turns out this was closely related to poor performance in Entity Framework (EF) in materalization of list objects.

Use AsNoTracking() attribute

on queries that return read-only data, typically for calculations or lists. By default EF runs WITH change tracking and when your model is fairly big and has many associations this will give a severe performance degrade. It doesn’t even matter if these associations is included in your query!! The AsNoTracking() extension instruct EF to not add the data to context for change tracking and is really powerful.

For demo purposes I used the RawBencher by Frans Bouma (https://github.com/FransBouma/RawDataAccessBencher) and loaded all 31465 records in SalesOrderHeader table.

All performance number below is the average of 10 runs with fastest and slowest time removed. The tests were run using

· WS2008 with SqlServer 2008
· Win7 – Citrix Virtual machine as client
· Running RawBencher – compiled in Release mode without Debugger from VS 2013.

With change tracking:

Code:

using(var ctx = new AWDataContext())
{
    return ctx.SalesOrderHeaders.ToList();
}
Materialization (load) time:

EF 6.0.1:

#1: Entity Framework v6.0.0.0 (v6.0.21010.0) : 4.370,00ms.
#2: Entity Framework v6.0.0.0 (v6.0.21010.0) : 4.387,88ms.

EF 6.0.2:

#1: Entity Framework v6.0.0.0 (v6.0.21211.0) : 4.712,38ms.
#2: Entity Framework v6.0.0.0 (v6.0.21211.0) : 4.683,13ms.

EF 6.1.0-alpha1:

#1: Entity Framework v6.0.0.0 (v6.1.21218.0) : 4.524,00ms.
#2: Entity Framework v6.0.0.0 (v6.1.21218.0) : 4.577,50ms.

LINQ-TO-SQL

#1: Linq to Sql v4.0.0.0 (v4.0.30319.18408)  : 721,00ms.
#2: Linq to Sql v4.0.0.0 (v4.0.30319.18408)  : 717,25ms.

 

Without change tracking:

code:

EF:

using(var ctx = new AWDataContext())
{
    return ctx.SalesOrderHeaders.AsNoTracking().ToList();
}
LINQ to SQL
using(var ctx = new L2SBencherDataContext())
{
    ctx.ObjectTrackingEnabled = false;
    return ctx.SalesOrderHeaders.ToList();
}

Materialization (load) time:

EF 6.0.1:

#1: Entity Framework v6.0.0.0 (v6.0.21010.0) : 659,63ms.
#2: Entity Framework v6.0.0.0 (v6.0.21010.0) : 658,38ms.   

EF 6.0.2:

#1: Entity Framework v6.0.0.0 (v6.0.21211.0) : 461,88ms.
#2: Entity Framework v6.0.0.0 (v6.0.21211.0) : 458,13ms.

EF 6.1.0-alpha1:

#1: Entity Framework v6.0.0.0 (v6.1.21218.0) : 446,38ms.
#2: Entity Framework v6.0.0.0 (v6.1.21218.0) : 452,25ms

LINQ-TO-SQL:

#1: Linq to Sql v4.0.0.0 (v4.0.30319.18408)  : 555,88ms.
#2: Linq to Sql v4.0.0.0 (v4.0.30319.18408)  : 558,63ms.

Worth noting here is that

  • the materialization time with no tracking queries keeps getting improved with the newest versions of EF but the tracking versions is NOT improving.
  • LINQ-To-SQL with change tracking is WAY faster than EF with change tracking on a model with many associations.
  • You can also turn off object tracking with LINQ-TO-SQL
  • You may also see spikes of CPU usage on 100% when running these queries.

You should also be aware that the additional time affects the CPU usage on your client computer, the web server, and not the database – and depending on your transaction isolation level you may be holding transactions and locks on your table during the slower loading and cause bottlenecks in your database too.  See paragraph below.

So what should you do? The challenge is that these entities may end up inn code that expects them to have change tracking – as there is no obvious naming conventions that implies that these are readonly objects. After all – it is regular entities – just without change tracking.

When designing your data access and API you should pay close attention to how you handle read-only objects – typically for lists.
And at least for now – you should apply the AsNoTracking() on sets that load many objects into memory to enhance performance.
If you have a separate business objects layer and only use EF for Data Access then apply add AsNoTracking to fetches into readonly lists.

The AsNoTracking extension was introduced in EF 4.1.

The issue of slow materialization is a know issue in EF – see https://entityframework.codeplex.com/workitem/1829
“The time it takes EF to materialize data is a function of the amount of associations (foreign keys) the entire model has, even when such associations are not part of the query in question.”

If you would like to have this addressed please add your vote on this issue.

Consider using NOLOCK or SNAPSHOT ISOLATION LEVELto avoid transactions on your select statements

The most important part here is that you do not want selects (retrieving) data from the database to block writing of data. With hand coded SQL we would typically use the with (nolock) statement but with Linq 2 Sql and Enitity Framework there are other ways to accomplish this.

Scott Hanselman has a nice post on this issue

http://www.hanselman.com/blog/GettingLINQToSQLAndLINQToEntitiesToUseNOLOCK.aspx

Happy coding!

How to install CSLA 4.5 and run ProjectTracker sample

This article shows you how to download and install CSLA and compile and run the ProjectTracker sample.

Download and Install

Go to http://www.cslanet.com/Download.html and download the latest installer.

Run the installer with default settings. Take note of the install folder and open this folder in Windows Explorer.

In this folder you will find CSLA.zip.

Right click on file and select “Extract all” and extract to f.ex C:\Projects\CSLA .NET\<versionnumber>\Csla.
I downloaded version 4.5.491 so my path is C:\Projects\CSLA .NET\4.5.491\Csla)

ProjectTracker

Open the Samples\Net\cs\ProjectTracker\ProjectTracker.sln solution file in Visual Studio 2013. (you may get upgrade warnings).

Build the solution. This status line should now “Build Succeeded”.

Then open the solution property page and set startup projects:

image

You must set multiple startup projects and always select WcfAppServer plus the UI project(s) of your choice.
The WcfAppServer hosts the data access (N-Tier) deployment is always required for all UI projects.

I selected the WfUI (Windows Forms) and then hit run.
In the login dialog use manager/manager  or admin/admin and you are logged in to ProjectTracker.

Next change the startup project to WpfUI + WcfAppServer and you will be running the WPF sample.

So that’s all, folks. Enjoy and explore CSLA.

CSLA-Validation–the new NuGet package in CSLA .NET 4.5.40

Or how to use the Csla 3.6 to 3.8 style rules in Csla 4.5.40

Introduction

Many developers find the move from Csla .NET 3.x to Csla .NET 4.x to be a big hurdle. Especially when it comes to the new rule engine in Csla .NET 4.x. In this article I will take the ProjectTracker.Library project from the Csla .NET 3.6 samples and upgrade to .NET 4.5 with Csla 4.5.40 NuGet packages.

The 3.x rule engine used static methods and the new 4.x rule engine uses classes. The benefit of using classes is inheritance, testability and allow the framework to provide base classes that get to the inner properties and methods of the business objects. The new rule engine also provides the same base classes and signature for both sync and async rules.

Having done several migrations with different approach to how to do the migration (starting with having the “old” Csla in a private renamed assembly and namespace) I finally came up with the idea of how to support most of the older style rules within the new rule engine. As it turns out this was quite simple to do and only required 1 to 3 days of work to get the application up and running on the new version and then the entire team could start to work on migration of the rules.

In order to do a full migration there is several breaking changes that you must handle. These will be covered in a separate blog posts, so for now let us focus on getting the business library with old style rules to work. And keep in mind – your target platform may be . NET 4, .NET 4.5, Silverlight 5, Windows Store or MonoAndroid.

Important breaking changes for rules:

  • There can be only one Authorization rule per AuthorizationAction and IMethodInfo (or property) where the old rule engine allowed multiple rules and both Allow/Deny combinations.
  • AddAuthorizationRules() is now deprecated and these authorization rules should be moved to AddBusinesRules().
  • Rewrite authorization rules to use the new IsInRole() or IsNotInRole() rules.
  • Rewrite asynchronous rules to use the new rule classes.

The changes the we must do is:

  1. Remove references to old Csla assembly and add Csla references from NuGet
  2. Add intermediate base classes if not already present in solution
  3. Add usings to business objects
  4. Change the signature of CanReadProperty, CanWriteProperty and CanExecuteMethod
  5. pdate AuthorizationRules to new IsInRole/IsNotInRole rules
  6. Add usings to business objects
  7. Change the signature of CanReadProperty, CanWriteProperty and CanExecuteMethod pdate AuthorizationRules to new IsInRole/IsNotInRole rules

Step 1: Remove references to old Csla assembly and add Csla references from NuGet

Remove the references to Csla.dll and update target framework for this sample to .NET 4.5.

Then install nuget package csla-updateValitation in ProjectTracker.Library

See: https://www.nuget.org/packages/CSLA-UpdateValidation

For my own preference I changed the solution so that only

  • ProjectTracker.DalEf
  • ProjectTracker.DalLinq
  • ProjectTracker.Library

will be compiled.

Step 2: Add intermediate base classes for BusinessBase and ReadOnlyBase

The first step you must do is to add your own intermediate base classes for Csla business objects, if not already present in you solution. Make sure that these classes inherit from Csla.Validation base classes for BusinessBase and ReadOnlyBase. When your app is fully migrated to Csla 4.x you should change the inheritance to use the standard Csla base classes and remove the reference to Csla.Validation.

[Serializable()]
public class MyBusinessBase<T> : Csla.Validation.BusinessBase<T> where T:MyBusinessBase<T>
  {
    protected override void AddBusinessRules()
    {
      AddAuthorizationRules();
      base.AddBusinessRules();
    }

    [Obsolete("Move code to AddBusinessRules for CSLA 4.x")]
    protected virtual void AddAuthorizationRules()
    {
    }
  }
[Serializable()]
public class MyReadOnlyBase<T> : Csla.Validation.ReadOnlyBase<T> where T:MyReadOnlyBase<T>
  {
    protected override void AddBusinessRules()
    {
      AddAuthorizationRules();
      base.AddBusinessRules();
    }

    [Obsolete("Move code to AddBusinessRules for CSLA 4.x")]
    protected virtual void AddAuthorizationRules()
    {
    }
  }

And make sure that all you business objects and readonly objects inherit from these base classes.

Also take not of how you can add support for AddAuthorizationRules in your own intermediate base class.

Step 3 Add usings to business objects

You should now take the opportunity to add these usings to business objects

using Csla.Validation;
using Csla.Rules;

Step 4: Change the signature of CanReadProperty, CanWriteProperty and CanExecuteMethod

to use the overload that accepts a PropertyInfo or MethodInfo as parameter

Here shown for CanExecuteMethod()

From:

public Resource GetResource()
    {
      CanExecuteMethod("GetResource", true);
      return Resource.GetResource(GetProperty(ResourceIdProperty));
    }

To:

private static MethodInfo GetResourceMethod = RegisterMethod(p => p.GetResource());
    public Resource GetResource()
    {
      CanExecuteMethod(GetResourceMethod, true);
      return Resource.GetResource(GetProperty(ResourceIdProperty));
    }

and take note of how you can get rid of the string constant.

Step 5: Update AuthorizationRules to new IsInRole/IsNotInRole rules

and keep in mind that you can only have one rule per AuthorizationAction and methodinfo.

From:

protected static void AddObjectAuthorizationRules()
    {
      AuthorizationRules.AllowCreate(typeof(Project), "ProjectManager");
      AuthorizationRules.AllowEdit(typeof(Project), "ProjectManager");
      AuthorizationRules.AllowDelete(typeof(Project), "ProjectManager");
      AuthorizationRules.AllowDelete(typeof(Project), "Administrator");
    }

To:

protected static void AddObjectAuthorizationRules()
    {
      BusinessRules.AddRule(typeof(Project), new IsInRole(AuthorizationActions.CreateObject, "ProjectManager"));
      BusinessRules.AddRule(typeof(Project), new IsInRole(AuthorizationActions.EditObject, "ProjectManager"));
      BusinessRules.AddRule(typeof(Project), new IsInRole(AuthorizationActions.CreateObject,"ProjectManager", "Administrator"));
    }

and take note of how we can supply multiple rows on the last rule.

Then do the same updates for Resources.cs

Step 6: Update async rules to use new style classes

See this blog post by Rocky Lhotka on how to write an async business rule

http://www.lhotka.net/weblog/ImplementingAnAsyncRuleInCSLA4Version45.aspx

Step 7: Misc changes for Csla 4.5

Next there is a couple of changes that must be done.

CriteriaBase now has a generic constraint so code must be updated to:

    [Serializable()]
    private class ExistsCommand : CommandBase<ExistsCommand>
And the Windows Forms (BindingList) based classes has been renamed to BusinessBindingListBase and ReadOnlyBindingListBase.
The Csla 4.5 list classes now inherit from ObservableCollection and has slightly different signature on AddNew and events. 
So change from:
BusinssListBase  ==> BusinessBindingListBase

ReadOnlyListBase ==> ReadOnlyBindingListBase

Summary

So this showed how to make the old ProjectTracker.Library compile with Csla .NET 4.5 in .NET 4.5 and use (most) the old style rules. The tecnhique behind this is a set of extension methods that provide the “old” methods and wraps the static methods with a lamda rules. If you want to look at the actual code, the source code can be downloaded from github: https://github.com/MarimerLLC/csla

There is still a number of changes to be done in order to make the entire solution run and this will be a subject for more blog posts.

Hope this encourages you to start migration of your application to Csla .NET 4.5

LinqPad: DumpAsInsert extension

I use LinqPad a LOT and love this tool.

Sometimes we need to export data to stage to another computer/database or just for safekeeping and I wrote a simple DumpAsInsert extension to dump the result of a Linq query as insert statements.

To install the extension add the code to MyExtensions and press F5 or select the Execute button. This will make LinqPad compile the extensions to a custom assembly and automatically load the methods. Beware that you must select C# Statements as language. C# Expression automatically adds .Dump()  to your statement.

Usage of the extension:

var names = new[]
  {
     new {Name= "Tom"}, 
     new {Name= "Dick"}, 
     new {Name= "Harry"}, 
   }.AsEnumerable();
names.DumpAsInsert("test");

gives this output:

INSERT INTO [test] ([Name])
VALUES ('Tom');
INSERT INTO [test] ([Name])
VALUES ('Dick');
INSERT INTO [test] ([Name])
VALUES ('Harry');

Using the Northwind sample database we can query tables like this:

Categories.Take(2).DumpAsInsert("Category");

and get the following result:

INSERT INTO [Category] ([CategoryID], [CategoryName], [Description], [Picture])
VALUES (1,'Beverages','Soft drinks, coffees, teas, beers, and ales',NULL);
INSERT INTO [Category] ([CategoryID], [CategoryName], [Description], [Picture])
VALUES (2,'Condiments','Sweet and savory sauces, relishes, spreads, and seasonings',NULL);

Note that this includes the identity field so we need to specify fields to exclude:

Categories.Take(2).DumpAsInsert("Category", "CategoryID");

These extensions will also support projections to anonymous types like this

Categories.Take(2).Select(p => new {p.CategoryName, p.Description}).DumpAsInsert("Category");

and the result is

INSERT INTO [Category] ([CategoryName], [Description])
VALUES ('Beverages','Soft drinks, coffees, teas, beers, and ales');
INSERT INTO [Category] ([CategoryName], [Description])
VALUES ('Condiments','Sweet and savory sauces, relishes, spreads, and seasonings');

The SQL generator does not support Image or Xml data types. These will only be generated as NULL.

You should be able to add support for more datatypes into the SQL – I didn’t need it for my usage and didn’t have time to implement these.

Play around with the extension and feel free to update the code.

Here is the code to add in My Extensions:

void Main()
{
        // Write code to test your extensions here. Press F5 to compile and run.
        var names = new[] {new {Name= "Tom"}, new {Name= "Dick"}, new {Name= "Harry"}, new {Name= "Mary"}, new {Name= "Jay"} }.AsEnumerable();
        names.DumpAsInsert("test");
}

// Define other methods and classes here
public static class MyExtensions
{
   // Write custom extension methods here. They will be available to all queries.
   public static void DumpAsInsert<T>(this IEnumerable<T> data) where T:class
   {
       DumpAsInsert(data, null);
   }

   public static void DumpAsInsert<T>(this IEnumerable<T> data, string tableName) where T:class
   {
        DumpAsInsert(data, tableName, string.Empty);      
   }

   public static void DumpAsInsert<T>(this IEnumerable<T> data, string tableName, string hideColumn) where T:class
   {
        DumpAsInsert(data, tableName, new string[] { hideColumn});
   }

   public static void  DumpAsInsert<T>(this IEnumerable<T> data, string tableName, string[] hideColumns) where T:class
   {
        var firstItem = data.FirstOrDefault();
        if (firstItem == null) string.Empty.Dump();
        if (hideColumns == null) hideColumns = new [] { string.Empty };

        if (tableName == null)
            tableName = firstItem.GetType().Name;

        var formatProvider = GetSqlTextFormatInfo();
        var result = new StringBuilder();
        var members = new List<MemberInfo>();
        if (CheckIfAnonymousType(firstItem.GetType()))
            members.AddRange(firstItem.GetType().GetProperties().Where(p => !hideColumns.Contains(p.Name)));
        else
            members.AddRange(firstItem.GetType().GetFields().Where(p => !hideColumns.Contains(p.Name)));

        var stmt = string.Format("INSERT INTO [{0}] ({1})\nVALUES (", tableName, string.Join(", ", members.Select(p => string.Format("[{0}]", p.Name)).ToArray()));

        foreach (var item in data)
        {
            result.Append(stmt);

            var first = true;
            foreach (var col in members)
            {
                if (!first) result.Append(",");
                first = false;
                result.Append(GetFieldValue(formatProvider, col, item));
            }
            result.AppendLine(");");
        }

        result.ToString().Dump();
   }

   public static string GetFieldValue(IFormatProvider formatProvider, MemberInfo field, object row)
   {
        object value;
        Type fieldType;
        if (field is FieldInfo)
        {
            value = ((FieldInfo)field).GetValue(row);
            fieldType = ((FieldInfo) field).FieldType;
        }
        else
        {
            value = ((PropertyInfo)field).GetValue(row, null);
            fieldType = ((PropertyInfo)field).PropertyType;
        }
        if (value == null) return "NULL";

        if (fieldType == typeof(bool))
        return (bool) value ? "1" : "0";

        if (fieldType == typeof(System.String))
            return "'" + value.ToString().Replace("'", "''") + "'";
        else if (fieldType == typeof(DateTime) || fieldType == typeof(DateTime?))
            return "convert(datetime, '" + ((DateTime) value).ToString("yyyy-MM-dd HH:mm:ssss.fffffff") + "', 120)";
        else if (fieldType == typeof(System.Data.Linq.Binary))
            return "NULL";
        else if (fieldType == typeof(XElement))
            return "'" + ((XElement)value).Value.Replace("'", "''") + "'";
        else
            return string.Format(formatProvider, "{0}", value);
   }

    private static System.Globalization.NumberFormatInfo GetSqlTextFormatInfo() 
   {
        return new System.Globalization.NumberFormatInfo()
    {
        CurrencyDecimalSeparator = ".",
        CurrencyGroupSeparator = string.Empty,
        NumberDecimalSeparator = ".",
        NumberGroupSeparator = string.Empty,
        PercentDecimalSeparator = ".",
        PercentGroupSeparator = string.Empty,
    };
    }

    private static bool CheckIfAnonymousType(Type type)
    {
        if (type == null)
            throw new ArgumentNullException("type");

        // HACK: The only way to detect anonymous types right now.
        return Attribute.IsDefined(type, typeof(System.Runtime.CompilerServices.CompilerGeneratedAttribute), false)
            && type.IsGenericType && type.Name.Contains("AnonymousType")
            && (type.Name.StartsWith("<>") || type.Name.StartsWith("VB$"))
            && (type.Attributes & TypeAttributes.NotPublic) == TypeAttributes.NotPublic;
    }
}

CSLA 4.5 RuleEngine update

The rule engine for CSLA 4.5.10 has been updated with a new advanced mode.

By default BusinessRules is run as follows when a property is edited by a user:

  1. Call CheckRulesForPropertymethod with parameters Property and Cascade=true
    1. Clear all BrukenRules from the Property
    1. Get list of BusinessRules for Property
    2. Call RunRuleswith parameters RulesForProperty and Cascade=true to run rules for this property.
      1. ForEach rule in RulesForProperty
        1. Call Rule.Execute method
      2. Update property values from context.OuputProperties collection
        1. if value was changed add Property to DirtyProperties
      3. Return RuleResult –  a list of AffectedProperties and lst of DirtyProperties
    3. If  (cascade)
      1. For the aggreate of (context.AffectedProperties from the rules checked) and
        (properties that have BusinessRules with PrimaryProperty as InputProperty)
        take distinct PropertyInfo

        1. call CheckRulesForProperty with parameters Property and Cascade=false.

But then the rule engine stops is rechecking. So the rule engine will only cascade down to the next level and then stop rechecking rules.
The consequence of having properties changed by f.ex. OutputProperties in the second run when Cacade=false is that the Property and RuleResults may get out of sync and a field that has a valid value may be invalid and vice versa.

So this may be a serious problem unless you have taken care of this in designing you business rules. In some financial applications you may also have a lot of calculations that should cascade for all “dirty” properties to rerun rule for as long as properties is changed – much like Excel does in a spreadsheet for calculating formulas  and cascade calculation for as long as cells is updated.

So to provide this level of functionality the RuleEngine for CSLA 4.5.10 has been updated with a new configuration flag:
<bo>.BusinessRules.CascadeOnDirtyProperties.
This is an instance level flag that must be set in the Create/Fetch method of the <bo>.

When CascadeOnDirtyProperties is true the rule engine run rules as follows:

  1. Call CheckRulesForPropertymethod with parameters Property and Cascade=true
    1. Clear all BrukenRules from the Property
    1. Get list of BusinessRules for Property
    2. Call RunRuleswith parameters RulesForProperty and Cascade=true to run rules for this property.
      1. ForEach rule in RulesForProperty
        1. Call Rule.Execute method
      2. Update property values from context.OuputProperties collection
        1. if value was changed add Property to DirtyProperties
      3. Return RuleResult –  a list of AffectedProperties and lst of DirtyProperties
    3. If (cascade)
      1. For the aggreate of (context.AffectedProperties from the rules checked) and
        (properties that have BusinessRules with PrimaryProperty as InputProperty)
        take distinct PropertyInfo

        1. call CheckRulesForProperty with parameters Property and Cascade=true if property is in DirtyProperties list .

Of course – unless your BusinessRules is carefully crafted you may end up with an infinite loop and eventually a StackOverflow exception.
But if you really need to make sure that BusinessRules is actually rerun for as long as property values is updated from context.OutputPropertyValues then this will be a nice extension to the rule engine.

The above sequence diagrams is simplified when it comes to asynchronous rules but the end result is the same behavior for both synchronous and asynchronous rules.

Unit Test CSLA 4 BusinessRules

Unit tests in CSLA 4 is now classes and most developers want to create unit tests to verify the correct behavior and result.
In this article I will show you a suggested way to create these tests.

Basic rule knowledge

Let’s start with how rules is registered in CSLA:

  1. <bo>.AddBusinessRules is only called once and register ALL rules for that object type.
  2. A rule instance should be viewed as a Singelton or Shared object. You cannot store any value elated to the execution (data values) in member variables.

The execution of the rule follow these steps:

  1. The RuleEngine will create an instance of a RuleContext with the actual rule
  2. and if InputProperties have entries copy values from BO to <context>.InputPropertyValues
  3. set <context>.Target property to the <bo> instance (with exception if IsAsync = true and ProvideTargetWhenAsync = false).
  4. call the <rule>.Execute method with <context> as parameter.
  5. When rule has completed call a CompleteHandler for both synchronous and async rules.

The rule engine will call the completed handler automatically for syncronous rules but for async rules your code is responsible for calling <context>.Complete in ALL execution paths.
The <rule>.Execute method is always called inline and when IsAsync = true the assumption is that your code in Execute will do an asyncronous call an in order to notify the rule engine when the rule is completed your code must call <rule>.Complete.

Then take a look at the <rule>.Execute method.

A: Your rule does NOT use <context>.Target and has no relationship to the actual BO.
These rules only interact with the RuleContext and use <context>.InputPropertyValues for input values and may call <context>.AddOutValue or context.AddXYZResult.

The output from these rules is in

  • <context>.Results
  • <context>.OuputPropertyValues

B: Your uses uses <context>.Target and the support methods in BusinessRule base class for ReadProperty or LoadProperty.
These rules interact with the BO and uses methods in BusinessRule base class that expects an actual CSLA BO to be present (implements Csla.Core.IManageProperties).

The output of a rule can be

The output from these rules is in

  • <context>.Results
  • <context>.OuputPropertyValues
  • directly updated properties in the context.Target or child object from calling <rule>.LoadProperty

RuleContext support for result

context.Results is a list of RuleResult that yon can add test for. <context>.AddXYZResult will all add a RuleResult to context.Results.
context.OutputPropertyValues is a dictionary of IPropertyInfo and values to be set in the bo.

So we should be able to create unit tests for both

  • synchreonous rules and async rules
  • rules that only interacts with the rule context.
  • rules that interacts with the context.Target

Support methods in CSLA

RuleContext has a constructor specifically designed for use in unit tests:

public RuleContext(Action<RuleContext> completeHandler, IBusinessRule rule, object target, Dictionary<Csla.Core.IPropertyInfo, object> inputPropertyValues)

that allows the test code to specify the

  • completeHandler for callback when rule has completed.
  • the actual business ruleyou want to test
  • the target object (may be null if your rule does not use the context.Target)
  • a dictionary of input property values

Support class BusinessRuleTest.cs in RuleTutorial samples download

adds more support for tests and will allow you to write the exact same test code whether the rule is syncronous or not.
This is done by adding a CompleteEeventHandler that will wait for up to 3 seconds on an async rule before calling <context>.Complete.

public void InitializeTest(IBusinessRule rule, object target)
public void ExecuteRule()
public void ExecuteRule(Dictionary<IPropertyInfo, object> inputPropertyValues)
public T GetOutputPropertyValue<T>(PropertyInfo<T> propertyInfo)

The ExeuteRule method will copy values from target object to input property values and then call rule.Execute
The ExecuteRule with InputPropertyValues dictionary will just set thes into the context and call rule.Execute.

Then to the unit tests

My proposed solution for creating unit tests uses the constructor in RuleContext, the BusinessRuleTest base class and creating fake business objects for the tests.

I like the testing approach from Phil Haack and Brad Wilson so these sample tests use a separate class for each method.
Read more here: http://haacked.com/archive/2012/01/01/structuring-unit-tests.aspx

Testing a ToUpper transformation rule that makes sure a property is always uppercase.

This rule only interacts with the <context>.InputPropertyValues and <context>.OuputPropertyValues

My fake root object:

  [Serializable]
  public class RootFake : BusinessBase<RootFake>
  {
    public static readonly PropertyInfo<int> CustomerIdProperty = RegisterProperty<int>(c => c.CustomerId);
    public int CustomerId
    {
      get { return GetProperty(CustomerIdProperty); }
      set { SetProperty(CustomerIdProperty, value); }
    }

    public static readonly PropertyInfo<string> NameProperty = RegisterProperty<string>(c => c.Name);
    public string Name
    {
      get { return GetProperty(NameProperty); }
      set { SetProperty(NameProperty, value); }
    }
  }

The the test class for constructor:

    [TestClass()]
    public class TheCtor
    {
      private IBusinessRule Rule;
      [TestInitialize]
      public void InitTests()
      {
        Rule = new ToUpper(RootFake.NameProperty);
      }

      [TestMethod]
      public void IsSync()
      {
        Assert.IsFalse(Rule.IsAsync);
      }

      [TestMethod]
      public void HasPrimaryProperty()
      {
        Assert.IsNotNull(Rule.PrimaryProperty);
        Assert.AreEqual(RootFake.NameProperty, Rule.PrimaryProperty);
      }

      [TestMethod]
      public void HasInputProperties()
      {
        Assert.IsTrue(Rule.InputProperties.Contains(RootFake.NameProperty));
      }

      [TestMethod]
      public void HasAffectedProperties()
      {
        Assert.IsTrue(Rule.AffectedProperties.Contains(RootFake.NameProperty));
      }
    }

Then testing the Execute method with supplied InputPropertyValues:

    [TestClass()]
    public class TheExecuteMethod : BusinessRuleTest
    {
      [TestInitialize]
      public void InitTests()
      {
        var rule = new ToUpper(RootFake.NameProperty);
        InitializeTest(rule, null);
      }

      [TestMethod]
      public void MustSetOutputPropertyToUpper()
      {
        var expected = "CSLA ROCKS";
        ExecuteRule(new Dictionary<IPropertyInfo, object>() {{RootFake.NameProperty, "csla rocks"}});
        Assert.IsTrue(GetOutputPropertyValue(RootFake.NameProperty) == expected);
      }
    }

Then testing the Execute method with a fake object to supply the property value:

    [TestClass()]
    public class TheExecuteMethodAlt : BusinessRuleTest
    {
      [TestInitialize]
      public void InitTests()
      {
        var rule = new ToUpper(RootFake.NameProperty);
        var root = new RootFake();
        root.Name = "csla rocks";
        InitializeTest(rule, root);
      }

      [TestMethod]
      public void MustSetOutputPropertyToUpper()
      {
        var expected = "CSLA ROCKS";
        ExecuteRule();
        Assert.IsTrue(GetOutputPropertyValue(RootFake.NameProperty) == expected);
      }
    }

Summary

I hope this gave you a better understanding of how Business Rules work and what to test.

You will find more unit test samples in the Net\cs\RuleTutorial sample available at http://www.lhotka.net/cslanet/download.aspx and if you have questions please post on the CSLA,NET Forum at http://forums.lhotka.net