ServicePredicateBuilder for creating Serializable Expressions





5.00/5 (8 votes)
A library for creating .Net Serializable Expressions to send via WCF services
Download ServicePredicateBuilder.zip 15.8KB
Introduction
If you worked with WCF and EntityFramework, you faced with a big problem. Serializing Expressions
As you know, You can't serilize .Net Expression. so, you will have two choices:
1. Creating a method (Operation Contract) for each needing.
2. Creating another thing (class) to add predicates in a serializable form and send it via services.
ServicePredicateBuilde is that it. you can create predicates with it and send it via services and in service side get the expressions easily.
Background
There are some other libraries to do that, you can find them with googling 'serializable expression'
Unfortunately, most of them are old and don't update anymore. We couldn't use them easily.
Using the code
Install-Package ServicePredicateBuilder
Let us a review on ServicePredicateBuilder
class
[Serializable]
[DataContract]
public class ServicePredicateBuilder<TEntity> where TEntity : class
{
/// <summary>
/// Conditions
/// </summary>
[DataMember]
public Criteria<TEntity> Criteria { get; set; }
/// <summary>
/// Sorting consitions
/// </summary>
[DataMember]
public SortCondition<TEntity> SortCondition { get; set; }
/// <summary>
/// Navigation Properties to include in string format
/// </summary>
[DataMember]
public List<string> IncludedNavigationProperties { get; set; }
/// <summary>
/// Navigation Properties to include in expression format
/// There is not a difference between use `IncludedNavigationPropertiesExpression` Or `IncludedNavigationProperties`
/// but if you have some navigation properties on `n` side of relation, you have to use `IncludedNavigationProperties`
/// </summary>
private List<Expression<Func<TEntity, object>>> _includedNavigationPropertiesExpression;
public List<Expression<Func<TEntity, object>>> IncludedNavigationPropertiesExpression
{
...
}
/// <summary>
/// Pagination data for getting getting data as paging
/// </summary>
[DataMember]
public PaginationData PaginationData { get; set; }
public ServicePredicateBuilder<TDestination> Cast<TDestination>() where TDestination : class
{
...
}
}
Now, Let us code with ServicePredicateBuilder
At first, assume we have an Entity named User
, Now we wanna create a predicate on User
/////////////////////////////////////////
// In Client application
/////////////////////////////////////////
ServicePredicateBuilder servicePredicateBuilder = new ServicePredicateBuilder
{
// navigation properties you wanna add to entity
IncludedNavigationProperties = new List<string> { "Attachments", "Posts" },
// data of pagination result, you can leat it to null if you don't have pagination
PaginationData = new PaginationData
{
PageNumber = page, // destination page number
ItemsPerPage = itemsPerPage // items per each pages
}
};
// Filtering
Criteria<User> firstCritaria = Critaria.True<User>();
firstCritaria = firstCritaria.And(entity => entity.FirstName, OperatorEnum.Like, "Mohammad");
firstCritaria = firstCritaria.And>(entity =>entity.LastName, OperatorEnum.Like, "Dayyan");
firstCritaria = firstCritaria.And(entity =>entity.Comments.Count, OperatorEnum.GreaterThanOrEqual, 5);
Criteria<User> secondCritaria = Critaria.True<User>();
secondCritaria = secondCritaria.And(entity =>entity.FirstName, OperatorEnum.Like, "Vahid");
secondCritaria = secondCritaria.And(entity =>entity.LastName, OperatorEnum.Like, "Jafari");
secondCritaria = secondCritaria.And(entity =>entity.Comments.Count, OperatorEnum.GreaterThanOrEqual, 5);
Criteria<User> finalCritaria = firstCritaria.Or(secondCritaria);
servicePredicateBuilder.Critaria = finalCritaria;
// Sorting
SortCondition<User> sortCondition = SortCondition<User>.OrderBy(q=>q.FirstName);
sortCondition = sortCondition.ThenByDescending(q => q.LastName);
List<User> users = clientService.GetUser(servicePredicateBuilder);
/////////////////////////////////////////
// In Service side(WCF) method (Operation Contract)
/////////////////////////////////////////
[OperationContract]
public List<User> GetUser(ServicePredicateBuilder<User> servicePredicateBuilder)
{
using(ModelEntities context = new ModelEntities())
{
// IEnumerable Sorting Func
Func<IEnumerable<User>, IOrderedQueryable<User>> enumerableSortingFunc = servicePredicateBuilder.SortCondition.GetIEnumerableSortingFunc();
// IQueryable sorting Func
Func<IQueryable<User>, IOrderedQueryable<User>> queryableSortingFunc = servicePredicateBuilder.SortCondition.GetIQueryableSortingFunc();
// expression for filtering
Expression<Func<User, bool>> predicate = servicePredicateBuilder.Criteria.GetExpression();
return enumerableSortingFunc(context.where(predicate));
}
}
History
15th August 2015: First Post
6th October 2015: fix a bug in GetIEnumerableSortingExpression
29th November 2015: Support > >= < <= operators for string fields
30th November 2015: Fix a bug in GetExpression
method