lith há 4 anos atrás
commit
36adcd93e2

+ 62 - 0
Test/Vit.Linq.MsTest/DataSource.cs

@@ -0,0 +1,62 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+
+namespace Vit.Linq.MsTest
+{
+    public class DataSource
+    {
+        #region (x.1) BuildDataSource
+        public static List<ModelA> BuildDataSource()
+        {
+            var list = new List<ModelA>(1000);
+            for (int i = 0; i < 1000; i++)
+            {
+                list.Add(new ModelA
+                {
+                    id = i,
+                    pid = i / 10,
+                    name = "name" + i,
+                    addTime = DateTime.Now,
+                    ext = "ext" + i
+                }.BuildB());
+
+            }
+            return list;
+        }
+
+        public static IQueryable GetIQueryable() => BuildDataSource().AsQueryable();
+        public static IQueryable<ModelA> GetQueryable() => BuildDataSource().AsQueryable();
+
+        public class ModelA
+        {
+            public int id;
+            public int? pid;
+            public string name { get; set; }
+            public DateTime addTime;
+            public string ext;
+
+            public ModelB b1;
+
+            public ModelB[] ba;
+            public ModelA BuildB()
+            {
+                b1 = new ModelB { name = name + "_b1", pid = pid };
+
+                ba = new[] { b1 };
+                return this;
+            }
+
+
+
+            public class ModelB
+            {
+                public int? pid;
+                public string name;
+            }
+
+        }
+
+        #endregion
+    }
+}

+ 295 - 0
Test/Vit.Linq.MsTest/IQueryableTest.cs

@@ -0,0 +1,295 @@
+using Vit.Extensions;
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using Vit.Core.Util.ComponentModel.Data;
+using Vit.Linq.Query;
+using Vit.Core.Util.ComponentModel.Query;
+using static Vit.Linq.MsTest.DataSource;
+
+namespace Vit.Linq.MsTest
+{
+    [TestClass]
+    public class IQueryableTest
+    {
+
+
+        #region (x.2)DataFilter        
+        [TestMethod]
+        public void TestDataFilter()
+        {
+            var query = DataSource.GetIQueryable();
+
+            //操作符。可为 "=", "!=", ">", "<" , ">=", "<=", "Contains", "NotContains", "StartsWith", "EndsWith", "IsNullOrEmpty", "IsNotNullOrEmpty", "In", "NotIn"
+
+
+            #region (x.0) Count ToList ToArray
+            {
+
+                int count = query.IQueryable_Count();
+
+                var list1 = query.IQueryable_ToList<ModelA>();
+                var list2 = query.IQueryable_ToList();
+
+                var array1 = query.IQueryable_ToArray<ModelA>();
+                var array2 = query.IQueryable_ToArray();               
+            }
+            #endregion
+
+            #region (x.1)  =
+            {
+
+                //(x.x.1)
+                {
+                    var result = query.IQueryable_Where(new[] { new DataFilter { field = "id", opt = "=", value = 10 } }).IQueryable_ToList<ModelA>();
+                    Assert.AreEqual(result.Count, 1);
+                    Assert.AreEqual(result[0].id, 10);
+                }
+
+                //(x.x.2) == null
+                {
+                    var item = query.IQueryable_Skip(10).IQueryable_FirstOrDefault<ModelA>();
+                    var pid = item.pid;
+                    item.pid = null;
+
+                    var result = query.IQueryable_Where(new[] { new DataFilter { field = "pid", opt = "=", value = null } }).IQueryable_FirstOrDefault<ModelA>();
+                    Assert.AreEqual(result?.id, 10);
+                    item.pid = pid;
+                }
+            }
+            #endregion
+
+
+            #region (x.2)  !=
+            {
+                //(x.x.1)
+                {
+                    var result = query.IQueryable_Where(new[] { new DataFilter { field = "id", opt = "!=", value = 10 } }).IQueryable_ToList<ModelA>();
+                    Assert.AreEqual(result.Count, 999);
+                }
+
+                //(x.x.2) != null
+                {
+                    var item = query.IQueryable_Skip(10).IQueryable_FirstOrDefault<ModelA>();
+                    var pid = item.pid;
+                    item.pid = null;
+
+                    var result = query.IQueryable_Where(new[] { new DataFilter { field = "pid", opt = "!=", value = null } }).IQueryable_ToList<ModelA>();
+                    Assert.AreEqual(result.Count, 999);
+                    item.pid = pid;
+                }
+            }
+            #endregion
+
+            #region (x.3)  > <
+            {
+                var result = query.IQueryable_Where(new[] { new DataFilter { field = "id", opt = ">", value = 10 }, new DataFilter { field = "id", opt = "<", value = 20 } }).IQueryable_ToList<ModelA>();
+                Assert.AreEqual(result.Count, 9);
+            }
+            #endregion
+
+            #region (x.4)  >= <=
+            {
+                var result = query.IQueryable_Where(new[] { new DataFilter { field = "id", opt = ">=", value = 10 }, new DataFilter { field = "id", opt = "<=", value = 20 } }).IQueryable_ToList<ModelA>();
+                Assert.AreEqual(result.Count, 11);
+            }
+            #endregion
+
+
+            #region (x.5)  Contains
+            {
+                var result = query.IQueryable_Where(new[] { new DataFilter { field = "name", opt = "Contains", value = "987" } }).IQueryable_ToList<ModelA>();
+                Assert.AreEqual(result.Count, 1);
+                Assert.AreEqual(result[0].id, 987);
+            }
+            #endregion
+
+            #region (x.x)  NotContains
+            {
+
+                //(x.x.1)
+                {
+                    var result = query.IQueryable_Where(new[] { new DataFilter { field = "name", opt = "NotContains", value = "987" } }).IQueryable_ToList<ModelA>();
+                    Assert.AreEqual(result.Count, 999);
+                }
+
+
+                //(x.x.2)
+                {
+                    var item = query.IQueryable_Where(
+                        new[] { new DataFilter { field = "name", opt = "Contains",value="987" } }
+                        ).IQueryable_FirstOrDefault<ModelA>();
+                    var oriName = item.name;
+
+                    item.name = null;
+                    var result = query.IQueryable_Where(new[] { new DataFilter { field = "name", opt = "NotContains", value = "987" } }).IQueryable_ToList<ModelA>();
+                    Assert.AreEqual(result.Count, 1000);
+                    item.name = oriName;
+                }
+
+                //(x.x.3)
+                {
+                    var item = query.IQueryable_Where(
+                        new[] { new DataFilter { field = "name", opt = "Contains", value = "987" } }
+                        ).IQueryable_FirstOrDefault<ModelA>();
+                    var oriName = item.name;
+
+                    item.name = "";
+                    var result = query.IQueryable_Where(new[] { new DataFilter { field = "name", opt = "NotContains", value = "987" } }).IQueryable_ToList<ModelA>();
+                    Assert.AreEqual(result.Count, 1000);
+                    item.name = oriName;
+                }
+            }
+            #endregion
+
+            #region (x.6)  StartsWith
+            {
+                var result = query.IQueryable_Where(new[] { new DataFilter { field = "name", opt = "StartsWith", value = "name98" } }).IQueryable_ToList<ModelA>();
+                Assert.AreEqual(result.Count, 11);
+            }
+            #endregion
+
+            #region (x.7)  EndsWith
+            {
+                var result = query.IQueryable_Where(new[] { new DataFilter { field = "name", opt = "EndsWith", value = "987" } }).IQueryable_ToList<ModelA>();
+                Assert.AreEqual(result.Count, 1);
+            }
+            #endregion
+
+            #region (x.x)  IsNullOrEmpty
+            {
+                var item = query.IQueryable_Skip(10).IQueryable_FirstOrDefault<ModelA>();
+                var oriName = item.name;
+
+                //(x.x.1)
+                {
+                    item.name = null;
+                    var result = query.IQueryable_Where(new[] { new DataFilter { field = "name", opt = "IsNullOrEmpty" } }).IQueryable_ToList<ModelA>();
+                    Assert.AreEqual(result.Count, 1);
+                    item.name = oriName;
+                }
+                //(x.x.2)
+                {
+                    item.name = "";
+                    var result = query.IQueryable_Where(new[] { new DataFilter { field = "name", opt = "IsNullOrEmpty" } }).IQueryable_ToList<ModelA>();
+                    Assert.AreEqual(result.Count, 1);
+                    item.name = oriName;
+                }
+            }
+            #endregion
+
+            #region (x.x)  IsNotNullOrEmpty
+            {
+                var item = query.IQueryable_Skip(10).IQueryable_FirstOrDefault<ModelA>();
+                var oriName = item.name;
+
+                //(x.x.1)
+                {
+                    item.name = null;
+                    var result = query.IQueryable_Where(
+                        new[] { new DataFilter { field = "name", opt = "IsNotNullOrEmpty" } }
+                        )
+                        .IQueryable_ToList<ModelA>();
+                    Assert.AreEqual(result.Count, 999);
+                    item.name = oriName;
+                }
+                //(x.x.2)
+                {
+                    item.name = "";
+                    var result = query.IQueryable_Where(new[] { new DataFilter { field = "name", opt = "IsNotNullOrEmpty" } }).IQueryable_ToList<ModelA>();
+                    Assert.AreEqual(result.Count, 999);
+                    item.name = oriName;
+                }
+            }
+            #endregion
+
+            #region (x.8)  In 1
+            {
+                var result = query.IQueryable_Where(new[] { new DataFilter { field = "id", opt = "In", value = new int[] { 3, 4, 5 } } }).IQueryable_ToList<ModelA>();
+                Assert.AreEqual(result.Count, 3);
+            }
+            #endregion
+
+            #region (x.9)  In 2
+            {
+                query.IQueryable_FirstOrDefault<ModelA>().name = null;
+                var result = query.IQueryable_Where(new[] { new DataFilter { field = "name", opt = "In", value = new[] { "name3", "name4", null } } }).IQueryable_ToList<ModelA>();
+                Assert.AreEqual(result.Count, 3);
+            }
+            #endregion
+
+            #region (x.10)  NotIn 
+            {
+                var result = query.IQueryable_Where(new[] { new DataFilter { field = "name", opt = "NotIn", value = new[] { "name3", "name4", null } } }).IQueryable_ToList<ModelA>();
+                Assert.AreEqual(result.Count, 997);
+            }
+            #endregion
+
+
+            #region (x.11)  多级field
+            {
+                var result = query.IQueryable_Where(new[] { new DataFilter { field = "b1.name", opt = "=", value = "name987_b1" } }).IQueryable_ToList<ModelA>();
+                Assert.AreEqual(result.Count, 1);
+                Assert.AreEqual(result[0].id, 987);
+            }
+            #endregion
+
+        }
+
+        #endregion
+
+
+
+        #region (x.3)TestSortAndPage        
+        [TestMethod]
+        public void TestSortAndPage()
+        {
+            var query = GetIQueryable();
+
+            #region (x.1)
+            {
+                var result = query
+                    .IQueryable_Sort(new[] {
+                        new SortItem { field = "b1.pid", asc = false },
+                        new SortItem { field = "id", asc = true }
+                    })
+                    .IQueryable_Page(new PageInfo { pageIndex = 1, pageSize = 10 })
+                    .IQueryable_ToList<ModelA>();
+                Assert.AreEqual(result.Count, 10);
+                Assert.AreEqual(result[0].id, 990);
+            }
+            #endregion
+
+
+            #region (x.2)
+            {
+                var result = query
+                    .IQueryable_Sort("id", false)
+                    .IQueryable_Page(2, 10)
+                    .IQueryable_ToList<ModelA>();
+                Assert.AreEqual(result.Count, 10);
+                Assert.AreEqual(result[0].id, 989);
+            }
+            #endregion
+
+
+            #region (x.3)
+            {
+                var result = query
+                    .IQueryable_Sort(new[] {
+                        new SortItem { field = "b1.pid", asc = false },
+                        new SortItem { field = "id", asc = true }
+                    })
+                    .IQueryable_ToPageData<ModelA>(new PageInfo { pageIndex = 1, pageSize = 10 });
+
+                Assert.AreEqual(result.totalCount, 1000);
+                Assert.AreEqual(result.rows.Count, 10);
+                Assert.AreEqual(result.rows[0].id, 990);
+            }
+            #endregion
+
+        }
+        #endregion
+
+
+
+    }
+}

