Jonny Bekkum's Blog

My ramblings on .Net and in particular CSLA.NET business object framework

Entity Framework and Performance Improvements

Posted by jonnybekkum on 27/12/2013

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!

Posted in Entity Framework | Leave a Comment »

How to install CSLA 4.5 and run ProjectTracker sample

Posted by jonnybekkum on 10/11/2013

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.

Posted in Uncategorized | Leave a Comment »

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

Posted by jonnybekkum on 07/10/2013

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

Posted in Business Rules, Csla | 4 Comments »

LinqPad: DumpAsInsert extension

Posted by jonnybekkum on 02/03/2013

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;
    }
}

Posted in LinqPad | Tagged: , | Leave a Comment »

CSLA 4.5 RuleEngine update

Posted by jonnybekkum on 07/11/2012

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.

Posted in Business Rules, Csla | Leave a Comment »

Unit Test CSLA 4 BusinessRules

Posted by jonnybekkum on 29/04/2012

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

Posted in .Net, Business Rules, Csla, Unit Test, Visual Studio 2010 | Tagged: , , | 3 Comments »

How to: C# Connect to dynamic event

Posted by jonnybekkum on 19/02/2012

In our WCF services we have used the DbProviderFactories and the default interface so that our WCF services has no direct reference to the actual database provider or version. Recently we needed to connect to the InfoMessage event on the TeraData database provider without knowing the actual type. We wanted to still remain on the path with no reference to the driver and this was not straight forward. With the inspiration fro0m http://stackoverflow.com/questions/45779/c-sharp-dynamic-event-subscription we adapted a solution with Expression trees and in case you need to dynamically hook into an event, here is how we did it:

 1: namespace DynamicEvent
 2: {
 3:   internal class Program
 4:   {
 5:     private static void Main(string[] args)
 6:     {
 7:       var connectionString = "Your connection string here!";
 8:       var factory = DbProviderFactories.GetFactory("Teradata.Client.Provider");
 9:       var conn = factory.CreateConnection();
 10:       conn.ConnectionString = connectionString;
 11:       conn.Open();
 12:  
 13:       EventInfo ev = conn.GetType().GetEvent("InfoMessage");
 14:       var handler = EventProxy.Create(ev, HandleInfoMessage);
 15:       // add event handler 
 16:       ev.AddEventHandler(conn, handler);
 17:       try
 18:       {
 19:         // run sql call
 20:  
 21:  
 22:       }
 23:       finally
 24:       {
 25:         // unhook event handler 
 26:         ev.RemoveEventHandler(conn, handler);
 27:       }
 28:     }
 29:  
 30:     static void HandleInfoMessage(object obj, EventArgs eventArgs)
 31:     {
 32:       // Use Fasterflect, http://fasterflect.codeplex.com to dynamically access properties on the EventArgs object.
 33:       Console.WriteLine("InfoMessage {0}:{1}", obj.GetType().ToString(), eventArgs.GetType().ToString());
 34:     }
 35:   }
 36:  
 37:   public class EventProxy
 38:   {
 39:     public static Delegate Create(EventInfo evt, Action<object, EventArgs> d)
 40:     {
 41:       var handlerType = evt.EventHandlerType;
 42:       var eventParams = handlerType.GetMethod("Invoke").GetParameters();
 43:  
 44:       var parameters = eventParams.Select(p => Expression.Parameter(p.ParameterType, "x")).ToArray();
 45:       var body = Expression.Call(Expression.Constant(d), d.GetType().GetMethod("Invoke"), parameters[0], parameters[1]);
 46:       var lambda = Expression.Lambda(body, parameters.ToArray());
 47:       return Delegate.CreateDelegate(handlerType, lambda.Compile(), "Invoke", false);
 48:     }
 49:   }
 50: }

You could use this EventProxy and technique to hook into any event where the signature is (object, EventArgs) or any other class that descends from EventArgs.

Posted in Uncategorized | Leave a Comment »

CSLA 4.2 Rules update

Posted by jonnybekkum on 29/08/2011

The CSLA rule engine had a major change when moving from 3.8.x to 4.0. The major change being that rules is now classes and inheritable whereas the earlier rules were static methods.

For the upcoming CSLA 4.2 release we have made a number of bug fixes plus added some new features that you should be aware of.

1. InputProperties is now also considered Dependencies just as AffectedProperties.

Take for example the validation of StartDate and EndDate where StartDate must be before EndDate.

