├── .gitignore ├── .travis.yml ├── Kendo.DynamicLinq.Tests ├── Kendo.DynamicLinq.Tests.csproj ├── Properties │ └── AssemblyInfo.cs ├── SerializationTests.cs └── packages.config ├── Kendo.DynamicLinq.sln ├── Kendo.DynamicLinq ├── Aggregate.cs ├── DataSourceRequest.cs ├── DataSourceResult.cs ├── Filter.cs ├── Kendo.DynamicLinq.csproj ├── Kendo.DynamicLinq.nuspec ├── Properties │ └── AssemblyInfo.cs ├── QueryableExtensions.cs ├── Sort.cs └── packages.config ├── README.md ├── packages ├── NUnit.2.6.3 │ ├── NUnit.2.6.3.nupkg │ ├── NUnit.2.6.3.nuspec │ ├── lib │ │ ├── nunit.framework.dll │ │ └── nunit.framework.xml │ └── license.txt ├── System.Linq.Dynamic.1.0.0 │ ├── System.Linq.Dynamic.1.0.0.nupkg │ ├── System.Linq.Dynamic.1.0.0.nuspec │ └── lib │ │ └── net40 │ │ └── System.Linq.Dynamic.dll └── repositories.config └── travis.proj /.gitignore: -------------------------------------------------------------------------------- 1 | # Build Folders (you can keep bin if you'd like, to store dlls and pdbs) 2 | [Bb]in/ 3 | [Oo]bj/ 4 | 5 | ## Ignore Visual Studio temporary files, build results, and 6 | ## files generated by popular Visual Studio add-ons. 7 | 8 | # User-specific files 9 | *.suo 10 | *.user 11 | *.sln.docstates 12 | 13 | # Build results 14 | [Dd]ebug/ 15 | [Rr]elease/ 16 | x64/ 17 | *_i.c 18 | *_p.c 19 | *.ilk 20 | *.meta 21 | *.obj 22 | *.pch 23 | *.pdb 24 | *.pgc 25 | *.pgd 26 | *.rsp 27 | *.sbr 28 | *.tlb 29 | *.tli 30 | *.tlh 31 | *.tmp 32 | *.log 33 | *.vspscc 34 | *.vssscc 35 | .builds 36 | 37 | # Publish Web Output 38 | *.Publish.xml 39 | 40 | Kendo.DynamicLinq.*.nupkg 41 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: c 2 | 3 | cache: apt 4 | 5 | install: 6 | - sudo apt-get install mono-devel mono-gmcs nunit-console 7 | 8 | script: 9 | - xbuild travis.proj 10 | - nunit-console ./Kendo.DynamicLinq.Tests/bin/Debug/Kendo.DynamicLinq.Tests.dll 11 | 12 | -------------------------------------------------------------------------------- /Kendo.DynamicLinq.Tests/Kendo.DynamicLinq.Tests.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {92FF629E-8872-4CCE-B558-F9FE437DDA86} 8 | Library 9 | Properties 10 | Kendo.DynamicLinq.Tests 11 | Kendo.DynamicLinq.Tests 12 | v4.0 13 | 512 14 | 15 | 16 | 17 | true 18 | full 19 | false 20 | bin\Debug\ 21 | DEBUG;TRACE 22 | prompt 23 | 4 24 | 25 | 26 | pdbonly 27 | true 28 | bin\Release\ 29 | TRACE 30 | prompt 31 | 4 32 | 33 | 34 | 35 | ..\packages\NUnit.2.6.3\lib\nunit.framework.dll 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | {2bd75d53-e0ea-4995-8b0f-60ad709945ac} 59 | Kendo.DynamicLinq 60 | 61 | 62 | 63 | 70 | -------------------------------------------------------------------------------- /Kendo.DynamicLinq.Tests/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("Kendo.DynamicLinq.Tests")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("Kendo.DynamicLinq.Tests")] 13 | [assembly: AssemblyCopyright("Copyright © 2014")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // The following GUID is for the ID of the typelib if this project is exposed to COM 23 | [assembly: Guid("59ff0f3d-edff-45c3-8e15-691d02dcd302")] 24 | 25 | // Version information for an assembly consists of the following four values: 26 | // 27 | // Major Version 28 | // Minor Version 29 | // Build Number 30 | // Revision 31 | // 32 | // You can specify all the values or you can default the Build and Revision Numbers 33 | // by using the '*' as shown below: 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("1.0.0.0")] 36 | [assembly: AssemblyFileVersion("1.0.0.0")] 37 | -------------------------------------------------------------------------------- /Kendo.DynamicLinq.Tests/SerializationTests.cs: -------------------------------------------------------------------------------- 1 | using NUnit.Framework; 2 | using System.IO; 3 | using System.Linq; 4 | using System.Runtime.Serialization; 5 | using System.Runtime.Serialization.Json; 6 | using System.Text; 7 | 8 | namespace Kendo.DynamicLinq.Tests 9 | { 10 | [KnownType(typeof(Person))] 11 | public class Person 12 | { 13 | public int Age { get; set; } 14 | } 15 | 16 | [TestFixture] 17 | public class SerializationTests 18 | { 19 | [Test] 20 | public void DataContractJsonSerializerSerializesEmptyAggregates() 21 | { 22 | using (var stream = new MemoryStream()) 23 | { 24 | var serializer = new DataContractJsonSerializer(typeof(DataSourceResult)); 25 | 26 | serializer.WriteObject(stream, new[] { "foo" }.AsQueryable().ToDataSourceResult(1, 0, null, null)); 27 | 28 | Assert.AreEqual("{\"Aggregates\":null,\"Data\":[\"foo\"],\"Total\":1}", Encoding.UTF8.GetString(stream.ToArray())); 29 | } 30 | } 31 | 32 | [Test] 33 | public void DataContractJsonSerializerSerializesAggregates() 34 | { 35 | using (var stream = new MemoryStream()) 36 | { 37 | var serializer = new DataContractJsonSerializer(typeof(DataSourceResult), new [] { typeof (Person) }); 38 | 39 | var people = new[] { new Person { Age = 30 }, new Person { Age = 30 } }; 40 | 41 | serializer.WriteObject(stream, people.AsQueryable().ToDataSourceResult(1, 2, null, null, new [] { new Aggregator { 42 | Aggregate = "sum", 43 | Field = "Age" 44 | } })); 45 | 46 | var json = Encoding.UTF8.GetString(stream.ToArray()).Replace("\"__type\":\"DynamicClass2:#\",", ""); 47 | 48 | Assert.AreEqual("{\"Aggregates\":{\"Age\":{\"sum\":60}},\"Data\":[],\"Total\":2}", json); 49 | } 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /Kendo.DynamicLinq.Tests/packages.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | -------------------------------------------------------------------------------- /Kendo.DynamicLinq.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 2013 4 | VisualStudioVersion = 12.0.21005.1 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Kendo.DynamicLinq", "Kendo.DynamicLinq\Kendo.DynamicLinq.csproj", "{2BD75D53-E0EA-4995-8B0F-60AD709945AC}" 7 | EndProject 8 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Kendo.DynamicLinq.Tests", "Kendo.DynamicLinq.Tests\Kendo.DynamicLinq.Tests.csproj", "{92FF629E-8872-4CCE-B558-F9FE437DDA86}" 9 | EndProject 10 | Global 11 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 12 | Debug|Any CPU = Debug|Any CPU 13 | Release|Any CPU = Release|Any CPU 14 | EndGlobalSection 15 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 16 | {2BD75D53-E0EA-4995-8B0F-60AD709945AC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 17 | {2BD75D53-E0EA-4995-8B0F-60AD709945AC}.Debug|Any CPU.Build.0 = Debug|Any CPU 18 | {2BD75D53-E0EA-4995-8B0F-60AD709945AC}.Release|Any CPU.ActiveCfg = Release|Any CPU 19 | {2BD75D53-E0EA-4995-8B0F-60AD709945AC}.Release|Any CPU.Build.0 = Release|Any CPU 20 | {92FF629E-8872-4CCE-B558-F9FE437DDA86}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 21 | {92FF629E-8872-4CCE-B558-F9FE437DDA86}.Debug|Any CPU.Build.0 = Debug|Any CPU 22 | {92FF629E-8872-4CCE-B558-F9FE437DDA86}.Release|Any CPU.ActiveCfg = Release|Any CPU 23 | {92FF629E-8872-4CCE-B558-F9FE437DDA86}.Release|Any CPU.Build.0 = Release|Any CPU 24 | EndGlobalSection 25 | GlobalSection(SolutionProperties) = preSolution 26 | HideSolutionNode = FALSE 27 | EndGlobalSection 28 | EndGlobal 29 | -------------------------------------------------------------------------------- /Kendo.DynamicLinq/Aggregate.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Globalization; 3 | using System.Linq; 4 | using System.Linq.Expressions; 5 | using System.Reflection; 6 | using System.Runtime.Serialization; 7 | 8 | namespace Kendo.DynamicLinq 9 | { 10 | /// 11 | /// Represents a aggregate expression of Kendo DataSource. 12 | /// 13 | [DataContract(Name = "aggregate")] 14 | public class Aggregator 15 | { 16 | /// 17 | /// Gets or sets the name of the aggregated field (property). 18 | /// 19 | [DataMember(Name = "field")] 20 | public string Field { get; set; } 21 | 22 | /// 23 | /// Gets or sets the aggregate. 24 | /// 25 | [DataMember(Name = "aggregate")] 26 | public string Aggregate { get; set; } 27 | 28 | /// 29 | /// Get MethodInfo. 30 | /// 31 | /// Specifies the type of querable data. 32 | /// A MethodInfo for field. 33 | public MethodInfo MethodInfo(Type type) 34 | { 35 | var proptype = type.GetProperty(Field).PropertyType; 36 | switch (Aggregate) 37 | { 38 | case "max": 39 | case "min": 40 | return GetMethod(CultureInfo.InvariantCulture.TextInfo.ToTitleCase(Aggregate), MinMaxFunc().Method, 2).MakeGenericMethod(type, proptype); 41 | case "average": 42 | case "sum": 43 | return GetMethod(CultureInfo.InvariantCulture.TextInfo.ToTitleCase(Aggregate), 44 | ((Func)this.GetType().GetMethod("SumAvgFunc", BindingFlags.Static | BindingFlags.NonPublic) 45 | .MakeGenericMethod(proptype).Invoke(null, null)).Method, 1).MakeGenericMethod(type); 46 | case "count": 47 | return GetMethod(CultureInfo.InvariantCulture.TextInfo.ToTitleCase(Aggregate), 48 | Nullable.GetUnderlyingType(proptype) != null ? CountNullableFunc().Method : CountFunc().Method, 1).MakeGenericMethod(type); 49 | } 50 | return null; 51 | } 52 | 53 | private static MethodInfo GetMethod(string methodName, MethodInfo methodTypes, int genericArgumentsCount) 54 | { 55 | var methods = from method in typeof(Queryable).GetMethods(BindingFlags.Public | BindingFlags.Static) 56 | let parameters = method.GetParameters() 57 | let genericArguments = method.GetGenericArguments() 58 | where method.Name == methodName && 59 | genericArguments.Length == genericArgumentsCount && 60 | parameters.Select(p => p.ParameterType).SequenceEqual((Type[])methodTypes.Invoke(null, genericArguments)) 61 | select method; 62 | return methods.FirstOrDefault(); 63 | } 64 | 65 | private static Func CountNullableFunc() 66 | { 67 | return (T) => new[] 68 | { 69 | typeof(IQueryable<>).MakeGenericType(T), 70 | typeof(Expression<>).MakeGenericType(typeof(Func<,>).MakeGenericType(T, typeof(bool))) 71 | }; 72 | } 73 | 74 | private static Func CountFunc() 75 | { 76 | return (T) => new[] 77 | { 78 | typeof(IQueryable<>).MakeGenericType(T) 79 | }; 80 | } 81 | 82 | private static Func MinMaxFunc() 83 | { 84 | return (T, U) => new[] 85 | { 86 | typeof (IQueryable<>).MakeGenericType(T), 87 | typeof (Expression<>).MakeGenericType(typeof (Func<,>).MakeGenericType(T, U)) 88 | }; 89 | } 90 | 91 | private static Func SumAvgFunc() 92 | { 93 | return (T) => new[] 94 | { 95 | typeof (IQueryable<>).MakeGenericType(T), 96 | typeof (Expression<>).MakeGenericType(typeof (Func<,>).MakeGenericType(T, typeof(U))) 97 | }; 98 | } 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /Kendo.DynamicLinq/DataSourceRequest.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace Kendo.DynamicLinq 4 | { 5 | /// 6 | /// Describes a Kendo Datasource request. 7 | /// 8 | public class DataSourceRequest 9 | { 10 | /// 11 | /// Specifies how many items to take. 12 | /// 13 | public int Take { get; set; } 14 | 15 | /// 16 | /// Specifies how many items to skip. 17 | /// 18 | public int Skip { get; set; } 19 | 20 | /// 21 | /// Specifies the requested sort order. 22 | /// 23 | public IEnumerable Sort { get; set; } 24 | 25 | /// 26 | /// Specifies the requested filter. 27 | /// 28 | public Filter Filter { get; set; } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /Kendo.DynamicLinq/DataSourceResult.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using System.Collections; 4 | using System.Collections.Generic; 5 | using System.Runtime.Serialization; 6 | 7 | namespace Kendo.DynamicLinq 8 | { 9 | /// 10 | /// Describes the result of Kendo DataSource read operation. 11 | /// 12 | [KnownType("GetKnownTypes")] 13 | public class DataSourceResult 14 | { 15 | /// 16 | /// Represents a single page of processed data. 17 | /// 18 | public IEnumerable Data { get; set; } 19 | 20 | /// 21 | /// The total number of records available. 22 | /// 23 | public int Total { get; set; } 24 | 25 | /// 26 | /// Represents a requested aggregates. 27 | /// 28 | public object Aggregates { get; set; } 29 | 30 | /// 31 | /// Used by the KnownType attribute which is required for WCF serialization support 32 | /// 33 | /// 34 | private static Type[] GetKnownTypes() 35 | { 36 | var assembly = AppDomain.CurrentDomain 37 | .GetAssemblies() 38 | .FirstOrDefault(a => a.FullName.StartsWith("DynamicClasses")); 39 | 40 | if (assembly == null) 41 | { 42 | return new Type[0]; 43 | } 44 | 45 | return assembly.GetTypes() 46 | .Where(t => t.Name.StartsWith("DynamicClass")) 47 | .ToArray(); 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /Kendo.DynamicLinq/Filter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using System.Collections.Generic; 4 | using System.Runtime.Serialization; 5 | 6 | namespace Kendo.DynamicLinq 7 | { 8 | /// 9 | /// Represents a filter expression of Kendo DataSource. 10 | /// 11 | [DataContract] 12 | public class Filter 13 | { 14 | /// 15 | /// Gets or sets the name of the sorted field (property). Set to null if the Filters property is set. 16 | /// 17 | [DataMember(Name = "field")] 18 | public string Field { get; set; } 19 | 20 | /// 21 | /// Gets or sets the filtering operator. Set to null if the Filters property is set. 22 | /// 23 | [DataMember(Name = "operator")] 24 | public string Operator { get; set; } 25 | 26 | /// 27 | /// Gets or sets the filtering value. Set to null if the Filters property is set. 28 | /// 29 | [DataMember(Name = "value")] 30 | public object Value { get; set; } 31 | 32 | /// 33 | /// Gets or sets the filtering logic. Can be set to "or" or "and". Set to null unless Filters is set. 34 | /// 35 | [DataMember(Name = "logic")] 36 | public string Logic { get; set; } 37 | 38 | /// 39 | /// Gets or sets the child filter expressions. Set to null if there are no child expressions. 40 | /// 41 | [DataMember(Name = "filters")] 42 | public IEnumerable Filters { get; set; } 43 | 44 | /// 45 | /// Mapping of Kendo DataSource filtering operators to Dynamic Linq 46 | /// 47 | private static readonly IDictionary operators = new Dictionary 48 | { 49 | {"eq", "="}, 50 | {"neq", "!="}, 51 | {"lt", "<"}, 52 | {"lte", "<="}, 53 | {"gt", ">"}, 54 | {"gte", ">="}, 55 | {"startswith", "StartsWith"}, 56 | {"endswith", "EndsWith"}, 57 | {"contains", "Contains"}, 58 | {"doesnotcontain", "Contains"} 59 | }; 60 | 61 | /// 62 | /// Get a flattened list of all child filter expressions. 63 | /// 64 | public IList All() 65 | { 66 | var filters = new List(); 67 | 68 | Collect(filters); 69 | 70 | return filters; 71 | } 72 | 73 | private void Collect(IList filters) 74 | { 75 | if (Filters != null && Filters.Any()) 76 | { 77 | foreach (Filter filter in Filters) 78 | { 79 | filters.Add(filter); 80 | 81 | filter.Collect(filters); 82 | } 83 | } 84 | else 85 | { 86 | filters.Add(this); 87 | } 88 | } 89 | 90 | /// 91 | /// Converts the filter expression to a predicate suitable for Dynamic Linq e.g. "Field1 = @1 and Field2.Contains(@2)" 92 | /// 93 | /// A list of flattened filters. 94 | public string ToExpression(IList filters) 95 | { 96 | if (Filters != null && Filters.Any()) 97 | { 98 | return "(" + String.Join(" " + Logic + " ", Filters.Select(filter => filter.ToExpression(filters)).ToArray()) + ")"; 99 | } 100 | 101 | int index = filters.IndexOf(this); 102 | 103 | string comparison = operators[Operator]; 104 | 105 | if (Operator == "doesnotcontain") 106 | { 107 | return String.Format("!{0}.{1}(@{2})", Field, comparison, index); 108 | } 109 | 110 | if (comparison == "StartsWith" || comparison == "EndsWith" || comparison == "Contains") 111 | { 112 | return String.Format("{0}.{1}(@{2})", Field, comparison, index); 113 | } 114 | 115 | return String.Format("{0} {1} @{2}", Field, comparison, index); 116 | } 117 | } 118 | } 119 | -------------------------------------------------------------------------------- /Kendo.DynamicLinq/Kendo.DynamicLinq.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Release 5 | AnyCPU 6 | 8.0.30703 7 | 2.0 8 | {2BD75D53-E0EA-4995-8B0F-60AD709945AC} 9 | Library 10 | Properties 11 | Kendo.DynamicLinq 12 | Kendo.DynamicLinq 13 | v4.0 14 | 512 15 | 16 | 17 | true 18 | full 19 | false 20 | bin\Debug\ 21 | DEBUG;TRACE 22 | prompt 23 | 4 24 | 25 | 26 | pdbonly 27 | true 28 | bin\Release\ 29 | TRACE 30 | prompt 31 | 4 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | ..\packages\System.Linq.Dynamic.1.0.0\lib\net40\System.Linq.Dynamic.dll 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 64 | -------------------------------------------------------------------------------- /Kendo.DynamicLinq/Kendo.DynamicLinq.nuspec: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | $id$ 5 | $version$ 6 | $title$ 7 | Atanas Korchev 8 | https://github.com/kendo-labs/dlinq-helpers 9 | false 10 | $description$ 11 | Copyright 2013 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /Kendo.DynamicLinq/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.InteropServices; 3 | 4 | // General Information about an assembly is controlled through the following 5 | // set of attributes. Change these attribute values to modify the information 6 | // associated with an assembly. 7 | [assembly: AssemblyTitle("Kendo.DynamicLinq")] 8 | [assembly: AssemblyDescription("Server side paging, sorting and filtering via Dynamic Linq")] 9 | [assembly: AssemblyConfiguration("")] 10 | [assembly: AssemblyProduct("Kendo.DynamicLinq")] 11 | [assembly: AssemblyTrademark("")] 12 | [assembly: AssemblyCulture("")] 13 | 14 | // Setting ComVisible to false makes the types in this assembly not visible 15 | // to COM components. If you need to access a type in this assembly from 16 | // COM, set the ComVisible attribute to true on that type. 17 | [assembly: ComVisible(false)] 18 | 19 | // The following GUID is for the ID of the typelib if this project is exposed to COM 20 | [assembly: Guid("969930e8-6f3d-4369-bdc1-835628cd125f")] 21 | 22 | // Version information for an assembly consists of the following four values: 23 | // 24 | // Major Version 25 | // Minor Version 26 | // Build Number 27 | // Revision 28 | // 29 | // You can specify all the values or you can default the Build and Revision Numbers 30 | // by using the '*' as shown below: 31 | // [assembly: AssemblyVersion("1.0.*")] 32 | [assembly: AssemblyVersion("1.1.2.0")] 33 | [assembly: AssemblyFileVersion("1.1.2.0")] 34 | -------------------------------------------------------------------------------- /Kendo.DynamicLinq/QueryableExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Linq.Dynamic; 5 | using System.Linq.Expressions; 6 | using DynamicExpression = System.Linq.Dynamic.DynamicExpression; 7 | 8 | namespace Kendo.DynamicLinq 9 | { 10 | public static class QueryableExtensions 11 | { 12 | /// 13 | /// Applies data processing (paging, sorting, filtering and aggregates) over IQueryable using Dynamic Linq. 14 | /// 15 | /// The type of the IQueryable. 16 | /// The IQueryable which should be processed. 17 | /// Specifies how many items to take. Configurable via the pageSize setting of the Kendo DataSource. 18 | /// Specifies how many items to skip. 19 | /// Specifies the current sort order. 20 | /// Specifies the current filter. 21 | /// Specifies the current aggregates. 22 | /// A DataSourceResult object populated from the processed IQueryable. 23 | public static DataSourceResult ToDataSourceResult(this IQueryable queryable, int take, int skip, IEnumerable sort, Filter filter, IEnumerable aggregates) 24 | { 25 | // Filter the data first 26 | queryable = Filter(queryable, filter); 27 | 28 | // Calculate the total number of records (needed for paging) 29 | var total = queryable.Count(); 30 | 31 | // Calculate the aggregates 32 | var aggregate = Aggregate(queryable, aggregates); 33 | 34 | // Sort the data 35 | queryable = Sort(queryable, sort); 36 | 37 | // Finally page the data 38 | if (take > 0) 39 | { 40 | queryable = Page(queryable, take, skip); 41 | } 42 | 43 | return new DataSourceResult 44 | { 45 | Data = queryable.ToList(), 46 | Total = total, 47 | Aggregates = aggregate 48 | }; 49 | } 50 | 51 | /// 52 | /// Applies data processing (paging, sorting and filtering) over IQueryable using Dynamic Linq. 53 | /// 54 | /// The type of the IQueryable. 55 | /// The IQueryable which should be processed. 56 | /// Specifies how many items to take. Configurable via the pageSize setting of the Kendo DataSource. 57 | /// Specifies how many items to skip. 58 | /// Specifies the current sort order. 59 | /// Specifies the current filter. 60 | /// A DataSourceResult object populated from the processed IQueryable. 61 | public static DataSourceResult ToDataSourceResult(this IQueryable queryable, int take, int skip, IEnumerable sort, Filter filter) 62 | { 63 | return queryable.ToDataSourceResult(take, skip, sort, filter, null); 64 | } 65 | 66 | /// 67 | /// Applies data processing (paging, sorting and filtering) over IQueryable using Dynamic Linq. 68 | /// 69 | /// The type of the IQueryable. 70 | /// The IQueryable which should be processed. 71 | /// The DataSourceRequest object containing take, skip, order, and filter data. 72 | /// A DataSourceResult object populated from the processed IQueryable. 73 | public static DataSourceResult ToDataSourceResult(this IQueryable queryable, DataSourceRequest request) 74 | { 75 | return queryable.ToDataSourceResult(request.Take, request.Skip, request.Sort, request.Filter, null); 76 | } 77 | 78 | private static IQueryable Filter(IQueryable queryable, Filter filter) 79 | { 80 | if (filter != null && filter.Logic != null) 81 | { 82 | // Collect a flat list of all filters 83 | var filters = filter.All(); 84 | 85 | // Get all filter values as array (needed by the Where method of Dynamic Linq) 86 | var values = filters.Select(f => f.Value).ToArray(); 87 | 88 | // Create a predicate expression e.g. Field1 = @0 And Field2 > @1 89 | string predicate = filter.ToExpression(filters); 90 | 91 | // Use the Where method of Dynamic Linq to filter the data 92 | queryable = queryable.Where(predicate, values); 93 | } 94 | 95 | return queryable; 96 | } 97 | 98 | private static object Aggregate(IQueryable queryable, IEnumerable aggregates) 99 | { 100 | if (aggregates != null && aggregates.Any()) 101 | { 102 | var objProps = new Dictionary(); 103 | var groups = aggregates.GroupBy(g => g.Field); 104 | Type type = null; 105 | foreach (var group in groups) 106 | { 107 | var fieldProps = new Dictionary(); 108 | foreach (var aggregate in group) 109 | { 110 | var prop = typeof (T).GetProperty(aggregate.Field); 111 | var param = Expression.Parameter(typeof (T), "s"); 112 | var selector = aggregate.Aggregate == "count" && (Nullable.GetUnderlyingType(prop.PropertyType) != null) 113 | ? Expression.Lambda(Expression.NotEqual(Expression.MakeMemberAccess(param, prop), Expression.Constant(null, prop.PropertyType)), param) 114 | : Expression.Lambda(Expression.MakeMemberAccess(param, prop), param); 115 | var mi = aggregate.MethodInfo(typeof (T)); 116 | if (mi == null) 117 | continue; 118 | 119 | var val = queryable.Provider.Execute(Expression.Call(null, mi, 120 | aggregate.Aggregate == "count" && (Nullable.GetUnderlyingType(prop.PropertyType) == null) 121 | ? new[] { queryable.Expression } 122 | : new[] { queryable.Expression, Expression.Quote(selector) })); 123 | 124 | fieldProps.Add(new DynamicProperty(aggregate.Aggregate, typeof(object)), val); 125 | } 126 | type = DynamicExpression.CreateClass(fieldProps.Keys); 127 | var fieldObj = Activator.CreateInstance(type); 128 | foreach (var p in fieldProps.Keys) 129 | type.GetProperty(p.Name).SetValue(fieldObj, fieldProps[p], null); 130 | objProps.Add(new DynamicProperty(group.Key, fieldObj.GetType()), fieldObj); 131 | } 132 | 133 | type = DynamicExpression.CreateClass(objProps.Keys); 134 | 135 | var obj = Activator.CreateInstance(type); 136 | 137 | foreach (var p in objProps.Keys) 138 | { 139 | type.GetProperty(p.Name).SetValue(obj, objProps[p], null); 140 | } 141 | 142 | return obj; 143 | } 144 | else 145 | { 146 | return null; 147 | } 148 | } 149 | 150 | private static IQueryable Sort(IQueryable queryable, IEnumerable sort) 151 | { 152 | if (sort != null && sort.Any()) 153 | { 154 | // Create ordering expression e.g. Field1 asc, Field2 desc 155 | var ordering = String.Join(",", sort.Select(s => s.ToExpression())); 156 | 157 | // Use the OrderBy method of Dynamic Linq to sort the data 158 | return queryable.OrderBy(ordering); 159 | } 160 | 161 | return queryable; 162 | } 163 | 164 | private static IQueryable Page(IQueryable queryable, int take, int skip) 165 | { 166 | return queryable.Skip(skip).Take(take); 167 | } 168 | } 169 | } 170 | -------------------------------------------------------------------------------- /Kendo.DynamicLinq/Sort.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using System.Runtime.Serialization; 4 | 5 | namespace Kendo.DynamicLinq 6 | { 7 | /// 8 | /// Represents a sort expression of Kendo DataSource. 9 | /// 10 | [DataContract] 11 | public class Sort 12 | { 13 | /// 14 | /// Gets or sets the name of the sorted field (property). 15 | /// 16 | [DataMember(Name = "field")] 17 | public string Field { get; set; } 18 | 19 | /// 20 | /// Gets or sets the sort direction. Should be either "asc" or "desc". 21 | /// 22 | [DataMember(Name = "dir")] 23 | public string Dir { get; set; } 24 | 25 | /// 26 | /// Converts to form required by Dynamic Linq e.g. "Field1 desc" 27 | /// 28 | public string ToExpression() 29 | { 30 | return Field + " " + Dir; 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /Kendo.DynamicLinq/packages.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | #Kendo.DynamicLinq 2 | 3 | [![Build Status](https://travis-ci.org/kendo-labs/dlinq-helpers.svg?branch=master)](https://travis-ci.org/kendo-labs/dlinq-helpers) 4 | 5 | # Note 6 | Kendo UI Labs projects are experimental and largely built and supported by the community. As such Telerik does not provide official support for any of the Kendo UI Labs projects via Telerik support agreements. We do encourage you to open an issue or visit [Stack Overflow](http://www.stackoverflow.com). 7 | 8 | ## Description 9 | Kendo.DynamicLinq implements server paging, filtering, sorting and aggregating via Dynamic Linq. 10 | 11 | 12 | ## Usage 13 | 1. Add the Kendo.DynamicLinq NuGet package to your project. 14 | 1. Configure your Kendo DataSource to send its options as JSON. 15 | 16 | parameterMap: function(options, type) { 17 | return JSON.stringify(options); 18 | } 19 | 1. Configure the `schema` of the DataSource. 20 | 21 | schema: { 22 | data: "Data", 23 | total: "Total", 24 | aggregates: "Aggregates" 25 | 26 | } 27 | 1. Import the Kendo.DynamicLinq namespace. 28 | 1. Use the `ToDataSourceResult` extension method to apply paging, sorting and filtering. 29 | 30 | [WebMethod] 31 | public static DataSourceResult Products(int take, int skip, IEnumerable sort, Filter filter, IEnumerable aggregates) 32 | { 33 | using (var northwind = new Northwind()) 34 | { 35 | return northwind.Products 36 | .OrderBy(p => p.ProductID) // EF requires ordering for paging 37 | // Use a view model to avoid serializing internal Entity Framework properties as JSON 38 | .Select(p => new ProductViewModel 39 | { 40 | ProductID = p.ProductID, 41 | ProductName = p.ProductName, 42 | UnitPrice = p.UnitPrice, 43 | UnitsInStock = p.UnitsInStock, 44 | Discontinued = p.Discontinued 45 | }) 46 | .ToDataSourceResult(take, skip, sort, filter, aggregates); 47 | } 48 | } 49 | 50 | ## Examples 51 | 52 | The following examples use Kendo.DynamicLinq. 53 | 54 | - [ASP.NET MVC](https://github.com/telerik/kendo-examples-asp-net-mvc/tree/master/grid-crud) 55 | - [ASP.NET Web Forms and Page Methods](https://github.com/telerik/kendo-examples-asp-net/tree/master/grid-page-methods-crud) 56 | - [ASP.NET Web Forms and WCF](https://github.com/telerik/kendo-examples-asp-net/tree/master/grid-wcf-crud) 57 | - [ASP.NET Web Forms and Web Services](https://github.com/telerik/kendo-examples-asp-net/tree/master/grid-web-service-crud) 58 | - [ASP.NET Web Forms and Web API](https://github.com/telerik/kendo-examples-asp-net/tree/master/grid-webapi-crud) 59 | -------------------------------------------------------------------------------- /packages/NUnit.2.6.3/NUnit.2.6.3.nupkg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kendo-labs/dlinq-helpers/459b11d3eded4cef215b51f5b1628be1bbfacd7e/packages/NUnit.2.6.3/NUnit.2.6.3.nupkg -------------------------------------------------------------------------------- /packages/NUnit.2.6.3/NUnit.2.6.3.nuspec: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | NUnit 5 | 2.6.3 6 | NUnit 7 | Charlie Poole 8 | Charlie Poole 9 | http://nunit.org/nuget/license.html 10 | http://nunit.org/ 11 | http://nunit.org/nuget/nunit_32x32.png 12 | false 13 | NUnit features a fluent assert syntax, parameterized, generic and theory tests and is user-extensible. A number of runners, both from the NUnit project and by third parties, are able to execute NUnit tests. 14 | 15 | Version 2.6 is the seventh major release of this well-known and well-tested programming tool. 16 | 17 | This package includes only the framework assembly. You will need to install the NUnit.Runners package unless you are using a third-party runner. 18 | NUnit is a unit-testing framework for all .Net languages with a strong TDD focus. 19 | Version 2.6 is the seventh major release of NUnit. 20 | 21 | Unlike earlier versions, this package includes only the framework assembly. You will need to install the NUnit.Runners package unless you are using a third-party runner. 22 | 23 | The nunit.mocks assembly is now provided by the NUnit.Mocks package. The pnunit.framework assembly is provided by the pNUnit package. 24 | en-US 25 | nunit test testing tdd framework fluent assert theory plugin addin 26 | 27 | -------------------------------------------------------------------------------- /packages/NUnit.2.6.3/lib/nunit.framework.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kendo-labs/dlinq-helpers/459b11d3eded4cef215b51f5b1628be1bbfacd7e/packages/NUnit.2.6.3/lib/nunit.framework.dll -------------------------------------------------------------------------------- /packages/NUnit.2.6.3/license.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kendo-labs/dlinq-helpers/459b11d3eded4cef215b51f5b1628be1bbfacd7e/packages/NUnit.2.6.3/license.txt -------------------------------------------------------------------------------- /packages/System.Linq.Dynamic.1.0.0/System.Linq.Dynamic.1.0.0.nupkg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kendo-labs/dlinq-helpers/459b11d3eded4cef215b51f5b1628be1bbfacd7e/packages/System.Linq.Dynamic.1.0.0/System.Linq.Dynamic.1.0.0.nupkg -------------------------------------------------------------------------------- /packages/System.Linq.Dynamic.1.0.0/System.Linq.Dynamic.1.0.0.nuspec: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | System.Linq.Dynamic 5 | 1.0.0 6 | Microsoft 7 | Microsoft 8 | http://www.opensource.org/licenses/ms-pl 9 | https://github.com/kahanu/System.Linq.Dynamic 10 | false 11 | This is the Microsoft assembly for the .Net 4.0 Dynamic language functionality. 12 | system linq dynamic 13 | 14 | -------------------------------------------------------------------------------- /packages/System.Linq.Dynamic.1.0.0/lib/net40/System.Linq.Dynamic.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kendo-labs/dlinq-helpers/459b11d3eded4cef215b51f5b1628be1bbfacd7e/packages/System.Linq.Dynamic.1.0.0/lib/net40/System.Linq.Dynamic.dll -------------------------------------------------------------------------------- /packages/repositories.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /travis.proj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | --------------------------------------------------------------------------------