+ 238 - 0
Test/Vit.Linq.MsTest/Queryable_DataFilter_Test.cs

@@ -0,0 +1,238 @@
+using Vit.Extensions;
+using System.Linq;
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using Vit.Linq.Query;
+using static Vit.Linq.MsTest.DataSource;
+
+namespace Vit.Linq.MsTest
+{
+    [TestClass]
+    public class Queryable_DataFilter_Test
+    {
+
+        #region DataFilter        
+        [TestMethod]
+        public void TestDataFilter()
+        {
+            var query = DataSource.GetQueryable();
+
+            //操作符。可为 "=", "!=", ">", "<" , ">=", "<=", "Contains", "NotContains", "StartsWith", "EndsWith", "IsNullOrEmpty", "IsNotNullOrEmpty", "In", "NotIn"
+
+            #region (x.0) Count ToList ToArray
+            {
+
+                int count = query.Count();
+
+                var list1 = query.ToList<ModelA>();
+                var list2 = query.ToList();
+
+                var array1 = query.ToArray<ModelA>();
+                var array2 = query.ToArray();
+            }
+            #endregion
+
+
+            #region (x.1)  =
+            {
+                //(x.x.1)
+                {
+                    var result = query.Where(new[] { new DataFilter { field = "id", opt = "=", value = 10 } }).ToList();
+                    Assert.AreEqual(result.Count, 1);
+                    Assert.AreEqual(result[0].id, 10);
+                }
+
+
+                //(x.x.2) == null
+                {
+                    var item = query.Skip(10).FirstOrDefault();
+                    var pid = item.pid;
+                    item.pid = null;
+
+                    var result = query.Where(new[] { new DataFilter { field = "pid", opt = "=", value = null } }).FirstOrDefault();                
+                    Assert.AreEqual(result?.id, 10);
+                    item.pid = pid;
+                }
+
+
+            }
+            #endregion
+
+            #region (x.2)  !=
+            {
+                //(x.x.1)
+                {
+                    var result = query.Where(new[] { new DataFilter { field = "id", opt = "!=", value = 10 } }).ToList();
+                    Assert.AreEqual(result.Count, 999);
+                }
+
+                //(x.x.2) != null
+                {
+                    var item = query.Skip(10).FirstOrDefault();
+                    var pid = item.pid;
+                    item.pid = null;
+
+                    var result = query.Where(new[] { new DataFilter { field = "pid", opt = "!=", value = null } }).ToList();
+                    Assert.AreEqual(result.Count, 999);
+                    item.pid = pid;
+                }
+            }
+            #endregion
+
+            #region (x.3)  > <
+            {
+                var result = query.Where(new[] { new DataFilter { field = "id", opt = ">", value = 10 }, new DataFilter { field = "id", opt = "<", value = 20 } }).ToList();
+                Assert.AreEqual(result.Count, 9);
+            }
+            #endregion
+
+            #region (x.4)  >= <=
+            {
+                var result = query.Where(new[] { new DataFilter { field = "id", opt = ">=", value = 10 }, new DataFilter { field = "id", opt = "<=", value = 20 } }).ToList();
+                Assert.AreEqual(result.Count, 11);
+            }
+            #endregion
+
+
+            #region (x.5)  Contains
+            {
+                var result = query.Where(new[] { new DataFilter { field = "name", opt = "Contains", value = "987" } }).ToList();
+                Assert.AreEqual(result.Count, 1);
+                Assert.AreEqual(result[0].id, 987);
+            }
+            #endregion
+
+            #region (x.x)  NotContains
+            {              
+
+                //(x.x.1)
+                {
+                    var result = query.Where(new[] { new DataFilter { field = "name", opt = "NotContains", value = "987" } }).ToList();
+                    Assert.AreEqual(result.Count, 999);                  
+                }
+
+
+                //(x.x.2)
+                {
+                    var item = query.Where(m => m.name.Contains("987")).FirstOrDefault();
+                    var oriName = item.name;
+
+                    item.name = null;
+                    var result = query.Where(new[] { new DataFilter { field = "name", opt = "NotContains", value = "987" } }).ToList();
+                    Assert.AreEqual(result.Count, 1000);
+                    item.name = oriName;
+                }
+
+                //(x.x.3)
+                {
+                    var item = query.Where(m=>m.name.Contains("987")).FirstOrDefault();
+                    var oriName = item.name;
+
+                    item.name = "";
+                    var result = query.Where(new[] { new DataFilter { field = "name", opt = "NotContains", value = "987" } }).ToList();
+                    Assert.AreEqual(result.Count, 1000);
+                    item.name = oriName;
+                }
+            }
+            #endregion
+
+
+            #region (x.6)  StartsWith
+            {
+                var result = query.Where(new[] { new DataFilter { field = "name", opt = "StartsWith", value = "name98" } }).ToList();
+                Assert.AreEqual(result.Count, 11);
+            }
+            #endregion
+
+            #region (x.7)  EndsWith
+            {
+                var result = query.Where(new[] { new DataFilter { field = "name", opt = "EndsWith", value = "987" } }).ToList();
+                Assert.AreEqual(result.Count, 1);
+            }
+            #endregion
+
+            #region (x.x)  IsNullOrEmpty
+            {
+                var item = query.Skip(10).FirstOrDefault();
+                var oriName = item.name;
+
+                //(x.x.1)
+                {
+                    item.name = null;
+                    var result = query.Where(new[] { new DataFilter { field = "name", opt = "IsNullOrEmpty" } }).ToList();
+                    Assert.AreEqual(result.Count, 1);
+                    item.name = oriName;
+                }
+                //(x.x.2)
+                {
+                    item.name = "";
+                    var result = query.Where(new[] { new DataFilter { field = "name", opt = "IsNullOrEmpty" } }).ToList();
+                    Assert.AreEqual(result.Count, 1);
+                    item.name = oriName;
+                }
+            }
+            #endregion
+
+            #region (x.x)  IsNotNullOrEmpty
+            {
+                var item = query.Skip(10).FirstOrDefault();
+                var oriName = item.name;
+
+                //(x.x.1)
+                {
+                    item.name = null;
+                    var result = query.Where(new[] { new DataFilter { field = "name", opt = "IsNotNullOrEmpty" } }).ToList();
+                    Assert.AreEqual(result.Count, 999);
+                    item.name = oriName;
+                }
+                //(x.x.2)
+                {
+                    item.name = "";
+                    var result = query.Where(new[] { new DataFilter { field = "name", opt = "IsNotNullOrEmpty" } }).ToList();
+                    Assert.AreEqual(result.Count, 999);
+                    item.name = oriName;
+                }
+            }
+            #endregion
+
+
+
+            #region (x.8)  In 1
+            {
+                var result = query.Where(new[] { new DataFilter { field = "id", opt = "In", value = new int[] { 3, 4, 5 } } }).ToList();
+                Assert.AreEqual(result.Count, 3);
+            }
+            #endregion
+
+            #region (x.9)  In 2
+            {
+                query.FirstOrDefault().name = null;
+                var result = query.Where(new[] { new DataFilter { field = "name", opt = "In", value = new[] { "name3", "name4", null } } }).ToList();
+                Assert.AreEqual(result.Count, 3);
+            }
+            #endregion
+
+            #region (x.10)  NotIn 
+            {
+                var result = query.Where(new[] { new DataFilter { field = "name", opt = "NotIn", value = new[] { "name3", "name4", null } } }).ToList();
+                Assert.AreEqual(result.Count, 997);
+            }
+            #endregion
+
+
+            #region (x.11)  多级field
+            {
+                var result = query.Where(new[] { new DataFilter { field = "b1.name", opt = "=", value = "name987_b1" } }).ToList();
+                Assert.AreEqual(result.Count, 1);
+                Assert.AreEqual(result[0].id, 987);
+            }
+            #endregion
+
+        }
+
+        #endregion
+
+
+     
+
+    }
+}

