Click here to Skip to main content
15,867,330 members
Articles / Mobile Apps / Windows Phone 7

Write Compact and Functional Code with BFsharp

Rate me:
Please Sign up or sign in to vote.
4.83/5 (10 votes)
16 Aug 2011CPOL10 min read 40.4K   372   27   9
BFsharp is a cross-platform open source library providing several object services which can be used in a wide range of applications.

What is BFsharp?

It's an open source framework which helps in creating testable, maintainable and extensible code in a wide range of applications. It provides features which can be used in business layer to build your domain. BFsharp can be used in user interface layer (like MVVM pattern) to deliver richer user experience quicker and more easily. It has a strong support for data and behavior extensibility allowing you to build applications which end user can customize. What is more, BFsharp makes your code more agile; your design will be more resilient to changes in requirements.

The project is two years old now, I believe. I've started BFsharp during development one of the projects I was participating in. Now, it’s used successfully in several production systems. The framework is cross-platform which means the same API is accessible from .NET, Silverlight and WP7. The project can be found here and in the near future here. BFsharp gets new features once in a while.

Below is a loose list of the framework’s features however it’s not exhausted. I will be going into details as how to use some of these features in this article and others in the future ones:

  • Rules - they allow defining reusable code which can be executed in response to a property change (strategy pattern). Rules can calculate values (BusinessRules), validate conditions (ValidationRules) or execute action (ActionRules).
    • Rules have priority.
    • Rules support Exception Handling.
    • Rules have several modes which control how they behave.
    • Rule could react to a property change and a collection change. The dependency is automatically gotten from rule definition or specified manually.
    • Rules can suppress other rules.
    • Rules have a strong support for databinding enabling rich user experience without additional code.
    • Rules can be grouped and enabled\disabled based on some arbitrary condition.
    • Rules can be configured for client\server context differently.
    • WCF serialization is supported.
    • Entities have IsValid property which tells whether all ValidationRules are valid.
    • Validation supports hierarchical entities. See entity management for details.
    • There is rule and entity prototype mechanism.
    • Validation can be defined using attributes.
    • Rules enable reuse and separation of concerns.
    • POCO support. 
  • DynamicProperties - an entity can be extended dynamically at runtime.
    • DynamicProperties can be used in rule formulas.
    • DynamicProperties support a dynamic or a strong schema.
    • Dynamic type is generated so all frameworks using reflection work.
    • WCF serialization is supported.
    • DynamicProperties prototype is supported.
  • Formula DSL - a textual language enabling an end user to define formulas, predicates and other code
    • It can be used to customize functionality.
    • Rules can be written using Formula.
    • Methods and properties accessible from DSL can be controlled providing greater security.
  • IsDirty - marks entity as dirty in response to a property change.
  • Entity management - groups entities into aggregates - concepts known from Domain Driven Design. It allows managing entities in logical groups. Let's take invoice as an example. If any of InvoiceLines (child) is modified or validated the Invoice (parent) should be notified to react accordingly.
    • It can be used in business layer as well as in ViewModel hierarchy.
    • There are a couple of strategies defined and it can be extended.

In this article I'll try to present the basic concepts of BFsharp based on some examples.

Getting started

We start by adding BFsharp to a sample project. All you need to do is to reference BFsharp.dll and BFsharp.AOP.dll (Silverlight assemblies end with SL and Windows Phone end with WP7) or use nuget to do it automatically.

add_ref.png

add_ref2.png

The Standard Code

Let's take an InvoiceLine as the first example. Basically it's a position in an invoice or receipt. Its responsibility is to calculate the total price of the purchased product.

InvoiceLine.jpg

The entity has several properties: Name, Quantity, Price and Total.

C#
public class InvoiceLine
{
    public string Name { get; set; }
    public decimal Quantity { get; set; }
    public decimal Price { get; set; }
 
    public decimal Total { get; set; }
}

The main rule which controls the entity is:

C#
Total = Quantity*Price

One of the possible implementation of this feature is to make Total property read-only:

C#
public decimal Total { get { return Quantity*Price; } }

It's simple but very limited solution. Imagine now that the entity is used in Silverlight or WPF as a data context. To react as the user types the entity has to implement INotifyPropertyChanged. Of course, it can be done manually:

C#
public class InvoiceLine : INotifyPropertyChanged
{
    private string _name;
    public string Name
    {
        get { return _name; }
        set
        {
            _name = value;
            RaisePropertyChanged("Name");
        }
    }
 
