Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Languages / C#

Fluentx: A Special .NET Library

4.80/5 (66 votes)
24 Sep 2014CPOL6 min read 81.1K  
This article talks about Fluentx .NET Library
Download Fluentx

Introduction

Fluentx is a .NET library I have created to help me in development in any project I work on, you can consider it as a utility libarary but its really bigger than that, it holds various utility methods, helper methods and design patterns that will help any developer using the .NET framework, I have also collected some extension methods from the web (some are optimized and some are left as is) which I have used previously in projects I have been working on, so if you think that you have a helper method, extension method a pattern or a class that will help others and you think it can fit in Fluentx then please feel free, the main limitation is dependency, because as much as possible I want fluentx to be portable and does'nt depend on web, windows or any other library or framework.

The aim is that Fluentx helps developers in their coding and make things just more and more simpler.

Fluentx covers all major C# control statements, and eliminates the limitations within them, and adds more features, the assembly holds 5 major categories: C# control statements, Helper Classes, Extension Methods, Specifications Pattern, Object to Object Mapper, the assembly will get bigger and bigger by time as we will add more and more to it to make it used and helps everybody out there.

It also has an implementation of Specification Pattern as a validation for any type of code, whether its a business validation or anything.

Functionalities Categories

So fluentx has so many functionalities, and up to the moment of writing this article I can divide them into five main categories:

- C# Control Statements

- Helper classes 

- Extension methods

- Patterns

- Fluentx Mapper (Object to Object Mapper)

To use Fluentx its straight forward and simple: you add a reference and then you can use the library, now for C# control statements and some Helper methods you need to start with the main class of fluentx which is Fx (Fx is also the representation of the mathematical expression F(x) => function of x).

Below you will find code snippets using Fluentx functionalities in all different categories

1. C# Control Statements

C# control statements such as if, switch, try catch, while, for, foreach etc ... are all translated into lambda expressions and can be used as functional statements, to use them start by stating Fx the main class then the control statement you want to use, below are some examples and you can find the rest also documented in the library it self.

Switch Statement

C# switch statement is a very helpful statement and provides more readability than if statement specially when the conditions become more and more, the main limitation of the switch statement is the that switch cases should hold a constant value, and you can't use it for any variable you want. A really good example of using it is with Types, when you want to do a switch statement over a type instance:

Fx.Switch("Fluentx".GetType())
      .Case<int>().Execute(() =>
      {
          result = 1;
      })
      .Case<string>().Execute(() =>
      {
          result = 2;
      })
      .Default(() =>
      {
          result = 0;
      });

ForEach/ForEvery with item and index

In many times I found my self wanting to use a foreach statement with an index of the current item. 

C++
IList<Custom> customers = new List<Customer>();
customers.Foreach((item, index)=>{
//item is the current item in the loop, and index of the current item in the loop
}); 

of course foreach has a synonem in fluentx ForEvery which it exactly does the same thing as foreach, I added this one because some .NET libraries use ForEach and it might do a conflict which you can over come by using ForEvery

WhileFalse, WhileTrue, WhileTrueFor and WhileFalseFor

int result = 0; Fx.WhileTrue(() => { ++result; return result != 6; });

int result = 0; Fx.WhileFalse(() => { ++result; return result == 6; });

Fx.WhileTrueFor(()=>IsProcessComplete(), 4);

Fx.WhileFalseFor(()=>IsProcessComplete(), 5);

Retry on Fail (Long Polling)

int result=0;
Fx.RetryOnFail(() =>
     {
        ++result;
        return result > 5;
     });

//Or

int result=0;
Fx.RetryOnFail(() =>
     {
        ++result;
        return result > 5;
     }, attemptSleepInMilliSeconds: 10);

Try - SwallowIf

int result = 0;
Fx.Try(() =>
{
     throw new NotImplementedException();
     ++result;
}).SwallowIf<NotImplementedException>();
//Swallows a certain exception and prevent throwing it.

Using Statement

Fx.Using(new PrivateDisposableTestEntity(), (instance) => { });

Fx.To(Value)