+ 66 - 0
Test/Vit.Linq.MsTest/Queryable_SortAndPage_Test.cs

@@ -0,0 +1,66 @@
+using Vit.Extensions;
+using System.Linq;
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using Vit.Core.Util.ComponentModel.Data;
+using Vit.Core.Util.ComponentModel.Query;
+
+namespace Vit.Linq.MsTest
+{
+    [TestClass]
+    public class Queryable_SortAndPage_Test
+    {
+
+        #region (x.3)TestSortAndPage        
+        [TestMethod]
+        public void TestSortAndPage()
+        {
+            var query = DataSource.GetQueryable();
+
+            #region (x.1)
+            {
+                var result = query
+                    .Sort(new[] {
+                        new SortItem { field = "b1.pid", asc = false },
+                        new SortItem { field = "id", asc = true }
+                    })
+                    .Page(new PageInfo { pageIndex = 1, pageSize = 10 })
+                    .ToList();
+                Assert.AreEqual(result.Count, 10);
+                Assert.AreEqual(result[0].id, 990);
+            }
+            #endregion
+
+
+            #region (x.2)
+            {         
+                var result = query
+                    .Sort("id",false)
+                    .Page(2,10)
+                    .ToList();
+                Assert.AreEqual(result.Count, 10);
+                Assert.AreEqual(result[0].id, 989);
+            }
+            #endregion
+
+
+            #region (x.3)
+            {
+                var result = query
+                    .Sort(new[] {
+                        new SortItem { field = "b1.pid", asc = false },
+                        new SortItem { field = "id", asc = true }
+                    })
+                    .ToPageData(new PageInfo { pageIndex = 1, pageSize = 10 });
+
+                Assert.AreEqual(result.totalCount, 1000);
+                Assert.AreEqual(result.rows.Count, 10);
+                Assert.AreEqual(result.rows[0].id, 990);
+            }
+            #endregion
+
+
+        }
+        #endregion
+
+    }
+}

+ 66 - 0
Test/Vit.Linq.MsTest/Queryable_Sort_ByReflection_Test.cs

@@ -0,0 +1,66 @@
+using Vit.Extensions;
+using System.Linq;
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using Vit.Core.Util.ComponentModel.Data;
+using Vit.Core.Util.ComponentModel.Query;
+
+namespace Vit.Linq.MsTest
+{
+    [TestClass]
+    public class Queryable_Sort_ByReflection_Test
+    {
+
+        #region (x.3)TestSortAndPage        
+        [TestMethod]
+        public void TestSortAndPage()
+        {
+            var query = DataSource.GetQueryable();
+
+            #region (x.1)
+            {
+                var result = query
+                    .Sort_ByReflection(new[] {
+                        new SortItem { field = "b1.pid", asc = false },
+                        new SortItem { field = "id", asc = true }
+                    })
+                    .Page(new PageInfo { pageIndex = 1, pageSize = 10 })
+                    .ToList();
+                Assert.AreEqual(result.Count, 10);
+                Assert.AreEqual(result[0].id, 990);
+            }
+            #endregion
+
+
+            #region (x.2)
+            {         
+                var result = query
+                    .Sort_ByReflection("id",false)
+                    .Page(2,10)
+                    .ToList();
+                Assert.AreEqual(result.Count, 10);
+                Assert.AreEqual(result[0].id, 989);
+            }
+            #endregion
+
+
+            #region (x.3)
+            {
+                var result = query
+                    .Sort_ByReflection(new[] {
+                        new SortItem { field = "b1.pid", asc = false },
+                        new SortItem { field = "id", asc = true }
+                    })
+                    .ToPageData(new PageInfo { pageIndex = 1, pageSize = 10 });
+
+                Assert.AreEqual(result.totalCount, 1000);
+                Assert.AreEqual(result.rows.Count, 10);
+                Assert.AreEqual(result.rows[0].id, 990);
+            }
+            #endregion
+
+
+        }
+        #endregion
+
+    }
+}

