diff --git a/Kendo.DynamicLinq.Tests/SerializationTests.cs b/Kendo.DynamicLinq.Tests/SerializationTests.cs
index a023ad9..febb8f0 100644
--- a/Kendo.DynamicLinq.Tests/SerializationTests.cs
+++ b/Kendo.DynamicLinq.Tests/SerializationTests.cs
@@ -41,7 +41,7 @@ public void DataContractJsonSerializerSerializesAggregates()
serializer.WriteObject(stream, people.AsQueryable().ToDataSourceResult(1, 2, null, null, new [] { new Aggregator {
Aggregate = "sum",
Field = "Age"
- } }));
+ } },null));
var json = Encoding.UTF8.GetString(stream.ToArray()).Replace("\"__type\":\"DynamicClass2:#\",", "");
diff --git a/Kendo.DynamicLinq/DataSourceRequest.cs b/Kendo.DynamicLinq/DataSourceRequest.cs
index ee2b358..7764eaa 100644
--- a/Kendo.DynamicLinq/DataSourceRequest.cs
+++ b/Kendo.DynamicLinq/DataSourceRequest.cs
@@ -20,7 +20,17 @@ public class DataSourceRequest
///
/// Specifies the requested sort order.
///
- public IEnumerable Sort { get; set; }
+ public IEnumerable Sort { get; set; }
+
+ ///
+ /// Specifies the requested grouping.
+ ///
+ public IEnumerable Group { get; set; }
+
+ ///
+ /// Specifies the requested aggregators.
+ ///
+ public IEnumerable Aggregate { get; set; }
///
/// Specifies the requested filter.
diff --git a/Kendo.DynamicLinq/DataSourceResult.cs b/Kendo.DynamicLinq/DataSourceResult.cs
index d3b6ce1..80ba7f5 100644
--- a/Kendo.DynamicLinq/DataSourceResult.cs
+++ b/Kendo.DynamicLinq/DataSourceResult.cs
@@ -1,34 +1,38 @@
using System;
-using System.Linq;
using System.Collections;
-using System.Collections.Generic;
+using System.Linq;
using System.Runtime.Serialization;
namespace Kendo.DynamicLinq
{
///
- /// Describes the result of Kendo DataSource read operation.
+ /// Describes the result of Kendo DataSource read operation.
///
[KnownType("GetKnownTypes")]
public class DataSourceResult
{
///
- /// Represents a single page of processed data.
+ /// Represents a single page of processed data.
///
public IEnumerable Data { get; set; }
///
- /// The total number of records available.
+ /// Represents a single page of processed grouped data.
+ ///
+ public IEnumerable Group { get; set; }
+
+ ///
+ /// The total number of records available.
///
public int Total { get; set; }
///
- /// Represents a requested aggregates.
+ /// Represents a requested aggregates.
///
public object Aggregates { get; set; }
///
- /// Used by the KnownType attribute which is required for WCF serialization support
+ /// Used by the KnownType attribute which is required for WCF serialization support
///
///
private static Type[] GetKnownTypes()
@@ -47,4 +51,4 @@ private static Type[] GetKnownTypes()
.ToArray();
}
}
-}
+}
\ No newline at end of file
diff --git a/Kendo.DynamicLinq/EnumerableExtenstions.cs b/Kendo.DynamicLinq/EnumerableExtenstions.cs
new file mode 100644
index 0000000..c51250b
--- /dev/null
+++ b/Kendo.DynamicLinq/EnumerableExtenstions.cs
@@ -0,0 +1,90 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Linq.Dynamic;
+
+namespace Kendo.DynamicLinq
+{
+ public static class EnumerableExtenstions
+ {
+ public static dynamic GroupByMany(this IEnumerable elements,
+ IEnumerable groupSelectors)
+ {
+ //create a new list of Kendo Group Selectors
+ var selectors = new List>(groupSelectors.Count());
+ foreach (var selector in groupSelectors)
+ {
+ //compile the Dynamic Expression Lambda for each one
+ var expression =
+ DynamicExpression.ParseLambda(typeof(TElement), typeof(object), selector.Field);
+ //add it to the list
+ selectors.Add(new GroupSelector
+ {
+ Selector = (Func) expression.Compile(),
+ Field = selector.Field,
+ Aggregates = selector.Aggregates
+ });
+ }
+ //call the actual group by method
+ return elements.GroupByMany(selectors.ToArray());
+ }
+ //returned value should look like the following
+ // [{
+ // aggregates: {
+ // FIEL1DNAME: {
+ // FUNCTON1NAME: FUNCTION1VALUE,
+ // FUNCTON2NAME: FUNCTION2VALUE
+ // },
+ // FIELD2NAME: {
+ // FUNCTON1NAME: FUNCTION1VALUE
+ // }
+ // },
+ // field: FIELDNAME, // the field by which the data items are grouped
+ // hasSubgroups: true, // true if there are subgroups
+ // items: [
+ // // either the subgroups or the data items
+ // {
+ // aggregates: {
+ // //nested group aggregates
+ // },
+ // field: NESTEDGROUPFIELDNAME,
+ // hasSubgroups: false,
+ // items: [
+ // // data records
+ // ],
+ // value: NESTEDGROUPVALUE
+ // },
+ // //nestedgroup2, nestedgroup3, etc.
+ // ],
+ // value: VALUE // the group key
+ //} /* other groups */
+ //]
+ //for more info check http://docs.telerik.com/kendo-ui/api/javascript/data/datasource#configuration-schema.groups
+
+ public static dynamic GroupByMany(this IEnumerable elements,
+ params GroupSelector[] groupSelectors)
+ {
+ if (groupSelectors.Length > 0)
+ {
+ //get selector
+ var selector = groupSelectors.First();
+ var nextSelectors = groupSelectors.Skip(1).ToArray(); //reduce the list recursively until zero
+ return
+ //group by and return
+ elements.GroupBy(selector.Selector).Select(
+ g => new GroupResult
+ {
+ Value = g.Key,
+ Aggregates = selector.Aggregates,
+ HasSubgroups = groupSelectors.Length > 1,
+ Count = g.Count(),
+ //recursivly group the next selectors
+ Items = g.GroupByMany(nextSelectors),
+ SelectorField = selector.Field
+ });
+ }
+ //if there are not more group selectors return data
+ return elements;
+ }
+ }
+}
\ No newline at end of file
diff --git a/Kendo.DynamicLinq/Group.cs b/Kendo.DynamicLinq/Group.cs
new file mode 100644
index 0000000..aada77f
--- /dev/null
+++ b/Kendo.DynamicLinq/Group.cs
@@ -0,0 +1,11 @@
+using System.Collections.Generic;
+using System.Runtime.Serialization;
+
+namespace Kendo.DynamicLinq
+{
+ public class Group : Sort
+ {
+ [DataMember(Name = "aggregates")]
+ public IEnumerable Aggregates { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/Kendo.DynamicLinq/GroupResult.cs b/Kendo.DynamicLinq/GroupResult.cs
new file mode 100644
index 0000000..68357e5
--- /dev/null
+++ b/Kendo.DynamicLinq/GroupResult.cs
@@ -0,0 +1,36 @@
+using System.Collections.Generic;
+using System.Runtime.Serialization;
+
+namespace Kendo.DynamicLinq
+{
+ [DataContract(Name = "groupresult")]
+ public class GroupResult
+ {
+ //small letter properties are kendo js properties so please execuse the warnings
+ //for more info check http://docs.telerik.com/kendo-ui/api/javascript/data/datasource#configuration-schema.groups
+ [DataMember(Name = "value")]
+ public object Value { get; set; }
+
+ public string SelectorField { get; set; }
+ [DataMember(Name = "field")]
+ public string Field
+ {
+ get { return string.Format("{0} ({1})", this.SelectorField, this.Count); }
+ }
+ public int Count { get; set; }
+
+ [DataMember(Name = "aggregates")]
+ public IEnumerable Aggregates { get; set; }
+
+ [DataMember(Name = "items")]
+ public dynamic Items { get; set; }
+
+ [DataMember(Name = "hasSubgroups")]
+ public bool HasSubgroups { get; set; } // true if there are subgroups
+
+ public override string ToString()
+ {
+ return string.Format("{0} ({1})", this.Value, this.Count);
+ }
+ }
+}
\ No newline at end of file
diff --git a/Kendo.DynamicLinq/GroupSelector.cs b/Kendo.DynamicLinq/GroupSelector.cs
new file mode 100644
index 0000000..6fb2e90
--- /dev/null
+++ b/Kendo.DynamicLinq/GroupSelector.cs
@@ -0,0 +1,13 @@
+using System;
+using System.Collections.Generic;
+
+namespace Kendo.DynamicLinq
+{
+ public class GroupSelector
+ {
+ public Func Selector { get; set; }
+ public string Field { get; set; }
+ public IEnumerable Aggregates { get; set; }
+
+ }
+}
\ No newline at end of file
diff --git a/Kendo.DynamicLinq/Kendo.DynamicLinq.csproj b/Kendo.DynamicLinq/Kendo.DynamicLinq.csproj
index 3c5fd88..6af882a 100644
--- a/Kendo.DynamicLinq/Kendo.DynamicLinq.csproj
+++ b/Kendo.DynamicLinq/Kendo.DynamicLinq.csproj
@@ -1,64 +1,69 @@
-
-
-
- Release
- AnyCPU
- 8.0.30703
- 2.0
- {2BD75D53-E0EA-4995-8B0F-60AD709945AC}
- Library
- Properties
- Kendo.DynamicLinq
- Kendo.DynamicLinq
- v4.0
- 512
-
-
- true
- full
- false
- bin\Debug\
- DEBUG;TRACE
- prompt
- 4
-
-
- pdbonly
- true
- bin\Release\
- TRACE
- prompt
- 4
-
-
-
-
-
-
-
- ..\packages\System.Linq.Dynamic.1.0.0\lib\net40\System.Linq.Dynamic.dll
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+ Release
+ AnyCPU
+ 8.0.30703
+ 2.0
+ {2BD75D53-E0EA-4995-8B0F-60AD709945AC}
+ Library
+ Properties
+ Kendo.DynamicLinq
+ Kendo.DynamicLinq
+ v4.0
+ 512
+
+
+ true
+ full
+ false
+ bin\Debug\
+ DEBUG;TRACE
+ prompt
+ 4
+
+
+ pdbonly
+ true
+ bin\Release\
+ TRACE
+ prompt
+ 4
+
+
+
+
+
+
+
+ ..\packages\System.Linq.Dynamic.1.0.7\lib\net40\System.Linq.Dynamic.dll
+ True
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ -->
\ No newline at end of file
diff --git a/Kendo.DynamicLinq/QueryableExtensions.cs b/Kendo.DynamicLinq/QueryableExtensions.cs
index 7944e12..8c156d0 100644
--- a/Kendo.DynamicLinq/QueryableExtensions.cs
+++ b/Kendo.DynamicLinq/QueryableExtensions.cs
@@ -1,169 +1,329 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Linq.Dynamic;
-using System.Linq.Expressions;
-using DynamicExpression = System.Linq.Dynamic.DynamicExpression;
-
-namespace Kendo.DynamicLinq
-{
- public static class QueryableExtensions
- {
- ///
- /// Applies data processing (paging, sorting, filtering and aggregates) over IQueryable using Dynamic Linq.
- ///
- /// The type of the IQueryable.
- /// The IQueryable which should be processed.
- /// Specifies how many items to take. Configurable via the pageSize setting of the Kendo DataSource.
- /// Specifies how many items to skip.
- /// Specifies the current sort order.
- /// Specifies the current filter.
- /// Specifies the current aggregates.
- /// A DataSourceResult object populated from the processed IQueryable.
- public static DataSourceResult ToDataSourceResult(this IQueryable queryable, int take, int skip, IEnumerable sort, Filter filter, IEnumerable aggregates)
- {
- // Filter the data first
- queryable = Filter(queryable, filter);
-
- // Calculate the total number of records (needed for paging)
- var total = queryable.Count();
-
- // Calculate the aggregates
- var aggregate = Aggregate(queryable, aggregates);
-
- // Sort the data
- queryable = Sort(queryable, sort);
-
- // Finally page the data
- if (take > 0)
- {
- queryable = Page(queryable, take, skip);
- }
-
- return new DataSourceResult
- {
- Data = queryable.ToList(),
- Total = total,
- Aggregates = aggregate
- };
- }
-
- ///
- /// Applies data processing (paging, sorting and filtering) over IQueryable using Dynamic Linq.
- ///
- /// The type of the IQueryable.
- /// The IQueryable which should be processed.
- /// Specifies how many items to take. Configurable via the pageSize setting of the Kendo DataSource.
- /// Specifies how many items to skip.
- /// Specifies the current sort order.
- /// Specifies the current filter.
- /// A DataSourceResult object populated from the processed IQueryable.
- public static DataSourceResult ToDataSourceResult(this IQueryable queryable, int take, int skip, IEnumerable sort, Filter filter)
- {
- return queryable.ToDataSourceResult(take, skip, sort, filter, null);
- }
-
- ///
- /// Applies data processing (paging, sorting and filtering) over IQueryable using Dynamic Linq.
- ///
- /// The type of the IQueryable.
- /// The IQueryable which should be processed.
- /// The DataSourceRequest object containing take, skip, order, and filter data.
- /// A DataSourceResult object populated from the processed IQueryable.
- public static DataSourceResult ToDataSourceResult(this IQueryable queryable, DataSourceRequest request)
- {
- return queryable.ToDataSourceResult(request.Take, request.Skip, request.Sort, request.Filter, null);
- }
-
- private static IQueryable Filter(IQueryable queryable, Filter filter)
- {
- if (filter != null && filter.Logic != null)
- {
- // Collect a flat list of all filters
- var filters = filter.All();
-
- // Get all filter values as array (needed by the Where method of Dynamic Linq)
- var values = filters.Select(f => f.Value).ToArray();
-
- // Create a predicate expression e.g. Field1 = @0 And Field2 > @1
- string predicate = filter.ToExpression(filters);
-
- // Use the Where method of Dynamic Linq to filter the data
- queryable = queryable.Where(predicate, values);
- }
-
- return queryable;
- }
-
- private static object Aggregate(IQueryable queryable, IEnumerable aggregates)
- {
- if (aggregates != null && aggregates.Any())
- {
- var objProps = new Dictionary();
- var groups = aggregates.GroupBy(g => g.Field);
- Type type = null;
- foreach (var group in groups)
- {
- var fieldProps = new Dictionary();
- foreach (var aggregate in group)
- {
- var prop = typeof (T).GetProperty(aggregate.Field);
- var param = Expression.Parameter(typeof (T), "s");
- var selector = aggregate.Aggregate == "count" && (Nullable.GetUnderlyingType(prop.PropertyType) != null)
- ? Expression.Lambda(Expression.NotEqual(Expression.MakeMemberAccess(param, prop), Expression.Constant(null, prop.PropertyType)), param)
- : Expression.Lambda(Expression.MakeMemberAccess(param, prop), param);
- var mi = aggregate.MethodInfo(typeof (T));
- if (mi == null)
- continue;
-
- var val = queryable.Provider.Execute(Expression.Call(null, mi,
- aggregate.Aggregate == "count" && (Nullable.GetUnderlyingType(prop.PropertyType) == null)
- ? new[] { queryable.Expression }
- : new[] { queryable.Expression, Expression.Quote(selector) }));
-
- fieldProps.Add(new DynamicProperty(aggregate.Aggregate, typeof(object)), val);
- }
- type = DynamicExpression.CreateClass(fieldProps.Keys);
- var fieldObj = Activator.CreateInstance(type);
- foreach (var p in fieldProps.Keys)
- type.GetProperty(p.Name).SetValue(fieldObj, fieldProps[p], null);
- objProps.Add(new DynamicProperty(group.Key, fieldObj.GetType()), fieldObj);
- }
-
- type = DynamicExpression.CreateClass(objProps.Keys);
-
- var obj = Activator.CreateInstance(type);
-
- foreach (var p in objProps.Keys)
- {
- type.GetProperty(p.Name).SetValue(obj, objProps[p], null);
- }
-
- return obj;
- }
- else
- {
- return null;
- }
- }
-
- private static IQueryable Sort(IQueryable queryable, IEnumerable sort)
- {
- if (sort != null && sort.Any())
- {
- // Create ordering expression e.g. Field1 asc, Field2 desc
- var ordering = String.Join(",", sort.Select(s => s.ToExpression()));
-
- // Use the OrderBy method of Dynamic Linq to sort the data
- return queryable.OrderBy(ordering);
- }
-
- return queryable;
- }
-
- private static IQueryable Page(IQueryable queryable, int take, int skip)
- {
- return queryable.Skip(skip).Take(take);
- }
- }
-}
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Linq.Dynamic;
+using System.Linq.Expressions;
+using System.Reflection;
+using DynamicExpression = System.Linq.Dynamic.DynamicExpression;
+
+namespace Kendo.DynamicLinq
+{
+ public static class QueryableExtensions
+ {
+ ///
+ /// Applies data processing (paging, sorting, filtering and aggregates) over IQueryable using Dynamic Linq.
+ ///
+ /// The type of the IQueryable.
+ /// The IQueryable which should be processed.
+ /// Specifies how many items to take. Configurable via the pageSize setting of the Kendo DataSource.
+ /// Specifies how many items to skip.
+ /// Specifies the current sort order.
+ /// Specifies the current filter.
+ /// Specifies the current aggregates.
+ /// A DataSourceResult object populated from the processed IQueryable.
+ public static DataSourceResult ToDataSourceResult(this IQueryable queryable, int take, int skip,
+ IEnumerable sort, Filter filter, IEnumerable aggregates, IEnumerable group)
+ {
+ //the way this extension works it pages the records using skip and take
+ //in order to do that we need at least one sorted property
+ if (sort != null && !sort.Any())
+ {
+ var elementType = queryable.ElementType;
+ var properties = elementType.GetProperties().ToList();
+ //by default make dir desc
+ var sortByObject = new Sort
+ {
+ Dir = "desc"
+ };
+ PropertyInfo propertyInfo;
+ //look for property that is called id
+ if (properties.Any(p => p.Name.ToLower() == "id"))
+ {
+ propertyInfo = properties.FirstOrDefault(p => p.Name.ToLower() == "id");
+ }
+ //or contains id
+ else if (properties.Any(p => p.Name.ToLower().Contains("id")))
+ {
+ propertyInfo = properties.FirstOrDefault(p => p.Name.ToLower().Contains("id"));
+ }
+ //or just get the first property
+ else
+ {
+ propertyInfo = properties.FirstOrDefault();
+ }
+ if (propertyInfo != null)
+ {
+ sortByObject.Field = propertyInfo.Name;
+ }
+ sort = new List {sortByObject};
+ }
+ else
+ {
+ sort = new List();
+ }
+ // Filter the data first
+ queryable = Filter(queryable, filter);
+
+ // Calculate the total number of records (needed for paging)
+ var total = queryable.Count();
+
+ // Calculate the aggregates
+ var aggregate = Aggregate(queryable, aggregates);
+
+ if (group != null && group.Any())
+ {
+ foreach (var source in group.Reverse())
+ {
+ sort = sort.Append(new Sort()
+ {
+ Field = source.Field,
+ Dir = source.Dir
+ });
+ }
+ }
+
+ // Sort the data
+ queryable = Sort(queryable, sort);
+
+ // Finally page the data
+ if (take > 0)
+ {
+ queryable = Page(queryable, take, skip, sort.Any());
+ }
+
+ var result = new DataSourceResult
+ {
+ Total = total,
+ Aggregates = aggregate
+ };
+ // Group By
+ if (group != null && group.Any())
+ {
+ var groupedQuery = queryable.ToList().GroupByMany(group);
+ result.Group = groupedQuery;
+ }
+ else
+ {
+ result.Data = queryable.ToList();
+ }
+ return result;
+ }
+
+ ///
+ /// Applies data processing (paging, sorting and filtering) over IQueryable using Dynamic Linq.
+ ///
+ /// The type of the IQueryable.
+ /// The IQueryable which should be processed.
+ /// Specifies how many items to take. Configurable via the pageSize setting of the Kendo DataSource.
+ /// Specifies how many items to skip.
+ /// Specifies the current sort order.
+ /// Specifies the current filter.
+ /// A DataSourceResult object populated from the processed IQueryable.
+ public static DataSourceResult ToDataSourceResult(this IQueryable queryable, int take, int skip,
+ IEnumerable sort, Filter filter)
+ {
+ return queryable.ToDataSourceResult(take, skip, sort, filter, null, null);
+ }
+
+ ///
+ /// Applies data processing (paging, sorting and filtering) over IQueryable using Dynamic Linq.
+ ///
+ /// The type of the IQueryable.
+ /// The IQueryable which should be processed.
+ /// The DataSourceRequest object containing take, skip, order, and filter data.
+ /// A DataSourceResult object populated from the processed IQueryable.
+ public static DataSourceResult ToDataSourceResult(this IQueryable queryable, DataSourceRequest request)
+ {
+ return queryable.ToDataSourceResult(request.Take, request.Skip, request.Sort, request.Filter, null,
+ request.Group);
+ }
+
+ public static IEnumerable Append(this IEnumerable source, T item)
+ {
+ foreach (T i in source)
+ yield return i;
+
+ yield return item;
+ }
+
+ public static IEnumerable Prepend(this IEnumerable source, T item)
+ {
+ yield return item;
+
+ foreach (T i in source)
+ yield return i;
+ }
+
+ private static IQueryable Filter(IQueryable queryable, Filter filter)
+ {
+ if ((filter != null) && (filter.Logic != null))
+ {
+ // Collect a flat list of all filters
+ var filters = filter.All();
+
+ // Get all filter values as array (needed by the Where method of Dynamic Linq)
+ var values = filters.Select(f => f.Value is string ? f.Value.ToString().ToLower() : f.Value).ToArray();
+
+ ////Add toLower() for all filter Fields with type of string in the values
+ for (var i = 0; i < values.Length; i++)
+ {
+ if (values[i] is string)
+ {
+ filters[i].Field = string.Format("{0}.ToString().ToLower()", filters[i].Field);
+ }
+ // when we have a decimal value it gets converted to double and the query will break
+ if (values[i] is double)
+ {
+ values[i] = Convert.ToDecimal(values[i]);
+ }
+ if (values[i] is DateTime)
+ {
+ var dateTimeFilterValue = (DateTime) values[i];
+ values[i] = new DateTime(dateTimeFilterValue.Year, dateTimeFilterValue.Month,
+ dateTimeFilterValue.Day, 0, 0, 0);
+ }
+ }
+
+ var valuesList = values.ToList();
+
+ //Remove duplicate filters
+ //NOTE: we loop, and don't use .distinct for a reason!
+ //There is a miniscule chance different columns will filter by the same value, in which case using distinct will remove too many filters
+ for (int i = filters.Count - 1; i >= 0; i--)
+ {
+ var previousFilter = filters.ElementAtOrDefault(i - 1);
+
+ if (previousFilter != null && filters[i].Equals(previousFilter))
+ {
+ filters.RemoveAt(i);
+
+ valuesList.RemoveAt(i);
+ }
+ }
+ var filtersList = filters.ToList();
+ for (int i = 0; i < filters.Count; i++)
+ {
+ if (filters[i].Value is DateTime && filters[i].Operator == "eq")
+ {
+ var filterToEdit = filtersList[i];
+
+ //Copy the date from the filter
+ var baseDate = ((DateTime) filters[i].Value).Date;
+
+ //Instead of comparing for exact equality, we compare as greater than the start of the day...
+ filterToEdit.Value = new DateTime(baseDate.Year, baseDate.Month, baseDate.Day, 0, 0, 0);
+ filterToEdit.Operator = "gte";
+ valuesList[i] = filterToEdit.Value;
+
+ //...and less than the end of that same day (we're making an additional filter here)
+ var newFilter = new Filter()
+ {
+ Value = new DateTime(baseDate.Year, baseDate.Month, baseDate.Day, 23, 59, 59),
+ Field = filters[i].Field,
+ Filters = filters[i].Filters,
+ Operator = "lte",
+ Logic = "and"
+ };
+
+ //Add that additional filter to the list of filters
+ filtersList.Add(newFilter);
+ valuesList.Add(newFilter.Value);
+ }
+ }
+
+ values = valuesList.ToArray();
+ filters = filtersList;
+ //Set the filters, since we may have editted them
+ filter.Filters = filtersList;
+
+ // Create a predicate expression e.g. Field1 = @0 And Field2 > @1
+ var predicate = filter.ToExpression(filters);
+
+ // Use the Where method of Dynamic Linq to filter the data
+ queryable = queryable.Where(predicate, values);
+ }
+
+ return queryable;
+ }
+
+ private static object Aggregate(IQueryable queryable, IEnumerable aggregates)
+ {
+ if ((aggregates != null) && aggregates.Any())
+ {
+ var objProps = new Dictionary();
+ var groups = aggregates.GroupBy(g => g.Field);
+ Type type = null;
+ foreach (var group in groups)
+ {
+ var fieldProps = new Dictionary();
+ foreach (var aggregate in group)
+ {
+ var prop = typeof(T).GetProperty(aggregate.Field);
+ var param = Expression.Parameter(typeof(T), "s");
+ var selector = (aggregate.Aggregate == "count") &&
+ (Nullable.GetUnderlyingType(prop.PropertyType) != null)
+ ? Expression.Lambda(
+ Expression.NotEqual(Expression.MakeMemberAccess(param, prop),
+ Expression.Constant(null, prop.PropertyType)), param)
+ : Expression.Lambda(Expression.MakeMemberAccess(param, prop), param);
+ var mi = aggregate.MethodInfo(typeof(T));
+ if (mi == null)
+ {
+ continue;
+ }
+
+ var val = queryable.Provider.Execute(Expression.Call(null, mi,
+ (aggregate.Aggregate == "count") &&
+ (Nullable.GetUnderlyingType(prop.PropertyType) == null)
+ ? new[] {queryable.Expression}
+ : new[] {queryable.Expression, Expression.Quote(selector)}));
+
+ fieldProps.Add(new DynamicProperty(aggregate.Aggregate, typeof(object)), val);
+ }
+ type = DynamicExpression.CreateClass(fieldProps.Keys);
+ var fieldObj = Activator.CreateInstance(type);
+ foreach (var p in fieldProps.Keys)
+ {
+ type.GetProperty(p.Name).SetValue(fieldObj, fieldProps[p], null);
+ }
+ objProps.Add(new DynamicProperty(group.Key, fieldObj.GetType()), fieldObj);
+ }
+
+ type = DynamicExpression.CreateClass(objProps.Keys);
+
+ var obj = Activator.CreateInstance(type);
+
+ foreach (var p in objProps.Keys)
+ {
+ type.GetProperty(p.Name).SetValue(obj, objProps[p], null);
+ }
+
+ return obj;
+ }
+ return null;
+ }
+
+ private static IQueryable Sort(IQueryable queryable, IEnumerable sort)
+ {
+ if ((sort != null) && sort.Any())
+ {
+ // Create ordering expression e.g. Field1 asc, Field2 desc
+ var ordering = string.Join(",", sort.Reverse().Select(s => s.ToExpression()));
+
+ // Use the OrderBy method of Dynamic Linq to sort the data
+ return queryable.OrderBy(ordering);
+ }
+
+ return queryable;
+ }
+
+ private static IQueryable Page(IQueryable queryable, int take, int skip, bool sorted)
+ {
+ if (sorted)
+ {
+ return queryable.Skip(skip).Take(take);
+ }
+ return queryable.Take(take);
+ }
+ }
+}
\ No newline at end of file
diff --git a/Kendo.DynamicLinq/packages.config b/Kendo.DynamicLinq/packages.config
index 616a92b..46cb869 100644
--- a/Kendo.DynamicLinq/packages.config
+++ b/Kendo.DynamicLinq/packages.config
@@ -1,4 +1,4 @@
-
-
-
+
+
+
\ No newline at end of file
diff --git a/packages/System.Linq.Dynamic.1.0.0/System.Linq.Dynamic.1.0.0.nupkg b/packages/System.Linq.Dynamic.1.0.0/System.Linq.Dynamic.1.0.0.nupkg
deleted file mode 100644
index a54625a..0000000
Binary files a/packages/System.Linq.Dynamic.1.0.0/System.Linq.Dynamic.1.0.0.nupkg and /dev/null differ
diff --git a/packages/System.Linq.Dynamic.1.0.0/System.Linq.Dynamic.1.0.0.nuspec b/packages/System.Linq.Dynamic.1.0.0/System.Linq.Dynamic.1.0.0.nuspec
deleted file mode 100644
index d007f95..0000000
--- a/packages/System.Linq.Dynamic.1.0.0/System.Linq.Dynamic.1.0.0.nuspec
+++ /dev/null
@@ -1,14 +0,0 @@
-
-
-
- System.Linq.Dynamic
- 1.0.0
- Microsoft
- Microsoft
- http://www.opensource.org/licenses/ms-pl
- https://github.com/kahanu/System.Linq.Dynamic
- false
- This is the Microsoft assembly for the .Net 4.0 Dynamic language functionality.
- system linq dynamic
-
-
\ No newline at end of file
diff --git a/packages/System.Linq.Dynamic.1.0.0/lib/net40/System.Linq.Dynamic.dll b/packages/System.Linq.Dynamic.1.0.0/lib/net40/System.Linq.Dynamic.dll
deleted file mode 100644
index 4338697..0000000
Binary files a/packages/System.Linq.Dynamic.1.0.0/lib/net40/System.Linq.Dynamic.dll and /dev/null differ
diff --git a/packages/System.Linq.Dynamic.1.0.7/NuGet.exe b/packages/System.Linq.Dynamic.1.0.7/NuGet.exe
new file mode 100644
index 0000000..8dd7e45
Binary files /dev/null and b/packages/System.Linq.Dynamic.1.0.7/NuGet.exe differ
diff --git a/packages/System.Linq.Dynamic.1.0.7/System.Linq.Dynamic.1.0.7.nupkg b/packages/System.Linq.Dynamic.1.0.7/System.Linq.Dynamic.1.0.7.nupkg
new file mode 100644
index 0000000..3e22503
Binary files /dev/null and b/packages/System.Linq.Dynamic.1.0.7/System.Linq.Dynamic.1.0.7.nupkg differ
diff --git a/packages/System.Linq.Dynamic.1.0.7/lib/net40/System.Linq.Dynamic.dll b/packages/System.Linq.Dynamic.1.0.7/lib/net40/System.Linq.Dynamic.dll
new file mode 100644
index 0000000..46ff770
Binary files /dev/null and b/packages/System.Linq.Dynamic.1.0.7/lib/net40/System.Linq.Dynamic.dll differ
diff --git a/packages/repositories.config b/packages/repositories.config
index 30d0ea5..d0830f6 100644
--- a/packages/repositories.config
+++ b/packages/repositories.config
@@ -1,5 +1,5 @@
-
-
-
-
+
+
+
+
\ No newline at end of file