Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / web / ASP.NET

ServicePredicateBuilder for creating Serializable Expressions

5.00/5 (8 votes)
30 Nov 2015CPOL 38.3K   269  
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

C#
[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

C#
/////////////////////////////////////////
// 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&#39;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

License

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