//All primitive types are supported to parse a string with an optional default value
var birthDate = Fx.ToDateTime("Some Text", DateTime.Now);
var age = Fx.ToInt32("Some Text", -1);
var age = Fx.ToInt32("Some Text");

2. Helper Classes

Period Class

Period class represents a time period between two datetime instances, some times we have a different set of dates and we need to determine if there is an overlap occurs in between periods.

var firstPeriod = new Period(date1, date2);

var secondPeriod = new Period(date3, date4);

bool isOverlap = firstPeriod.IsOverlap(secondPeriod);//Edges NOT considered in the overlap

bool isOverlap1 = firstPeriod.IsOverlap(secondPeriod, true); //Edges are considered in the overla

bool flag = firstPeriod.IsWrap(DateTime.Now);//Checks if the current date is within that period

Guard Class

Use this class to validate for certain conditions and assertions but in a more neat way and without using the IF statement.

Guard.Against<NotImplementedException>(myFlag);

PredicateBuilder Class

Predicate Builder is where you build boolean logic using expressions, a good use for it is with linq queries as they miss the Or functionality, using this predicate builder you can do Or and AND operations easily, it also has a start point value of TRUE or FALSE, there are several implementations on the net for it, I think this one is good:

var predicate = PredicateBuilder.True<Customer>();

foreach (var product in products)
{
    predicate = predicate.And(c => c.Products.Any(x => x.Id == productId));
}

3. Extension Methods

Nullability Check

var customer = GetCustomerById(id);

var flag= customer.IsNull(); 

var flag = customer.IsNotNull();

IfTrue and IfFlase

bool flag = GetBooleanValueMethod();