In CSLA 4.0/4.1 you would have to add:

 1: BusinessRules.AddRule(new LessThan(StartDateProperty, EndDateProperty));
 2: BusinessRules.AddRule(new Dependency(EndDateProperty, StartDateProperty));
 3:  
 4: BusinessRules.AddRule(new GreaterThan(EndDateProperty, StartDateProperty));
 5: BusinessRules.AddRule(new Dependency(StartDateProperty, EndDateProperty));

In order to make the validation rules execute as expected, ie,  StartDateProperty must be validated when StartDate is changed or EndDate is changed (Dependency) and EndDate vice versa.
It’s very easy to miss the dependencies so in CSLA 4.2 you will only need:

 1:  BusinessRules.AddRule(new LessThan(StartDateProperty, EndDateProperty));
 2:  BusinessRules.AddRule(new GreaterThan(EndDateProperty, StartDateProperty));

And both rules will be executed when either of the property values is changed.

Having Dependency rules will NOT break your code so the code sample with Dependency rules will run exactly the same way as the last sample.

2. Provide overloads to set your own message text

When you register a rule you may now also specify either a constant message string (MessageText) or a lambda expression (MessageDelegate) to fetch a string from a resource file.
Using the lambda version you can support translation/localization of the message.

NOTE: The formatting function is supported in just the same way as the default messages.

Using a constant string:

 1:       BusinessRules.AddRule(new Required(NameProperty){MessageText = "Customer must have name"});
 2:       BusinessRules.AddRule(new MaxLength(NameProperty, 50){Priority = 1, MessageText = "{0} cannot be longer than {1} chars"});
 3:       BusinessRules.AddRule(new MinValue<int>(Num1Property, 5) { MessageText = "Num1 must be larger than or equal to {1}" });

Using a lambda to support localization:

 1:       BusinessRules.AddRule(new Required(NameProperty){MessageDelegate = () => Resources.NameRequired});
 2:       BusinessRules.AddRule(new MaxLength(NameProperty, 50){Priority = 1, MessageDelegate = () => Resources.NameMaxLength});
 3:       BusinessRules.AddRule(new MinValue<int>(Num1Property, 5) { MessageDelegate = () => Resources.Num1MinValue });

This properties/delegate is implemented in the base classes, your own rules must make sure to override the GetMessage method to provide the default (and preferably) localizable message text.

 1:     protected override string GetMessage()
 2:     {
 3:       return HasMessageDelegate ? base.GetMessage() : Resources.AnyRequiredRule;
 4:     }

3. Rules for affected properties on an Async rule is automatically rerun and UI notified when context.Complete is called.

This was missing pre 4.2 so when an async rule would lookup information in a database and set a number of
properties in the <bo> the rules for those properties were not rechecked. This would cause the field content
and error/warn/info messages to get out of sync.

4. Rules can now specify conditions for when the rule is not allowed to run.

Typical usage is for any rule that you will only allow to run when the user edits a value and f.ex do an async lookup to a database.
By default and to not brake any existing code, rules will run without restrictions.

Flag Property rule Object rule
CanRunInCheckRules         Yes           Yes
CanRunAsAffectedProperty         Yes           No
CanRunOnServer         Yes           No

Object level rules is executed when either <bo>.CheckObjectRules() or <bo>.CheckRules() is called.

PropertyRules is executed when either <bo>.PropertyHasChanged(property) or <bo>.CheckRules() is called.
And PropertyHasChanged is implicitly called when SetProperty changes a value so most developers will never call PropertyHasChanged.

CanRunInCheckRules when false means that the rule will not be executed when <bo>.CheckRules() is called.

CanRunAsAffectedProperty when false means that this property rule will not run when this property is an affected property on another property rule.

CanRunOnServer when false means that this property rule will not run on the “logical” server side, by all practical means in Data Access Layer.
So when CheckRules is called from Data Access do not execute this rule when set to false.

5. New rule base classes

Two new base classes is introduced:

Class name Description
ObjectRule For an object level rule. Will always have PrimaryProperty = null.
PropertyRule For a property level rule. Must always have a PrimaryProperty.

 

All Property validation rules shipped in CommonRules have this inheritance hierarchy:

BusinessRule
– PropertyRule
– CommonBusinessRule
– Actual Rule

So if your own rules inherit from CommonBusinessRule then you will automatically inherit from PropertyRule.