+ 19 - 0
Test/Vit.Linq.MsTest/Vit.Linq.MsTest.csproj

@@ -0,0 +1,19 @@
+<Project Sdk="Microsoft.NET.Sdk">
+
+  <PropertyGroup>
+    <TargetFramework>netcoreapp2.1</TargetFramework>
+
+    <IsPackable>false</IsPackable>
+  </PropertyGroup>
+
+  <ItemGroup>
+    <PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.6.1" />
+    <PackageReference Include="MSTest.TestAdapter" Version="2.1.2" />
+    <PackageReference Include="MSTest.TestFramework" Version="2.1.2" />
+  </ItemGroup>
+
+  <ItemGroup>
+    <ProjectReference Include="..\..\Vit.Linq\Vit.Linq.csproj" />
+  </ItemGroup>
+
+</Project>

+ 4 - 0
Vit.Linq/Doc/说明.txt

@@ -0,0 +1,4 @@
+参考 https://www.cnblogs.com/seriawei/p/entity-framework-dynamic-search.html 
+
+
+参考 DynamicQueryable

+ 13 - 0
Vit.Linq/Properties/PublishProfiles/FolderProfile.pubxml

@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+https://go.microsoft.com/fwlink/?LinkID=208121. 
+-->
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <PropertyGroup>
+    <PublishProtocol>FileSystem</PublishProtocol>
+    <Configuration>Release</Configuration>
+    <Platform>Any CPU</Platform>
+    <TargetFramework>netstandard2.0</TargetFramework>
+    <PublishDir>..\..\..\Publish\nuget</PublishDir>
+  </PropertyGroup>
+</Project>

+ 34 - 0
Vit.Linq/Query/DataFilter.cs

@@ -0,0 +1,34 @@
+using Newtonsoft.Json;
+using Vit.Core.Util.ComponentModel.Model;
+
+namespace Vit.Linq.Query
+{
+    public class DataFilter
+    {
+        /// <summary>
+        /// 操作列,可多级(如 "b1.name")
+        /// </summary>
+        [SsExample("id")]
+        [SsDescription("操作列,可多级(如 \"b1.name\")")]
+        public string field { get; set; }
+
+
+
+        /// <summary>
+        /// 操作符。可为 "=", "!=", "&gt;", "&lt;" , "&gt;=", "&lt;=", "Contains", "NotContains", "StartsWith", "EndsWith" , "IsNullOrEmpty", "IsNotNullOrEmpty" ,  "In" ,"NotIn"
+        /// </summary>
+        [SsExample(">=")]
+        [SsDescription("操作符。可为 \"=\", \"!=\", \">\", \"<\" , \">=\", \"<=\", \"Contains\", \"NotContains\", \"StartsWith\", \"EndsWith\", \"IsNullOrEmpty\", \"IsNotNullOrEmpty\", \"In\", \"NotIn\"")]
+        public string opt { get; set; }
+         
+
+        /// <summary>
+        /// 参数
+        /// </summary>
+        [SsExample("2")]
+        [SsDescription("参数")]
+        [JsonProperty(NullValueHandling =NullValueHandling.Ignore)]
+        public object value { get; set; }         
+
+    }
+}

+ 8 - 0
Vit.Linq/Query/ECondition.cs

@@ -0,0 +1,8 @@
+namespace Vit.Linq.Query
+{
+    public enum ECondition
+    {
+        OrElse = 1,
+        AndAlso = 2
+    }
+}

+ 249 - 0
Vit.Linq/Query/Extensions/DataFilterExtensions.cs

@@ -0,0 +1,249 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Linq.Expressions;
+using System.Runtime.CompilerServices;
+using Vit.Linq.Query;
+
+namespace Vit.Extensions
+{
+
+    /// <summary>
+    /// https://www.cnblogs.com/seriawei/p/entity-framework-dynamic-search.html 
+    /// </summary>
+    public static partial class DataFilterExtensions
+    {
+
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        public static Func<T, bool> ToPredicate<T>(this IEnumerable<DataFilter> filters, ECondition condition = ECondition.AndAlso)
+        {
+            return filters.ToExpression<T>(condition)?.Compile();
+        }
+
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        public static Expression<Func<T, bool>> ToExpression<T>(this IEnumerable<DataFilter> filters, ECondition condition = ECondition.AndAlso)
+        {
+            var exp = filters.ToLambdaExpression(typeof(T), condition);
+            if (exp == null)
+            {
+                return null;
+            }
+            else
+            {
+                var lambda = (Expression<Func<T, bool>>)exp;
+                return lambda;
+            }
+        }
+
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        public static LambdaExpression ToLambdaExpression(this IEnumerable<DataFilter> filters, Type targetType, ECondition condition = ECondition.AndAlso)
+        {
+            if (filters == null)
+            {
+                return null;
+            }
+
+            if (filters.Count() > LinqHelp.MaxFilterCount)
+            {
+                throw new Exception("筛选条件个数过多");
+            }
+
+            ParameterExpression parameter = Expression.Parameter(targetType);
+
+            Expression expression = null;
+
+            foreach (var item in filters)
+            {
+                var curExp = ToExpression(item, parameter);
+                if(curExp!=null) 
+                expression = Append(expression, curExp);
+            }
+
+            if (expression == null)
+            {
+                return null;
+            }
+            return Expression.Lambda(expression, parameter);
+
+
+            #region Method Append
+            Expression Append(Expression exp1, Expression exp2)
+            {
+                if (exp1 == null)
+                {
+                    return exp2;
+                }
+                return condition == ECondition.OrElse ? Expression.OrElse(exp1, exp2) : Expression.AndAlso(exp1, exp2);
+            }
+            #endregion
+
+        }
+
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        public static LambdaExpression ToLambdaExpression(this DataFilter filter, Type targetType)
+        {  
+            ParameterExpression parameter = Expression.Parameter(targetType);
+           
+            return Expression.Lambda(ToExpression(filter, parameter), parameter);
+        }
+
+
+
+
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        static Expression ToExpression(this DataFilter filter, ParameterExpression parameter)
+        {
+            if (filter == null || string.IsNullOrWhiteSpace(filter.field)) return null;
+
+
+            // (x.1)get memberExp
+            MemberExpression memberExp = LinqHelp.BuildField_MemberExpression(parameter, filter.field);
+
+
+            #region (x.2) get Expression
+
+            Type fieldType = memberExp.Type;
+
+            switch (filter.opt)
+            {
+                #region (x.x.1)数值               
+                case "=":
+                    {
+                        return Expression.Equal(memberExp, ConvertValueExp());
+                    }
+                case "!=":
+                    {
+                        return Expression.NotEqual(memberExp, ConvertValueExp());
+
+                    }           
+
+                case ">":
+                    {
+                        return Expression.GreaterThan(memberExp, ConvertValueExp());
+
+                    }
+                case ">=":
+                    {
+                        return Expression.GreaterThanOrEqual(memberExp, ConvertValueExp());
+
+                    }
+                case "<":
+                    {
+                        return Expression.LessThan(memberExp, ConvertValueExp());
+
+                    }
+                case "<=":
+                    {
+                        return Expression.LessThanOrEqual(memberExp, ConvertValueExp());
+
+                    }
+                #endregion
+
+                #region (x.x.2)字符串               
+                case "Contains":
+                    {
+                        var nullCheck = Expression.Call(typeof(string), "IsNullOrEmpty", null, memberExp);
+                        var contains = Expression.Call(memberExp, "Contains", null, ConvertValueExp());
+
+                        return Expression.AndAlso(Expression.Not(nullCheck), contains);
+
+                    }
+                case "NotContains":
+                    {
+                        var nullCheck = Expression.Call(typeof(string), "IsNullOrEmpty", null, memberExp);
+                        var contains = Expression.Call(memberExp, "Contains", null, ConvertValueExp());
+
+                        return Expression.OrElse(nullCheck, Expression.Not(contains));
+                    }
+                case "StartsWith":
+                    {
+                        var nullCheck = Expression.Not(Expression.Call(typeof(string), "IsNullOrEmpty", null, memberExp));
+                        var startsWith = Expression.Call(memberExp, "StartsWith", null, ConvertValueExp());
+
+                        return Expression.AndAlso(nullCheck, startsWith);
+                    }              
+
+                case "EndsWith":
+                    {
+                        var nullCheck = Expression.Not(Expression.Call(typeof(string), "IsNullOrEmpty", null, memberExp));
+                        var endsWith = Expression.Call(memberExp, "EndsWith", null, ConvertValueExp());
+                        return Expression.AndAlso(nullCheck, endsWith);
+                    }
+                case "IsNullOrEmpty":
+                    {
+                        return Expression.Call(typeof(string), "IsNullOrEmpty", null, memberExp);
+                    }
+                case "IsNotNullOrEmpty":
+                    {
+                        return Expression.Not(Expression.Call(typeof(string), "IsNullOrEmpty", null, memberExp));
+                    }
+                #endregion
+
+                #region (x.x.3)数组              
+
+                case "In":
+                    {
+                        Expression arrayExp = null;
+                        #region build arrayExp
+                        {
+                            Type valueType = typeof(IEnumerable<>).MakeGenericType(fieldType);
+                            object value = filter.value;
+                            if (value != null)
+                            {
+                                value = value.ConvertBySerialize(valueType);
+                            }
+                            Expression<Func<object>> valueLamba = () => value;
+                            arrayExp = Expression.Convert(valueLamba.Body, valueType);
+                        }
+                        #endregion
+                        var inCheck = Expression.Call(typeof(System.Linq.Enumerable), "Contains", new[] { fieldType }, arrayExp, memberExp);
+                        return inCheck;
+
+                    }
+                case "NotIn":
+                    {
+                        Expression arrayExp = null;
+                        #region build arrayExp
+                        {
+                            Type valueType = typeof(IEnumerable<>).MakeGenericType(fieldType);
+                            object value = filter.value;
+                            if (value != null)
+                            {
+                                value = value.ConvertBySerialize(valueType);
+                            }
+                            Expression<Func<object>> valueLamba = () => value;
+                            arrayExp = Expression.Convert(valueLamba.Body, valueType);
+                        }
+                        #endregion
+                        var inCheck = Expression.Call(typeof(System.Linq.Enumerable), "Contains", new[] { fieldType }, arrayExp, memberExp);
+                        return Expression.Not(inCheck);
+                    }
+                #endregion
+            }
+            #endregion
+
+
+            return null;
+
+
+            #region Method ConvertValueExp
+            UnaryExpression ConvertValueExp()
+            {
+                object value = filter.value;
+                if (value != null)
+                {
+                    Type valueType = Nullable.GetUnderlyingType(fieldType) ?? fieldType;
+                    value = Convert.ChangeType(value, valueType);
+                }
+
+                Expression<Func<object>> valueLamba = () => value;
+                return Expression.Convert(valueLamba.Body, fieldType);
+            }
+            #endregion
+
+
+        }
+
+
+    }
+}

