How to: C# Connect to dynamic event

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.

Advertisements

CSLA 4.2 Rules update

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.

CslaContrib.MEF and Repository pattern with Csla4

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.

 

Forms DataBinding – The magic sequence of BindUI/UnbindUI

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

Using custom FieldData in Csla 3.1.7 for Net 2.0

Jason Bock wrote a great article on his blog about Creating custom Fielddata classes in Csla that requires Csla 3.7.1 of newer for .Net 3.5 Sp1. If you havent already read his article please do to get a better understanding of what custom FieldData can bring to your Csla solution.

However most of my worktime is using the Csla 3.71. for .Net 2.0 so how could I use these custom fielddata in a .Net 2.0 solution and still be able to port/recompile my code with Csla 3.7.1 for .Net 3.5?

Our customers/users have also requested our apps to be more precise in determining if data has been changed or not, ie has the propertyValue actually changed from the originalvalue. The introduction of custom field data would give us this with minimal effort. I can also vision custom FieldData beein used for auditing purposes because they may track both original and new field value.

The business objects in Jasons sample uses RegisterProperty with lambdas that are actually Expressions. So I had to alter my intermediate classes in MyCsla to add new registerProperty overloads that take a string propertyName instead of a lambda expression.  Latest version of MyCsla for Csla 3.7.1 N2 and the entire CunstomFieldData sample project is available for download on the CslaContrib project on CodePlex.

  [Serializable]
  public class BusinessBase<T> : Csla.BusinessBase<T> where T : BusinessBase<T>
  {
    #region RegisterProperty
    protected static PropertyInfo<P> RegisterProperty<P>(string propertyName)
    {
      return RegisterProperty(Csla.Core.FieldManager.PropertyInfoFactory.Factory.Create<P>(typeof(T), propertyName));
    }

    protected static PropertyInfo<P> RegisterProperty<P>(string propertyName, string friendlyName)
    {
      return RegisterProperty(Csla.Core.FieldManager.PropertyInfoFactory.Factory.Create<P>(typeof(T), propertyName, friendlyName));
    }

    protected static PropertyInfo<P> RegisterProperty<P>(string propertyName, string friendlyName, RelationshipTypes relationship)
    {
      return RegisterProperty(Csla.Core.FieldManager.PropertyInfoFactory.Factory.Create<P>(typeof(T), propertyName, friendlyName, relationship));
    }

    protected static PropertyInfo<P> RegisterProperty<P>(string propertyName, string friendlyName, P defaultValue)
    {
      return RegisterProperty(Csla.Core.FieldManager.PropertyInfoFactory.Factory.Create<P>(typeof(T), propertyName, friendlyName, defaultValue));
    }

    protected static PropertyInfo<P> RegisterProperty<P>(string propertyName, string friendlyName, P defaultValue, RelationshipTypes relationship)
    {
      return RegisterProperty(Csla.Core.FieldManager.PropertyInfoFactory.Factory.Create<P>(typeof(T), propertyName, friendlyName, defaultValue, relationship));
    }
    #endregion
  }

Next we need to alter BusinessCore base class in CustomFieldData project to inherit from MyCsla.BusinessBase  to get the new RegisterProperty overloads:

  [Serializable]
  public abstract class BusinessCore : MyCsla.BusinessBase where T: BusinessCore

And finally change the test business object to use the new RegisterProperty.

  [Serializable]
  public sealed class Person : BusinessCore
  {
    private static PropertyInfo ageProperty =
	                    RegisterProperty("Age");
    private static PropertyInfo firstNameProperty =
			    RegisterProperty("FirstName", "Friendly first name");
    private static PropertyInfo lastNameProperty =
			    RegisterProperty("LastName", "Friendly lastname", String.Empty);

And thats just about it. The custom FieldData works great!!

Download the latest version of MyCsla for Csla 3.7.1 for N2 with CustomFieldData project from CslaContrib

[Serializable]
public class BusinessBase<T> : Csla.BusinessBase<T> where T : BusinessBase<T>
{
#region RegisterProperty
protected static PropertyInfo<P> RegisterProperty<P>(string propertyName)
{
return RegisterProperty(Csla.Core.FieldManager.PropertyInfoFactory.Factory.Create<P>(typeof(T), propertyName));
}protected static PropertyInfo<P> RegisterProperty<P>(string propertyName, string friendlyName)
{
return RegisterProperty(Csla.Core.FieldManager.PropertyInfoFactory.Factory.Create<P>(typeof(T), propertyName, friendlyName));
}protected static PropertyInfo<P> RegisterProperty<P>(string propertyName, string friendlyName, RelationshipTypes relationship)
{
return RegisterProperty(Csla.Core.FieldManager.PropertyInfoFactory.Factory.Create<P>(typeof(T), propertyName, friendlyName, relationship));
}

protected static PropertyInfo<P> RegisterProperty<P>(string propertyName, string friendlyName, P defaultValue)
{
return RegisterProperty(Csla.Core.FieldManager.PropertyInfoFactory.Factory.Create<P>(typeof(T), propertyName, friendlyName, defaultValue));
}

protected static PropertyInfo<P> RegisterProperty<P>(string propertyName, string friendlyName, P defaultValue, RelationshipTypes relationship)
{
return RegisterProperty(Csla.Core.FieldManager.PropertyInfoFactory.Factory.Create<P>(typeof(T), propertyName, friendlyName, defaultValue, relationship));
}
#endregion

protected override void DataPortal_OnDataPortalInvoke(DataPortalEventArgs e)
{
Debug.Print(“DataPortalInvoke object:{0}, operation:{1}”, e.ObjectType, e.Operation);
base.DataPortal_OnDataPortalInvoke(e);
}

protected override void DataPortal_OnDataPortalInvokeComplete(DataPortalEventArgs e)
{
Debug.Print(“DataPortalInvokeCompleted object:{0}, operation:{1}”, e.ObjectType, e.Operation);
base.DataPortal_OnDataPortalInvokeComplete(e);
}

protected override void DataPortal_OnDataPortalException(DataPortalEventArgs e, Exception ex)
{
Debug.Print(“DataPortalExeption object:{0}, operation:{1}, exception:{2}”, e.ObjectType, e.Operation, ex);
base.DataPortal_OnDataPortalException(e, ex);
}

}

CSLA 3.7.1 for .Net 2.0 is released

Csla 3.7.1 for .Net 2.0 SP1 was released on the 30. of September.  This release includes:

  • bugfixes to FilteredBindingList/SortedBindingList
  • updates to ApplicationContext – LogicalExecutionLocation
  • and the new PropertyInfoFactory that allows you to create your own custom field data

All the Business Objects you write using Csla 3.7.1 for N2 will also work and compile with the “full” Csla 3.7.1 for .Net 3.5 when you are ready to move to the latest version of .Net.

Unfortunately there are still many developers working with legacy systems or have to support their solutions on Windows2000 platform that limits their choice of .Net framwork to version 2.0. As our client is stuck with a similar condition I have “downgraded” Csla from .Net 3,5 to run on .Net 2.0 with SP1. My version for N2 also inludes LinqBridge from www.albahari.com that enables nearly all of C# 3.0 syntax inlunding Linq over IEnumerable.

If you are interested in using Csla make sure to download the samples and look at the code.