To make this easier for your developers consider adding your own base classes like:

 1:   public abstract class SyncLookupRule : CommonBusinessRule
 2:   {
 3:     protected SyncLookupRule(IPropertyInfo primaryProperty) : base(primaryProperty)
 4:     {
 5:       IsAsync = false;
 6:       CanRunAsAffectedProperty = false;
 7:       CanRunInCheckRules = false;
 8:       CanRunOnServer = false;
 9:     }
 10:   }

and/or

 1:   public abstract class AsyncLookupRule : CommonBusinessRule
 2:   {
 3:     protected AsyncLookupRule(IPropertyInfo primaryProperty) : base(primaryProperty)
 4:     {
 5:       IsAsync = true;
 6:       CanRunAsAffectedProperty = false;
 7:       CanRunInCheckRules = false;
 8:       CanRunOnServer = false;
 9:     }
 10: 

for Sync and Async lookup rules that will only run when a property is changed by user or code.

Identify patterns and stereotypes of rules in your app and create more base classes. You can share them with us on the CslaContrib site

6. Both Object and Property rules can set Error/Warning/Info on other properties, and even co-exist.

This feature would seem possible with Csla 4.0/4.1 but never worked properly. These issues are now fixed and will even allow a
property level rule to set error/warning/info message on other properties than the Primary property.

This AnyRequired rule will make sure that at least one of the fields must have a value and if broken sets ErrorMessage on all fields:

 1:   public class AnyRequired : CommonBusinessRule
 2:   {
 3:     public AnyRequired(IPropertyInfo primaryProperty, params IPropertyInfo[] additionalProperties)
 4:       : base(primaryProperty)
 5:     {
 6:       InputProperties = new List<IPropertyInfo>() {primaryProperty};
 7:       InputProperties.AddRange(additionalProperties);
 8:     }
 9:  
 10:     protected override string GetMessage()
 11:     {
 12:       return HasMessageDelegate ? base.GetMessage() : Resources.AnyRequiredRule;
 13:     }
 14: 
 15:     protected override void Execute(RuleContext context)
 16:     {
 17:       // if all values are Null or Empty
 18:       if (context.InputPropertyValues.Select(keyvalue => keyvalue.Value.ToString().Trim()).All(string.IsNullOrEmpty))
 19:       {
 20:         var fieldNames = string.Join(", ", context.InputPropertyValues.Select(p =>p.Key.FriendlyName));
 21:  
 22:         foreach (var field in context.InputPropertyValues)
 23:         {
 24:           context.Results.Add(new RuleResult(this.RuleName, field.Key, string.Format(GetMessage(), fieldNames)) { Severity = this.Severity });
 25:         }
 26:       }
 27:     }
 28:   }

usage:
 1:     BusinessRules.AddRule(new AnyRequired(NameProperty, Name2Property, Name3Property));

Summary

This post gives a brief introduction to the enhancements that is made in the Rule engine for Csla 4.2.

Stay tuned for more updates. My next posts will dive into different types of rules and how to implement these.
All code I use here can be found the RuleTutorial sample that will be in the Samples download.
At time of publishing this sample is not included in the Csla 4.2 Alfa release so you must get latest version from trunk to get the code.

Posted in .Net, Csla | 2 Comments »

CslaContrib.MEF and Repository pattern with Csla4

Posted by jonnybekkum on 30/12/2010

Introduction

MEF has become a standard component in .NET4 and so become an easy starting point for developers for IoC and much more.

In this article I will focus how you can easily use MEF to create an injectable Repository data access into  your Csla business objects. This will help you to create more testable code as your unit tests can inject their own implementation of data access or even just fake som data access. The accompanying sample code uses DataPortal_XYZ methods and can be downloaded from http://cslacontrib.codeplex.com

Csla implements several design patterns including Mobile Objects and Factory methods. So your business objects should not know if the data access happens inline (one-tier) or  on a server (two-tier). This is handled by configuration of the DataPortal and the sample code shown here will work in both configurations.

The Repository pattern is widely described in other articles but here is short introduction for typical usage in Csla:

  1. You have a known set of repository data access objects.
  2. You have defined Interfaces for your data access code.
  3. You want to inject the data access code into your BO.

Ioc static class

The Ioc static class holds the MEF container and will initialized when first call is made. This class also provides a method for test code to supply an externally configured container for data access or fake data.

Custom base classes

The first step is to create custom base classes. This will be the base classes that your BOs inherit from and will make sure to provide the Imports in your objects.

These classes must handle the OnDataPortalInvoke, OnDeserialized and Child_OnDataPortalInvoke (when applicable) events to inject dependencies.

The OnDataPortalInvoke is called by the DataPortal before it call the DataPortal_XYZ methods in both one-tier and two-tier configuration.
The Child_OnDataPortalInvoke is called by the ChildDataPortal beore it call the Child_XYZ methods.
the OnDeserialized is called when the object is deserialized on server or client.

Sample code for MefBusinessBase:

  public class MefBusinessBase<T> : BusinessBase<T> where T : BusinessBase<T>  
  {    
    protected override void DataPortal_OnDataPortalInvoke(DataPortalEventArgs e)    
    {      
       Inject();       //inject dependencies into instance      
       base.DataPortal_OnDataPortalInvoke(e);    //call base class   
    }     

    protected override void Child_OnDataPortalInvoke(DataPortalEventArgs e)    
    {          
       Inject();      //inject dependencies into instance      

       base.Child_OnDataPortalInvoke(e);   //call base class    
    }   

    protected override void OnDeserialized(System.Runtime.Serialization.StreamingContext context)    
    {      
       Inject();      
       base.OnDeserialized(context);    
    } 

    private void Inject()    
    {      
       Ioc.Container.ComposeParts(this);    
    }  
  }

The following classes are supplied in CslaContrib.MEF:

MefBusinessBase
MefBusinessBindingListBase
MefBusinessListBase
MefCommandBase
MefDynamicBindingListBase
MefDynamicListBase
MefNameValueList
MefReadOnlyBase
MefReadOnlyBindingList
MefReadOnlyListBase

Define repository object and data access

In order to use IoC you will create interfaces for the data access code and known repository objects. The smallest samples I could think of is shown here:

  public interface IRootDataAccess  
  {    
     RootData Get(int id);  
  }   

  public class RootData  
  {    
    public int Id { get; set; }    
    public string Name { get; set; }  
  } 

Use MEF to satisfy Imports in your data access

Define injectable properties in your business objects like this:

    [NonSerialized, NotUndoable]    
    private IRootDataAccess _myRootDataAccess;

    [Import(typeof(IRootDataAccess))]    
    public IRootDataAccess MyRootDataAccess    
    {
       get { return _myRootDataAccess; }
       set { _myRootDataAccess = value; }
     }
 Note – You must use a private backing field and mark the field as both NonSerialized and NotUndoable as injected properties should NOT be included in a two-tier application nor be part of the N-Level undo. 

So the data access would look like this:

    public static MyRoot GetRoot(int id)
    {
       return DataPortal.Fetch<MyRoot>(id);
    }    

    public void DataPortal_Fetch(int criteria)
    {
      var data = MyRootDataAccess.Get(criteria);
      using (BypassPropertyChecks) 
      { 
        Id = data.Id; 
        Name = data.Name; 
      }    
   }
In the fetch method the BusinessObject has been supplied with a repository object and knows that this object has a Get method that accepts an int parameter. 

DataAccess classes

 

 

Create classes that Export and implement the data access interfaces:

  [Export(typeof(IRootDataAccess))]
  public class MyRootDataAccess : IRootDataAccess
  {
    public RootData Get(int id)
    {
      return new RootData() { Id = id, Name = "Ole Olsen" };
    }
  }


The Ioc static class will load Exports from all assemblies in the current folder and the executing assembly (for a client application). So when your business objects inherits from the MefXYZ base classes  you will automatically get the available Exports which in your production environment should only be the production assemblies (no test assemblies). 

The good thing here is that when you create unit tests – you can supply your own configured container to the IoC class that will typically do fake data access while the production code will do actual data acces to a database or webservice.

  

I hope this will encourage and help you to use MEF if you want to use the repository pattern in a Csla project. All sample code can be found in the Samples folder in CslaContrib and the CslaContrib.MEF project is in the CslaContrib trunk.

 

Posted in Csla | Tagged: , , | 4 Comments »

Forms DataBinding – The magic sequence of BindUI/UnbindUI

Posted by jonnybekkum on 20/10/2009

If you are developing Windows Forms apps and using Csla you may get errors like “EditLevelMismatch in CopyState” or se that data values are overwritten when the dataobject is connected to the UI, diconnected from the UI or when the Form is disposed. And often/typically this happends when ComboBoxes are used on the form.

The reasion this happends is because you are making logical errors in your code with regards to DataBinding.

Csla 3.6 introduces the CslaActionExtender and additional helper classes to automate BindUI and UnbindUI but there are times when you really need to do this manually and this article will show you how to this in a proper way. The CslaActionExtender is covered in Rockys book “Expert C# 2008 Business Objects” so I will not cover here.

Whenever it is necessary to handle Bund/Unbind manually I always recommend to create the following methods in your form:

private void BindUI()
{
    // Bind ComboBox list datasources first 
    BindingHelper.RebindBindingSource(customerTypeNameValueListBindingSource,
                                        CustomerTypeNameValueList.GetNameValueList());

    // Bind dataobjects - starting with root object, child, grandchild and so on....
    BindingHelper.RebindBindingSource(testRootBindingSource, MyRoot);
}

private void UnbindUI(bool cancel)
{
    // Unbind in the opposite sequence of BindUI
    BindingHelper.UnbindBindingSource(testRootBindingSource, cancel, true);
    BindingHelper.UnbindBindingSource(customerTypeNameValueListBindingSource, false, false);
}

The correct sequence for BindUI is:

  1. First always bind the datasorces for lists used in ComboBoxes
  2. Next bind the root object
  3. If needed bind the child and grand child objects in that order

The correct sequence for Unbind is the reverse sequence of BindUI.

Why is the sequence so important? When databinding is active on one of the properties SelectedValue, SelectedItem or Text on a ComboBox the value vill be written to the underlying boound property when the prtoperty value is changed. This will happen if the list items does not exist or is removed whild databinding is active. So the key is to make sure that list items exists before the dataobject is connected to BindingSource (bound) and list items are removed only after the dataobject has been disconnected (unbound).

The last pitfall is if you (the developer) does not disconnect dataobjects from the UI when the form is closed. The sequence og disposing BindingSource will be undetermined and may or may not cause the same problems. To fix this you should always make sure to call UnbindUI in the FormClosed event.

    private void MyForm_FormClosed(object sender, FormClosedEventArgs e)
    {
      UnbindUI(true);
    }

The sematics for Save operation is as follows:

  private void SaveMyRoot()
  {
    // assumes dataobject is valid for save, should thest for IsSavable

    UnbindUI(false);
    try
    {
      MyRoot = MyRoot.Save();
    }
    catch (DataPortalException ex)
    {
      // Handle exception the way you waant
    }
    finally
    {
      BindUI();
    }

If you download the code from CslaContrib you will find a small helper class in MyCsla.Windows.BindingHelper that helps you to do a correct Bind and Unbind:

public static class BindingHelper
{

	/// 
	/// Unbinds the binding source and the Data object. Use this Method to safely disconnect the data object from a BindingSource before saving data.
	/// 
	/// The source.
	/// if set to true then call CancelEdit else call EndEdit.
	/// if set to true this BindingSource contains the Root object. Set to false for nested BindingSources
	public static void UnbindBindingSource(BindingSource source, bool cancel, bool isRoot)
	{
		IEditableObject current = null;
		// position may be -1 if bindigsource is already unbound which results in Exception when trying to address current
		if ((source.DataSource != null) && (source.Position > -1)) {
			current = source.Current as IEditableObject;
		}

		// set Raise list changed to True
		source.RaiseListChangedEvents = false;
		// tell currency manager to suspend binding
		source.SuspendBinding();

		if (isRoot) source.DataSource = null;
		if (current == null) return;

		if (cancel)
		{
			current.CancelEdit();
		}
		else
		{
			current.EndEdit();
		}
	}

	/// 
	/// Rebinds the binding source.
	/// 
	/// The source.
	/// The data.
	public static void RebindBindingSource(BindingSource source, object data)
	{
		RebindBindingSource(source, data, false);
	}


	/// 
	/// Rebinds the binding source.
	/// 
	/// The source.
	/// The data.
	/// if set to true then metadata (ovject/list type) was changed.
	public static void RebindBindingSource(BindingSource source, object data, bool metadataChanged)
	{
		if (data != null)
		{
			source.DataSource = data;
		}

		// set Raise list changed to True
		source.RaiseListChangedEvents = true;
		// tell currency manager to resume binding 
		source.ResumeBinding();
		// Notify UI controls that the dataobject/list was reset - and if metadata was changed 
		source.ResetBindings(metadataChanged);
	}
}

Posted in Csla | Tagged: , , | 9 Comments »

 
Follow

Get every new post delivered to your Inbox.