+ 146 - 0
Vit.Linq/Query/Extensions/IQueryableExtensions.cs

@@ -0,0 +1,146 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Linq.Expressions;
+using System.Runtime.CompilerServices;
+using Vit.Linq.Query;
+
+namespace Vit.Extensions
+{
+
+
+    /// <summary>
+    /// 参考 DynamicQueryable
+    /// </summary>
+    public static partial class IQueryable_Extensions   {
+
+
+        #region Where
+
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        public static IQueryable IQueryable_Where(this IQueryable source, IEnumerable<DataFilter> filters, ECondition condition = ECondition.AndAlso)
+        {  
+            LambdaExpression lambda = filters.ToLambdaExpression(source.ElementType, condition);
+            if (lambda == null) return source;
+            return source.Provider.CreateQuery(
+                Expression.Call(
+                    typeof(Queryable), "Where",
+                    new Type[] { source.ElementType },
+                    source.Expression, Expression.Quote(lambda)));
+        }
+
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        public static IQueryable IQueryable_Where(this IQueryable source, DataFilter filter)
+        {
+            return IQueryable_Where(source,new[]{ filter });
+        }
+        #endregion
+
+
+        #region Count
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        public static int IQueryable_Count(this IQueryable source)
+        {
+            return source.Provider.Execute<int>(
+                Expression.Call(
+                    typeof(Queryable), "Count",
+                    new Type[] { source.ElementType }, source.Expression));
+        }
+        #endregion
+
+        #region Skip
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        public static IQueryable IQueryable_Skip(this IQueryable source, int count)
+        {    
+            return source.Provider.CreateQuery(
+                Expression.Call(
+                    typeof(Queryable), "Skip",
+                    new Type[] { source.ElementType },
+                    source.Expression, Expression.Constant(count)));
+        }
+        #endregion
+
+        #region Take
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        public static IQueryable IQueryable_Take(this IQueryable source, int count)
+        {        
+            return source.Provider.CreateQuery(
+                Expression.Call(
+                    typeof(Queryable), "Take",
+                    new Type[] { source.ElementType },
+                    source.Expression, Expression.Constant(count)));
+        }
+        #endregion
+
+
+
+        #region ToList       
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        public static List<T> IQueryable_ToList<T>(this IQueryable source)
+        {
+            return source.Provider.Execute<List<T>>(
+                Expression.Call(
+                    typeof(Enumerable), "ToList",
+                    new Type[] { source.ElementType }, source.Expression));
+        }
+
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        public static object IQueryable_ToList(this IQueryable source)
+        { 
+            return source.Provider.Execute(
+                Expression.Call(
+                    typeof(Enumerable), "ToList",
+                    new Type[] { source.ElementType }, source.Expression));
+        }
+        #endregion
+
+
+        #region ToArray       
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        public static T[] IQueryable_ToArray<T>(this IQueryable source)
+        {  
+            return source.Provider.Execute<T[]>(
+                Expression.Call(
+                    typeof(Enumerable), "ToArray",
+                    new Type[] { source.ElementType }, source.Expression));
+        }
+
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        public static object IQueryable_ToArray(this IQueryable source)
+        {
+            return source.Provider.Execute(
+                Expression.Call(
+                    typeof(Enumerable), "ToArray",
+                    new Type[] { source.ElementType }, source.Expression));
+        }
+        #endregion
+
+
+        #region FirstOrDefault
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        public static object IQueryable_FirstOrDefault(this IQueryable source)
+        {
+            return source.Provider.Execute<object>(
+                Expression.Call(typeof(Queryable), "FirstOrDefault", 
+                new Type[] { source.ElementType }, 
+                source.Expression)
+                );
+        }
+
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        public static T IQueryable_FirstOrDefault<T>(this IQueryable source)
+        { 
+            return source.Provider.Execute<T>(
+                Expression.Call(typeof(Queryable), "FirstOrDefault", new Type[] { source.ElementType }, source.Expression)
+                );
+        }
+        #endregion
+
+
+
+
+
+
+
+    }
+}

+ 34 - 0
Vit.Linq/Query/Extensions/IQueryable_PageExtensions.cs

