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.
IList<Custom> customers = new List<Customer>();
customers.Foreach((item, index)=>{
});
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;
});
int result=0;
Fx.RetryOnFail(() =>
{
++result;
return result > 5;
}, attemptSleepInMilliSeconds: 10);
Try - SwallowIf
int result = 0;
Fx.Try(() =>
{
throw new NotImplementedException();
++result;
}).SwallowIf<NotImplementedException>();
Using Statement
Fx.Using(new PrivateDisposableTestEntity(), (instance) => { });
Fx.To(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);
bool isOverlap1 = firstPeriod.IsOverlap(secondPeriod, true);
bool flag = firstPeriod.IsWrap(DateTime.Now);
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(()=>{
<span style="font-size: 9pt;">flag.IfFlase(()=>{
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 ... });
}
}
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.