flag.IfTrue(()=>{ //Do Anything here });

<span style="font-size: 9pt;">flag.IfFlase(()=>{ //Do Anything here });</span>

Random

<span style="font-size: 9pt;">IList<Customer> customers = GetCustomers();</span>

<span style="font-size: 9pt;">var randomCustomer = customers.Random();</span>

Is

<span style="font-size: 9pt;">​var customer = GetCustomer();</span>

<span style="font-size: 9pt;">var flag = customer.Is<Customer>();</span>

Logical Operators

var flag = flag1.Or(flag2).Xor(flag3).And(flag4).And(()=> {return true;}).Not();

Between

var value = 7;
var flag = value.Between(5, 9);

In/NotIn

string parameter = null;

var result = parameter.In(new string[] { "one", null, "three" });

var result = parameter.NotIn(new string[] { "one", null, "three" });

MinBy/MaxBy

Finds out the minimum/maximum item in an IEnumerable according to the specifed predicate.

DateTime[] dates = new DateTime[] { DateTime.Now, DateTime.Now.AddYears(3) };

var min = dates.MinBy(x => x.Year);

Random

Extension method to perform random return of an item within the specified list.

DateTime[] dates = new DateTime[] { DateTime.Now, DateTime.Now.AddYears(3) };

var randomDate = dates.Random();

To[PremitiveType]

ToInt, ToInt16, ToInt32, ToInt64, ToUInt, ToUInt16, ToUInt32, ToUInt64 ToDateTime, ToDouble, ToFloat, ToLong, ToULong, ToByte, ToSByte, ToBool, ToGuid, ToDateTime

ToCSV

Extension method to perform a comma separated string from the specified list

DateTime[] dates = new DateTime[] { DateTime.Now, DateTime.Now.AddYears(3) };

var csvDates = dates.ToCSV();

DateTime Related Extension Methods

DateTime date = 24.September(2014);

Other Extension Methods

And many other extension methods that you can use.

4. Patterns

Specifications Pattern

Specifications pattern is to combine several business rules together using boolean logic. I have a good implementation of this pattern and I found a very useful and interesting basic implementation for it on the web written by Govindaraj Rangaraj and here is a link to the original article in codeproject:

 http://www.codeproject.com/Articles/670115/Specification-pattern-in-Csharp

I made a little bit of tweaking on it but eventually the main concept is still there

ISpecification<int> rule1 = new ExpressionSpecification<int>(x => x == 1, "rule1 failed");
ISpecification<int> rule2 = new ExpressionSpecification<int>(x => x == 2, "rule2 failed");
ISpecification<int> rule3 = new ExpressionSpecification<int>(x => x == 3, "rule3 failed");
ISpecification<int> rule4 = rule1.Or(rule2).Or(rule3); 
var result = rule4.ValidateWithMessages(4);

5. Fluentx Mapper (Object to Object Mapper)

Fluentx mapper is an object to object mapper, its very easy, simple and straight forward to use, it has the most common functionalities you would expect of a mapper to do the proper mapping you desire, it also provide you with the option to manually map properties and fields.

The following is an example of using the mapper with clarificaitons:

var mapper = new Mapper<DTOCustomer, Customer>()
.UserMapper<DTOProfile, Profile>()
.Conditional(x => x.Description, src => src.Description != string.Empty)
.Ignore(dest => dest.BirthDate)
.IgnoreIf(dest => dest.Serial, src=> { return src.Serial == string.Empty;})
.For(dest => dest.Order, src => RepoAccount.LoadOrder(src.OrderId))
.ForIf(dest => dest.Number, src => src.Number, src => src.AutoGenerate)
.Resolve((src, dest)=>{ .... do whatever you want here ... }); 

var customer = mapper.Map(dtoCustomer);

Basic Convention

First of all we need to know that Fluentx Mapper uses conventions to map properties, so it assumes and tries to map a src property to a destination property if the name of the properties match. so once you create:

var mapper = new Mapper<DTOCustomer, Customer>();

then you have the basic functionality turned on and kicking, and that is the default behaviour of an object to object mapper.

Nested Mapper

Now you can inject the use of another mapper to the mapper you have as:

var mapper = new Mapper<DTOCustomer, Customer>()
.UserMapper<DTOProfile, Profile>();

here we are instructing fluentx mapper that we are mapping DTOCustomer to Customer and we are using an internal mapper of DTOProfile to Profile when ever a property inside customer of type DTOProfile is found.

Conditional Mapping

Conditional mapping of a property is to override the basic mapping convention of NOT to map a specified property if a condition is NOT met, so a property will be mapped if the condition is true

.Conditional(x => x.Description, src => src.Description != string.Empty)

Ignore

Ignores the mapping of the specified property (dont map it)

.Ignore(dest => dest.BirthDate)

IgnoreIf

Ignores the mapping of the specified property if the condition evaluates to true.

.IgnoreIf(dest => dest.Serial, src=> { return src.Serial == string.Empty;})

For

Maps the specified property using the specified action which takes source as the parameter

.For(dest => dest.Order, src => RepoAccount.LoadOrder(src.OrderId))

ForIf

Maps the specified property using the specified action which takes source as the paramter if the evaluation of the specified action is true

.ForIf(dest => dest.Number, src => src.Number, src => src.AutoGenerate)

Resolve

A custom action to be executed on the mapper when all mapping is done, can be used to manually manipulate the mapping as it has the source and destination as parameters.

.Resolve((src, dest)=>{ .... do whatever you want here ... }); 

Centeralized Mapping

Sometimes you want to use the same mapping in different places in your code, in order to do that fluentx mapper can be used in such a way to make it centerazlied or reusable as follows using inheritance:

public class CustomerMapper : Mapper<DTOCustomer, Customer>
{
    public CustomerMapper()
    {
        this.UserMapper<DTOProfile, Profile>();
        this.Conditional(x => x.Description, src => src.Description != string.Empty);
        this.Ignore(dest => dest.BirthDate);
        this.IgnoreIf(dest => dest.Serial, src=> { return src.Serial == string.Empty;});
        this.For(dest => dest.Order, src => RepoAccount.LoadOrder(src.OrderId));
        this.ForIf(dest => dest.Number, src => src.Number, src => src.AutoGenerate);
        this.Resolve((src, dest)=>{ .... do whatever you want here ... }); 
        
        //Or you can chain the methods after the first call 
    }
}

var mapper = new CustomerMapper(); 
var customer = mapper.Map(dtoCustomer);

of course you can now cache the mapper and reuse it any where in your code in the way you desire.

Summary

I intend to have fluentx to become bigger an bigger, if you have any piece of code you think I could include in it please let me know, so we can share it and have it bigger and better.

License

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