@@ -0,0 +1,34 @@
+using System.Linq;
+using System.Runtime.CompilerServices;
+using Vit.Core.Util.ComponentModel.Data;
+
+namespace Vit.Extensions
+{
+
+    public static partial class IQueryable_PageExtensions
+    {
+
+
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        public static IQueryable IQueryable_Page(this IQueryable query, PageInfo page)            
+        {
+            if (query == null || page == null) return query;
+
+            return query.IQueryable_Page(page.pageIndex, page.pageSize); 
+        }
+
+
+        /// <summary>
+        /// 
+        /// </summary>
+        /// <param name="query"></param>
+        /// <param name="pageIndex">页码,从1开始</param>
+        /// <param name="pageSize">每页数据个数</param>
+        /// <returns></returns>
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        public static IQueryable IQueryable_Page(this IQueryable query,int pageIndex, int pageSize)      
+        {  
+            return query.IQueryable_Skip((pageIndex - 1) * pageSize).IQueryable_Take(pageSize);
+        }
+    }
+}

+ 75 - 0
Vit.Linq/Query/Extensions/IQueryable_SortExtensions.cs

@@ -0,0 +1,75 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Linq.Expressions;
+using System.Runtime.CompilerServices;
+using Vit.Core.Util.ComponentModel.Query;
+using Vit.Linq.Query;
+
+namespace Vit.Extensions
+{
+
+    public static partial class IQueryable_SortExtensions
+    {
+
+        #region Sort
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        public static IQueryable IQueryable_Sort(this IQueryable source, IEnumerable<SortItem> sort)
+        {
+
+            if (sort == null || sort.Count() == 0) return source;
+
+            #region GetSortMethodName
+            bool isFirst = true;
+            string GetSortMethodName(bool asc)
+            {
+                if (isFirst)
+                {
+                    isFirst = false;
+                    return asc ? "OrderBy" : "OrderByDescending";
+                }
+                return asc ? "ThenBy" : "ThenByDescending";
+            }
+            #endregion
+
+
+            var targetType = source.ElementType;
+            ParameterExpression parameter = Expression.Parameter(targetType);
+
+            Expression queryExpr = source.Expression;
+
+            foreach (var item in sort)
+            {
+                //(x.1)get memberExp     
+                MemberExpression memberExp = LinqHelp.BuildField_MemberExpression(parameter, item.field);
+
+
+                #region (x.2)Call
+                queryExpr = Expression.Call(
+                  typeof(Queryable), GetSortMethodName(item.asc),
+                  new Type[] { source.ElementType, memberExp.Type },
+                  queryExpr, Expression.Quote(Expression.Lambda(memberExp, parameter)));
+                #endregion
+            }
+
+            return source.Provider.CreateQuery(queryExpr);
+        }
+        #endregion
+
+
+
+        /// <summary>
+        /// 
+        /// </summary>      
+        /// <param name="query"></param>
+        /// <param name="field">字段名</param>
+        /// <param name="asc">是否为正向排序</param>
+        /// <returns></returns>
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        public static IQueryable IQueryable_Sort(this IQueryable query, string field, bool asc = true)
+        {
+            return query.IQueryable_Sort(new[] { new SortItem { field = field, asc = asc } });
+        }
+
+    }
+}

+ 81 - 0
Vit.Linq/Query/Extensions/IQueryable_ToPageDataExtensions.cs

@@ -0,0 +1,81 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Runtime.CompilerServices;
+using Vit.Core.Util.ComponentModel.Data;
+using Vit.Core.Util.ComponentModel.Query;
+using Vit.Linq.Query;
+
+namespace Vit.Extensions
+{
+
+    public static partial class IQueryable_ToPageDataExtensions
+    {
+        #region IQueryable_ToPageData       
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        public static PageData<T> IQueryable_ToPageData<T>(this IQueryable query, PageInfo page)             
+        {
+            if (query == null ) return null;
+
+            var queryPaged = query;
+            if (page != null)
+                queryPaged = queryPaged.IQueryable_Page(page);
+
+            return new PageData<T>(page) { totalCount = query.IQueryable_Count(), rows = queryPaged.IQueryable_ToList<T>() };
+        }
+
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        public static PageData<T> IQueryable_ToPageData<T>(this IQueryable query,
+            IEnumerable<DataFilter> filter, IEnumerable<SortItem> sort, PageInfo page
+        )
+        {
+            return query?.IQueryable_Where(filter).IQueryable_Sort(sort).IQueryable_ToPageData<T>(page);
+        }
+        #endregion
+
+
+        #region IQueryable_ToPageData  with selector
+        /// <summary>
+        /// 注:先查询,后调用selector
+        /// </summary>
+        /// <typeparam name="T"></typeparam>
+        /// <typeparam name="TResult"></typeparam>
+        /// <param name="query"></param>
+        /// <param name="page"></param>
+        /// <param name="selector"></param>
+        /// <returns></returns>
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        public static PageData<TResult> IQueryable_ToPageData<T, TResult>(this IQueryable query, PageInfo page, Func<T, TResult> selector)        
+        {
+            if (query == null) return null;
+
+            var queryPaged = query;
+            if (page != null)
+                queryPaged = queryPaged.IQueryable_Page(page);
+
+            return new PageData<TResult>(page) { totalCount = query.IQueryable_Count(), rows = queryPaged.IQueryable_ToList<T>().Select(selector).ToList() };
+        }
+
+        /// <summary>
+        /// 注:先查询,后调用selector
+        /// </summary>
+        /// <typeparam name="T"></typeparam>
+        /// <typeparam name="TResult"></typeparam>
+        /// <param name="query"></param>
+        /// <param name="filter"></param>
+        /// <param name="sort"></param>
+        /// <param name="page"></param>
+        /// <param name="selector"></param>
+        /// <returns></returns>
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        public static PageData<TResult> IQueryable_ToPageData<T, TResult>(this IQueryable query,
+            IEnumerable<DataFilter> filter, IEnumerable<SortItem> sort, PageInfo page, Func<T, TResult> selector
+        ) where T : class
+        {
+            return query?.IQueryable_Where(filter).IQueryable_Sort(sort).IQueryable_ToPageData(page, selector);
+        }
+        #endregion
+
+
+    }
+}

+ 37 - 0
Vit.Linq/Query/Extensions/Queryable_PageExtensions.cs

@@ -0,0 +1,37 @@
+using System.Linq;
+using System.Runtime.CompilerServices;
+using Vit.Core.Util.ComponentModel.Data;
+
+namespace Vit.Extensions
+{
+
+    public static partial class Queryable_PageExtensions
+    {
+
+
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        public static IQueryable<T> Page<T>(this IQueryable<T> query, PageInfo page)
+            where T : class
+        {
+            if (query == null || page == null) return query;
+
+            return query.Page(page.pageIndex, page.pageSize); 
+        }
+
+
+        /// <summary>
+        /// 
+        /// </summary>
+        /// <typeparam name="T"></typeparam>
+        /// <param name="query"></param>
+        /// <param name="pageIndex">页码,从1开始</param>
+        /// <param name="pageSize">每页数据个数</param>
+        /// <returns></returns>
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        public static IQueryable<T> Page<T>(this IQueryable<T> query,int pageIndex, int pageSize)
+          where T : class
+        {  
+            return query.Skip((pageIndex - 1) * pageSize).Take(pageSize);
+        }
+    }
+}

+ 115 - 0
Vit.Linq/Query/Extensions/Queryable_SortExtensions.cs