    private decimal _quantity;
    public decimal Quantity
    {
        get { return _quantity; }
        set
        {
            _quantity = value;
            RaisePropertyChanged("Quantity");
            RaisePropertyChanged("Total"); // Open/closed principle is broken here
        }
    }
 
[...]. 
    public decimal Total
    {
        get { return Quantity*Price; }
    }
 
    public event PropertyChangedEventHandler PropertyChanged;
    
    private void RaisePropertyChanged(string name)
    {
        var e = PropertyChanged;    
        if (e != null)
            (this, new PropertyChangedEventArgs(name));
    }
}

However, the above code has several drawbacks:

  1. Automatic properties cannot be used so the code is much longer and become unreadable.
  2. Each property has to call RaisePropertyChanged - it’s repeatable mechanic coding.
  3. Hardcoded property name prevents correct refactorings (for example using R#)
  4. If there is a calculated property (Total) RaisePropertyChanged has to be called from dependent properties violating the open/closed principle. Note Quantity implementation.
  5. WCF serialization is impossible in some cases.
  6. The rules can be neither changed nor reused.

There's one business rule which complicates the solution even further. Sometimes a cashier enters the total price of the line instead of the quantity, for example at a petrol station.

C#
Quantity = Total/Price

Implementation of this scenario into this entity is not so straightforward. You'll have to prevent properties from setting each other producing stack overflow. Imagine what would happen if we added two more properties Tax and TotalWithTax. I'll leave this exercise for the Reader.

The BFsharp code

Aspects

To mitigate the presented problems BFsharp can be used. First of all, instead of implementing INotifyPropertyChanged manually let's use aspect paradigm.

BFsharp delivers several postsharp aspects which can be applied using attributes. One of them is NotifyPropertyChangedAttribute. After the main compilation postsharp postprocesses the assembly and adds appropriate code. Postsharp can be downloaded from here - the free version is enough. Now the code can be stripped to:

C#
[NotifyPropertyChanged]
public class InvoiceLine
{
    public string Name { get; set; }
    public decimal Quantity { get; set; }
    public decimal Price { get; set; }
 
    [NotifiedBy("Quantity", "Price")]
    public decimal Total { get { return Quantity*Price; } }
}

Business Rules

Now it's time to externalize the Total rule. It can be done as follows:

C#
[NotifyPropertyChanged]
public class InvoiceLine : EntityBase<InvoiceLine>
{
    public string Name { get; set; }
    public decimal Quantity { get; set; }
    public decimal Price { get; set; }
 
    public decimal Total { get; set; }
 
    public InvoiceLine()
    {
        Extensions.CreateBusinessRule(e => e.Quantity*e.Price, e => e.Total)
            .Start();
    }
}

First of all entity has to derive from EntityBase<> (BFsharp can be enabled without interference into the inheritance hierarchy as well). The base class has one important property Extensions. It contains several groups of methods and properties responsible for Rules, DynamicProperties, IsDirty and Entity Management. The main API for the BFsharp is a fluent API; however there is a standard .net one where properties can be set.

CreateBusinessRule method creates a rule which is responsible for calculating value from the formula (e.Quantity*e.Price) and then inserting it into the target property (e.Total). Note that there is no specification of property dependencies anywhere. Internally CreateBusinessRule does not take Func<> as a parameter but Expression<> thus the formula can be analyzed and dependencies can be found automatically - it's called automatic dependency analysis. If one of the dependent properties is changed the rule is reevaluated.

To fulfill the second requirement (calculating from total) another rule can be added:

C#
Extensions.CreateBusinessRule(e => e.Price == 0 ? 0 : 
			e.Total/e.Price, e => e.Quantity).Start();

Rule Suppression

A curious reader will notice that in some cases both rules are evaluated overriding the value of the each other. One of the solutions to mitigate the problem is to use rule suppression:

C#
var rule = Extensions.CreateBusinessRule(e => e.Quantity*e.Price, e => e.Total)
    .Start();
 
var rule2 = Extensions.CreateBusinessRule
		(e => e.Price == 0 ? 0 : e.Total/e.Price, e => e.Quantity)
    .Start();
 
rule.MutuallySuppressedBy(rule2);

Now if rule is evaluated and sets Total property rule2 won't run. If rule2 is evaluated and sets Quantity rule won't run.

InitializeRules

One of the design goals of BFsharp was to support architecture were entities are used on the client and server side through Visual Studio links. In some contexts the entity is used as a simple Data Transfer Object which means that it can be sent using WCF. Entities with read-only properties can be serialized and deserialized easily, however more complex solutions (like the one presented) where there is a dependency on the order of property set can't. It's because the order of property deserialization is unknown. BFsharp defines a mechanism which helps in this case. Instead of creating rules in the constructor they are defined in a method with RuleInit attribute (if you've used nuget there’s already a snippet called ri):

C#
[NotifyPropertyChanged]
public class InvoiceLine : EntityBase<InvoiceLine>
{
    public string Name { get; set; }
    public decimal Quantity { get; set; }
    public decimal Price { get; set; }
 
    public decimal Total { get; set; }
    
    [RuleInit]
    public void RuleInit()
    {
        var r = Extensions.CreateBusinessRule(e => e.Quantity*e.Price, e => e.Total)
            .Start();
    
        var r2 = Extensions.CreateBusinessRule
		(e => e.Price == 0 ? 0 : e.Total/e.Price, e => e.Quantity)
            .Start();
 
        r.MutuallySuppressedBy(r2);
    }
}

Rules are not created by default, so entity can be used as DTO, serialized and deserialized without problems. If rules are required they can be initialized by calling:

C#
invoiceLine.Extensions.InitializeRules();

InvoiceLineSample.jpg

Textual Rules

As mentioned, in some situation it would be great if the user or support staff could change business rules. With BFsharp rules can be created from strings:

C#
Extensions.CreateBusinessRule("Total=Quantity*Price")
    .Start();

It's a language called formula which reminds C# and provides a subset of it. Methods and properties accessible from it can be controlled by turning them on and off. The code is compiled at runtime into native code so after the first use it's as fast as a normally written rule; one exception is WP7 where the code is interpreted.

Dynamic Properties

Extensibility is not only behavior but data as well. BFsharp defines a concept known as dynamic properties - additional data that can be attached to the entity. It supports WCF serialization and can be used in rule formulas. Making use of this two features end user can extend the system very easily. For example, she can add property Discount to an invoice line and modify the business rule making a simple loyalty feature.

C#
var invoiceLine = new InvoiceLine();
invoiceLine.Extensions.AddProperty<decimal>("Discount");
invoiceLine.Extensions.CreateBusinessRule("Total=Quantity*Price*Discount")
    .Start();

Presented scenario allows extending data (properties) and behavior (rules) without code recompilation.

ValidationRule

In the previous example one type of rule was used, namely BusinessRule. In this scenario I'll show another type - ValidationsRule. Let's take a user registration form as below:

C#
[NotifyPropertyChanged]
public class UserRegistrationForm : EntityBase<UserRegistrationForm>
{
    public string UserName { get; set; }
    public string Password { get; set; }
    public string ConfirmPassword { get; set; }
    public DateTime Birthday { get; set; }
    public bool AcceptLicence { get; set; }
}

There are several validation rules which control the entity; one of them is the requirement that the user should have at least 18 years old. To add such a constraint we create a rule:

C#
Extensions.CreateValidationRule(r => DateTime.Now.AddYears(-18) > r.Birthday)
    .WithMessage("You should be at least 18 years old.")
    .WithOwner(r=>r.Birthday)
    .Start();

As all rules, it has automatic dependency analysis too. It means that if the Birthday is changed to less than 18 years the rule becomes invalid and the entity is marked as invalid. When the predicate becomes false the rule creates a special object - BrokenRule. It specifies cause of the error, severity and few other properties. Broken rule message can be set using WithMessage method.

Attribute Rules

Another way to add validation rules is through attributes - an easy and convenient method to add basic rules. There are several predefined attributes:

  • Required
  • MaxLength
  • Email
  • Pattern
  • Compare
  • Range
  • ShouldBe

Using the above attributes the entity can be written as follows:

C#
[NotifyPropertyChanged]
public class UserRegistrationForm : EntityBase<UserRegistrationForm>
{
    [Email, Required]
    public string UserName { get; set; }
    [Required]
    public string Password { get; set; }
    [Required]
    public string ConfirmPassword { get; set; }
         
    public DateTime Birthday { get; set; }
    [ShouldBe(true)]
    public bool AcceptLicence { get; set; }
 
    [RuleInit]
    public void RuleInit()
    {
        Extensions.CreateValidationRule(r => string.IsNullOrEmpty(ConfirmPassword) 
			|| r.Password == r.ConfirmPassword)
            .WithMessage("Confirm password doesn't match.")
            .WithOwner(r => r.ConfirmPassword)
            .Start();
        
        Extensions.CreateValidationRule(r => DateTime.Now.AddYears(-18) > r.Birthday)
            .WithMessage("You should be at least 18 years old.")
            .WithOwner(r => r.Birthday)
            .Start();
    }
}

If rules have automatic dependency analysis turned on they are validated in real time and their status is always updated. It can be checked in Extensions.IsValid property. If the value is false, broken rules can be read from Extensions.BrokenRules. Moreover, if the code is executing in Silverlight the entity integrates with the standard validation mechanism and errors are shown beside controls.

UserRegistrationForm.png

ActionRule

In the final example I will show the use of the third main rule type - ActionRule. It’s a rule which invokes some arbitrary code when a property is changed. It can be used extensively in MVVM design pattern.

Let's take the entity from the previous example. Imagine that we want to add functionality which checks if the UserName is free. The entity is used both on the server and on the client so modifying the setter is not an option (or sometimes we even can't modify the entity). We have to subscribe to the UserName property changed event, react accordingly and unsubscribe when done. It's cumbersome to write it by hand. ActionRule is created exactly for scenarios like this. Note also that we extend the entity from the outside now.

C#
public class UserRegistrationFormViewModel
{
    public UserRegistrationForm Form { get; private set; }
 
    public UserRegistrationFormViewModel(UserRegistrationForm form)
    {
        Form = form;
        Form.Extensions.InitializeRules();
        Form.Extensions.CreateActionRule(x => CheckAccountAccessibility(x.UserName)).Start();
    }
            
    private readonly BrokenRule _rule = new BrokenRule
			("UserName is already used.", BrokenRuleSeverity.Error,
                                                       "UserName");
 
    public void CheckAccountAccessibility(string userName)
    {
        if (userName == "Michael") // Simulate server
            Form.Extensions.BrokenRules.Add(_rule);
        else
            Form.Extensions.BrokenRules.Remove(_rule);
    }
}

In response to a UserName change CheckAccountAccessibility method is invoked. It checks the server and adda a broken rule if the user name is taken. Using this approach you could subscribe rule execution to everything that implements INotifyPropertyChanged or INotifyCollectionChanged with ease.

As you can see rules can help build cleaner code. They can reduce the amount of coding needed to do routine tasks. Furthermore, rules can be extracted and applied in other places making them reusable components.

Summary

This article is just an introduction to BFsharp, try to use it and get more comfortable with it. There are a lot more features which can be used in different situations and scenarios in many types of applications. I'll try to present them in next articles. Thanks for reading.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Architect macmichal.pl
Poland Poland
Micheal is an independent consultant - www.macmichal.pl.
He's main areas of interest are: DDD\CqRS, TDD, SaaS, Design Patterns, Architecture. He specializes in .Net/C# for the early beginning of it and T-SQL. He's a writer, blogger (blog.macmichal.pl) and speaker.

In his spare time, he's climbing the mountains all over the Europe.

Comments and Discussions

 
GeneralMy vote of 5 Pin
Oak Chantosa25-Jan-12 8:32
Oak Chantosa25-Jan-12 8:32 
QuestionPostSharp community edition does not support NotifyPropertyChangedAttribute Pin
Oak Chantosa24-Jan-12 9:22
Oak Chantosa24-Jan-12 9:22 
AnswerRe: PostSharp community edition does not support NotifyPropertyChangedAttribute Pin
Michael Mac25-Jan-12 7:34
Michael Mac25-Jan-12 7:34 
GeneralRe: PostSharp community edition does not support NotifyPropertyChangedAttribute Pin
Oak Chantosa25-Jan-12 8:31
Oak Chantosa25-Jan-12 8:31 
QuestionGood job mate Pin
tatran.eu@gmail.com15-Sep-11 22:38
tatran.eu@gmail.com15-Sep-11 22:38 
GeneralMy vote of 5 Pin
p.rok117-Aug-11 7:55
p.rok117-Aug-11 7:55 
QuestionInteresting framework Pin
Sacha Barber14-Aug-11 21:45
Sacha Barber14-Aug-11 21:45 
AnswerRe: Interesting framework Pin
Michael Mac15-Aug-11 6:09
Michael Mac15-Aug-11 6:09 
GeneralRe: Interesting framework Pin
Sacha Barber15-Aug-11 7:00
Sacha Barber15-Aug-11 7:00 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.