@@ -0,0 +1,115 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Linq.Expressions;
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using Vit.Core.Util.ComponentModel.Query;
+using Vit.Linq.Query;
+
+namespace Vit.Extensions
+{
+    /// <summary>
+    /// 参考 https://www.cnblogs.com/Extnet/p/9848272.html
+    /// </summary>
+    public static partial class Queryable_SortExtensions
+    {
+
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        public static IQueryable<T> Sort<T>(this IQueryable<T> query, IEnumerable<SortItem> sort)
+            where T : class
+        {
+            if (query == null || sort == null) return query;
+
+            var sortCount = sort.Count();
+            if (sortCount == 0) return query;
+            if (sortCount > LinqHelp.MaxSortCount)
+            {
+                throw new Exception("排序条件个数过多");
+            }
+
+            var paramExp = Expression.Parameter(typeof(T));
+            IOrderedQueryable<T> orderedQuery = null;
+
+            foreach (var item in sort)
+            {
+
+                MemberExpression memberExp = LinqHelp.BuildField_MemberExpression(paramExp, item.field);
+                LambdaExpression exp = Expression.Lambda(memberExp, paramExp);
+
+                if (orderedQuery == null)
+                {
+                    orderedQuery = OrderBy(query, exp, item.asc);
+                }
+                else
+                {
+                    orderedQuery = ThenBy(orderedQuery, exp, item.asc);
+                }
+            }
+            return orderedQuery ?? query;
+        }
+
+
+        /// <summary>
+        /// 
+        /// </summary>
+        /// <typeparam name="T"></typeparam>
+        /// <param name="query"></param>
+        /// <param name="field">字段名</param>
+        /// <param name="asc">是否为正向排序</param>
+        /// <returns></returns>
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        public static IQueryable<T> Sort<T>(this IQueryable<T> query, string field, bool asc = true)
+           where T : class
+        {
+            if (string.IsNullOrEmpty(field)) return query;
+
+            var paramExp = Expression.Parameter(typeof(T));
+            MemberExpression memberExp = LinqHelp.BuildField_MemberExpression(paramExp, field);
+            LambdaExpression expr = Expression.Lambda(memberExp, paramExp);
+
+            return OrderBy(query, expr, asc);
+        }
+
+
+
+
+
+        #region OrderBy
+        public static IOrderedQueryable<T> OrderBy<T>(this IQueryable<T> query, LambdaExpression exp, bool asc = true)
+        {
+            if (asc)
+            {
+                var genericMethod = MethodInfo_OrderBy.MakeGenericMethod(typeof(T), exp.ReturnType);
+                return (IOrderedQueryable<T>)genericMethod.Invoke(null, new object[] { query, exp });
+            }
+            else
+            {
+                var genericMethod = MethodInfo_OrderByDescending.MakeGenericMethod(typeof(T), exp.ReturnType);
+                return (IOrderedQueryable<T>)genericMethod.Invoke(null, new object[] { query, exp });
+            }
+        }
+
+        public static IOrderedQueryable<T> ThenBy<T>(this IOrderedQueryable<T> query, LambdaExpression exp, bool asc = true)
+        {
+            if (asc)
+            {
+                var genericMethod = MethodInfo_ThenBy.MakeGenericMethod(typeof(T), exp.ReturnType);
+                return (IOrderedQueryable<T>)genericMethod.Invoke(null, new object[] { query, exp });
+            }
+            else
+            {
+                var genericMethod = MethodInfo_ThenByDescending.MakeGenericMethod(typeof(T), exp.ReturnType);
+                return (IOrderedQueryable<T>)genericMethod.Invoke(null, new object[] { query, exp });
+            }
+        }
+
+        static readonly MethodInfo MethodInfo_OrderBy = typeof(Queryable).GetMethods().FirstOrDefault(m => m.Name == "OrderBy" && m.GetParameters().Length == 2);
+        static readonly MethodInfo MethodInfo_OrderByDescending = typeof(Queryable).GetMethods().FirstOrDefault(m => m.Name == "OrderByDescending" && m.GetParameters().Length == 2);
+        static readonly MethodInfo MethodInfo_ThenBy = typeof(Queryable).GetMethods().FirstOrDefault(m => m.Name == "ThenBy" && m.GetParameters().Length == 2);
+        static readonly MethodInfo MethodInfo_ThenByDescending = typeof(Queryable).GetMethods().FirstOrDefault(m => m.Name == "ThenByDescending" && m.GetParameters().Length == 2);
+ 
+        #endregion
+
+    }
+}

+ 97 - 0
Vit.Linq/Query/Extensions/Queryable_Sort_ByReflection_Extensions.cs

@@ -0,0 +1,97 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Runtime.CompilerServices;
+using Vit.Core.Util.ComponentModel.Query;
+using Vit.Linq.Query;
+
+namespace Vit.Extensions
+{
+
+    public static partial class Queryable_Sort_ByReflection_Extensions
+    {
+
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        public static IQueryable<T> Sort_ByReflection<T>(this IQueryable<T> query, IEnumerable<SortItem> sort)
+            where T : class
+        {
+            if (query == null || sort == null) return query;
+
+            var sortCount = sort.Count();
+            if (sortCount == 0) return query;
+            if (sortCount > LinqHelp.MaxSortCount)
+            {
+                throw new Exception("排序条件个数过多");
+            }
+
+            IOrderedQueryable<T> orderedQuery = null;
+            foreach (var item in sort)
+            {
+                var keySelector = LinqHelp.BuildField_LambdaExpression_ByReflection<T>(item.field);
+
+                if (keySelector == null)
+                {
+                    continue;
+                }
+                if (item.asc)
+                {
+                    if (orderedQuery == null)
+                    {
+                        orderedQuery = query.OrderBy(keySelector);
+                    }
+                    else
+                    {
+                        orderedQuery = orderedQuery.ThenBy(keySelector);
+                    }
+                }
+                else
+                {
+                    if (orderedQuery == null)
+                    {
+                        orderedQuery = query.OrderByDescending(keySelector);
+                    }
+                    else
+                    {
+                        orderedQuery = orderedQuery.ThenByDescending(keySelector);
+                    }
+                }
+            }
+            return orderedQuery ?? query;
+        }
+
+
+        /// <summary>
+        /// 
+        /// </summary>
+        /// <typeparam name="T"></typeparam>
+        /// <param name="query"></param>
+        /// <param name="field">字段名</param>
+        /// <param name="asc">是否为正向排序</param>
+        /// <returns></returns>
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        public static IQueryable<T> Sort_ByReflection<T>(this IQueryable<T> query, string field, bool asc = true)
+           where T : class
+        {
+            if (query == null) return query;
+
+            var keySelector = LinqHelp.BuildField_LambdaExpression_ByReflection<T>(field);
+
+            if (keySelector == null)
+            {
+                return query;
+            }
+
+            if (asc)
+            {
+                query = query.OrderBy(keySelector);
+            }
+            else
+            {
+                query = query.OrderByDescending(keySelector);
+            }
+
+            return query;
+        }
+
+    }
+}

+ 84 - 0
Vit.Linq/Query/Extensions/Queryable_ToPageDataExtensions.cs

@@ -0,0 +1,84 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Runtime.CompilerServices;
+using Vit.Core.Util.ComponentModel.Data;
+using Vit.Core.Util.ComponentModel.Query;
+using Vit.Linq.Query;
+
+namespace Vit.Extensions
+{
+
+    public static partial class Queryable_ToPageDataExtensions
+    {
+
+        #region ToPageData       
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        public static PageData<T> ToPageData<T>(this IQueryable<T> query, PageInfo page)
+            where T : class
+        {
+            if (query == null) return null;
+
+            var queryPaged = query;
+            if (page != null)
+                queryPaged = queryPaged.Page(page);
+
+            return new PageData<T>(page) { totalCount = query.Count(), rows = queryPaged.ToList() };
+        }
+
+        public static PageData<T> ToPageData<T>(this IQueryable<T> query,
+            IEnumerable<DataFilter> filter, IEnumerable<SortItem> sort, PageInfo page
+        ) where T : class
+        {
+            return query?.Where(filter).Sort(sort).ToPageData(page);
+        }
+        #endregion
+
+
+        #region ToPageData with selector
+        /// <summary>
+        /// 注:先查询,后调用selector
+        /// </summary>
+        /// <typeparam name="T"></typeparam>
+        /// <typeparam name="TResult"></typeparam>
+        /// <param name="query"></param>
+        /// <param name="page"></param>
+        /// <param name="selector"></param>
+        /// <returns></returns>
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        public static PageData<TResult> ToPageData<T, TResult>(this IQueryable<T> query, PageInfo page, Func<T, TResult> selector)
+            where T : class
+        {
+            if (query == null) return null;
+
+            var queryPaged = query;
+            if (page != null)
+                queryPaged = queryPaged.Page(page);
+
+            return new PageData<TResult>(page) { totalCount = query.Count(), rows = queryPaged.ToList().Select(selector).ToList() };
+        }
+
+        /// <summary>
+        /// 注:先查询,后调用selector
+        /// </summary>
+        /// <typeparam name="T"></typeparam>
+        /// <typeparam name="TResult"></typeparam>
+        /// <param name="query"></param>
+        /// <param name="filter"></param>
+        /// <param name="sort"></param>
+        /// <param name="page"></param>
+        /// <param name="selector"></param>
+        /// <returns></returns>
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        public static PageData<TResult> ToPageData<T, TResult>(this IQueryable<T> query,
+            IEnumerable<DataFilter> filter, IEnumerable<SortItem> sort, PageInfo page, Func<T, TResult> selector
+        ) where T : class
+        {
+            return query?.Where(filter).Sort(sort).ToPageData(page, selector);
+        }
+        #endregion
+
+
+
+    }
+}

+ 31 - 0
Vit.Linq/Query/Extensions/Queryable_WhereExtensions.cs

@@ -0,0 +1,31 @@
+using System.Collections.Generic;
+using System.Linq;
+using System.Runtime.CompilerServices;
+using Vit.Linq.Query;
+
+namespace Vit.Extensions
+{
+    public  static partial class Queryable_WhereExtensions
+    {
+        #region Where
+
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        public static IQueryable<T> Where<T>(this IQueryable<T> query, IEnumerable<DataFilter> filters, ECondition condition = ECondition.AndAlso)
+          where T : class
+        {
+            if (query == null || filters == null) return query;
+
+            var predicate = filters.ToExpression<T>();
+            if (predicate == null)
+            {
+                return query;
+            }
+            else
+            {            
+                return query.Where(predicate);
+            }
+        }
+        #endregion
+
+    }
+}

+ 63 - 0
Vit.Linq/Query/LinqHelp.Reflection.cs

@@ -0,0 +1,63 @@
+using System;
+using System.Linq.Expressions;
+using System.Reflection;
+using System.Runtime.CompilerServices;
+
+namespace Vit.Linq.Query
+{
+    internal partial class LinqHelp
+    {  
+
+        #region BuildField_LambdaExpression
+
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        public static Func<T, object> BuildField_Selector_ByReflection<T>(Func<T, object> before,string fieldName)
+        {
+            return (T ori) => {
+
+                var midValue = before==null?ori: before(ori);
+                if (midValue == null) return null;
+
+                var midType = midValue.GetType();
+                //(x.1)property
+                var property = midType.GetProperty(fieldName);
+                if (property != null && property.CanRead)
+                {
+                    return property.GetValue(midValue);
+                }
+
+                //(x.2)field
+                var field = midType.GetField(fieldName, BindingFlags.Public | BindingFlags.Instance);
+                if (field != null)
+                {
+                    return  field.GetValue(midValue);
+                }
+
+                //(x.3) null
+                return null;
+            };
+        }
+
+
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        public  static Func<T, object> BuildField_Selector_ByReflection<T>(string fieldPath)
+        {
+            Func<T, object> getField=null;
+            foreach (var fieldName in fieldPath?.Split('.'))
+            {
+                getField = BuildField_Selector_ByReflection(getField, fieldName);
+            }
+            return getField;
+        }
+
+
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        internal static Expression<Func<T, object>> BuildField_LambdaExpression_ByReflection<T>(string fieldPath)
+        {           
+            Func<T, object> getField = BuildField_Selector_ByReflection<T>(fieldPath);            
+            return t => getField(t);
+        }
+        #endregion
+
+    }
+}

+ 89 - 0
Vit.Linq/Query/LinqHelp.cs

@@ -0,0 +1,89 @@
+using System;
+using System.Linq.Expressions;
+using System.Runtime.CompilerServices;
+using Vit.Core.Util.ConfigurationManager;
+
+namespace Vit.Linq.Query
+{
+    internal partial class LinqHelp
+    {
+        #region config
+
+
+        //    /*  Vit.Linq.dll 配置,可不指定。(Vit.Linq.MaxFilterCount  Vit.Linq.MaxSortCount)  */
+        //    "Linq": {
+        //      /* filter最大个数,超过这个个数则抛异常。可不指定,默认 50。 */
+        //      "MaxFilterCount": 50,
+
+        //      /* sort最大个数,超过这个个数则抛异常。可不指定,默认 50。 */
+        //      "MaxSortCount": 50
+        //    }      
+
+        /// <summary>
+        /// filter最大个数,超过这个个数则抛异常
+        /// </summary>
+        public static int MaxFilterCount = ConfigurationManager.Instance.GetByPath<int?>("Vit.Linq.MaxFilterCount") ?? 50;
+
+        /// <summary>
+        /// sort最大个数,超过这个个数则抛异常
+        /// </summary>
+        public static int MaxSortCount = ConfigurationManager.Instance.GetByPath<int?>("Vit.Linq.MaxSortCount") ?? 50;
+        #endregion
+
+        #region BuildField      
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        static MemberExpression BuildField_MemberExpression_ByName(Expression parameter, string propertyOrFieldName)
+        {
+            return Expression.PropertyOrField(parameter, propertyOrFieldName);
+        }
+
+        /// <summary>
+        /// 
+        /// </summary>
+        /// <param name="parameter"></param>
+        /// <param name="fieldPath">可多级。例如 "name" 、 "depart.name"</param>
+        /// <returns></returns>
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        internal static MemberExpression BuildField_MemberExpression(ParameterExpression parameter, string fieldPath)
+        {
+            MemberExpression memberExp = null;
+            foreach (var fieldName in fieldPath?.Split('.'))
+            {
+                memberExp = BuildField_MemberExpression_ByName(((Expression)memberExp) ?? parameter, fieldName);
+            }
+            return memberExp;
+        }
+
+
+        /// <summary>
+        /// 
+        /// </summary>
+        /// <param name="type"></param>
+        /// <param name="fieldPath">可多级。例如 "name" 、 "depart.name"</param>
+        /// <returns></returns>
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        internal static MemberExpression BuildField_MemberExpression(Type type, string fieldPath)
+        { 
+            return BuildField_MemberExpression(Expression.Parameter(type), fieldPath);
+        }
+
+        #endregion
+
+
+
+        #region BuildField_Selector
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        internal static Expression<Func<T, object>> BuildField_LambdaExpression<T>(string fieldPath)
+        {
+            var parammeter = Expression.Parameter(typeof(T));
+            MemberExpression memberExp = BuildField_MemberExpression(parammeter, fieldPath);
+            var lambda = Expression.Lambda(memberExp, parammeter).Compile();    
+            return t => lambda.DynamicInvoke(t);
+        }
+        #endregion
+
+
+
+
+    }
+}

+ 24 - 0
Vit.Linq/Vit.Linq.csproj

@@ -0,0 +1,24 @@
+<Project Sdk="Microsoft.NET.Sdk">
+
+	<PropertyGroup>
+		<TargetFramework>netstandard2.0</TargetFramework>
+		<Version>2.2.2</Version>
+	</PropertyGroup>
+
+	<PropertyGroup>
+		<Authors>Lith</Authors>
+		<Description>对Linq的扩展</Description>
+		<PackageProjectUrl>https://github.com/serset/Vit.Library</PackageProjectUrl>
+	</PropertyGroup>
+
+	<PropertyGroup>
+		<DocumentationFile>bin\Debug\netstandard2.0\Vit.Linq.xml</DocumentationFile>
+	</PropertyGroup>
+
+	<ItemGroup>
+		<PackageReference Include="Vit.Core" Version="2.1.5" />
+	</ItemGroup>
+
+
+
+</Project>