lith 1 年之前
父節點
當前提交
8007be1b14
共有 39 個文件被更改,包括 1179 次插入769 次删除
  1. 1 1
      src/Test/Vit.Linq.MsTest/DataSource.cs
  2. 1 1
      src/Test/Vit.Linq.MsTest/Extensions/IQueryable_SortAndPage_Test.cs
  3. 41 0
      src/Test/Vit.Linq.MsTest/Extensions/IQueryable_ToList_Test.cs
  4. 66 66
      src/Test/Vit.Linq.MsTest/Extensions/Queryable_SortAndPage_Test.cs
  5. 66 66
      src/Test/Vit.Linq.MsTest/Extensions/Queryable_Sort_ByReflection_Test.cs
  6. 43 0
      src/Test/Vit.Linq.MsTest/Extensions/Queryable_ToList_Test.cs
  7. 45 0
      src/Test/Vit.Linq.MsTest/QueryBuilder/CustomeValue_Test.cs
  8. 172 113
      src/Test/Vit.Linq.MsTest/QueryBuilder/Filter_TestBase.cs
  9. 51 0
      src/Test/Vit.Linq.MsTest/QueryBuilder/IQueryableTest/Filter_Test.cs
  10. 31 0
      src/Test/Vit.Linq.MsTest/QueryBuilder/IQueryableTest/Filter_Test_Newtonsoft.cs
  11. 29 0
      src/Test/Vit.Linq.MsTest/QueryBuilder/IQueryableTest/Filter_Test_Newtonsoft2.cs
  12. 32 0
      src/Test/Vit.Linq.MsTest/QueryBuilder/IQueryableTest/Filter_Test_SystemTextJson.cs
  13. 29 0
      src/Test/Vit.Linq.MsTest/QueryBuilder/IQueryableTest/Filter_Test_SystemTextJson2.cs
  14. 0 477
      src/Test/Vit.Linq.MsTest/QueryBuilder/IQueryable_QueryBuilder_Test.cs
  15. 31 0
      src/Test/Vit.Linq.MsTest/QueryBuilder/OperatorMap_Test.cs
  16. 51 0
      src/Test/Vit.Linq.MsTest/QueryBuilder/QueryableTest/Filter_Test.cs
  17. 31 0
      src/Test/Vit.Linq.MsTest/QueryBuilder/QueryableTest/Filter_Test_Newtonsoft.cs
  18. 29 0
      src/Test/Vit.Linq.MsTest/QueryBuilder/QueryableTest/Filter_Test_Newtonsoft2.cs
  19. 32 0
      src/Test/Vit.Linq.MsTest/QueryBuilder/QueryableTest/Filter_Test_SystemTextJson.cs
  20. 29 0
      src/Test/Vit.Linq.MsTest/QueryBuilder/QueryableTest/Filter_Test_SystemTextJson2.cs
  21. 19 13
      src/Test/Vit.Linq.MsTest/Vit.Linq.MsTest.csproj
  22. 28 0
      src/Test/Vit.Linq.MsTest31/Vit.Linq.MsTest31.csproj
  23. 104 0
      src/Vit.Linq.QueryBuilder.NewtonsoftJson/FilterRule_Newtonsoft.cs
  24. 25 0
      src/Vit.Linq.QueryBuilder.NewtonsoftJson/Vit.Linq.QueryBuilder.NewtonsoftJson.csproj
  25. 100 0
      src/Vit.Linq.QueryBuilder.SystemTextJson/FilterRule_SystemTextJson.cs
  26. 27 0
      src/Vit.Linq.QueryBuilder.SystemTextJson/Vit.Linq.QueryBuilder.SystemTextJson.csproj
  27. 2 2
      src/Vit.Linq.QueryBuilder/Extensions/IQueryable_Where_Extensions.cs
  28. 3 4
      src/Vit.Linq.QueryBuilder/Extensions/Queryable_Where_Extensions.cs
  29. 6 6
      src/Vit.Linq.QueryBuilder/IFilterRule.cs
  30. 28 12
      src/Vit.Linq.QueryBuilder/QueryBuilderService.cs
  31. 1 1
      src/Vit.Linq.QueryBuilder/Vit.Linq.QueryBuilder.csproj
  32. 19 0
      src/Vit.Linq.sln
  33. 1 1
      src/Vit.Linq/Extensions/IQueryable_Page_Extensions.cs
  34. 1 1
      src/Vit.Linq/Extensions/IQueryable_Sort_Extensions.cs
  35. 1 1
      src/Vit.Linq/Extensions/IQueryable_ToPageData_Extensions.cs
  36. 1 1
      src/Vit.Linq/Extensions/Queryable_Page_Extensions.cs
  37. 1 1
      src/Vit.Linq/Extensions/Queryable_Sort_Extensions.cs
  38. 1 1
      src/Vit.Linq/Extensions/Queryable_ToPageData_Extensions.cs
  39. 1 1
      src/Vit.Linq/Vit.Linq.csproj

+ 1 - 1
src/Test/Vit.Linq.MsTest/DataSource.cs

@@ -55,7 +55,7 @@ namespace Vit.Linq.MsTest
             return list;
         }
 
-        public static IQueryable GetIQueryable() => BuildDataSource().AsQueryable();
+        public static IQueryable GetIQueryable() => GetQueryable();
         public static IQueryable<ModelA> GetQueryable() => BuildDataSource().AsQueryable();
     }
 }

+ 1 - 1
src/Test/Vit.Linq.MsTest/IQueryable_SortAndPage_Test.cs → src/Test/Vit.Linq.MsTest/Extensions/IQueryable_SortAndPage_Test.cs

@@ -4,7 +4,7 @@ using Vit.Core.Util.ComponentModel.Data;
 using Vit.Core.Util.ComponentModel.Query;
 
 
-namespace Vit.Linq.MsTest
+namespace Vit.Linq.MsTest.Extensions
 {
     [TestClass]
     public class IQueryable_SortAndPage_Test

+ 41 - 0
src/Test/Vit.Linq.MsTest/Extensions/IQueryable_ToList_Test.cs

@@ -0,0 +1,41 @@
+using Vit.Extensions.Linq_Extensions;
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using System.Collections.Generic;
+
+namespace Vit.Linq.MsTest.Extensions
+{
+    [TestClass]
+    public class IQueryable_ToList_Test
+    {
+
+        [TestMethod]
+        public void Test_ToList()
+        {
+            var query = DataSource.GetIQueryable();
+
+            #region Count ToList ToArray
+            {
+
+                int count = query.IQueryable_Count();
+                Assert.AreEqual(1000, count);
+
+
+                var list1 = query.IQueryable_ToList<ModelA>();
+                Assert.AreEqual(1000, list1.Count);
+
+                var list2 = query.IQueryable_ToList() as List<ModelA>;
+                Assert.AreEqual(1000, list2.Count);
+
+
+                var array1 = query.IQueryable_ToArray<ModelA>();
+                Assert.AreEqual(1000, array1.Length);
+
+                var array2 = query.IQueryable_ToArray() as ModelA[];
+                Assert.AreEqual(1000, array2.Length);
+            }
+            #endregion
+        }
+
+
+    }
+}

+ 66 - 66
src/Test/Vit.Linq.MsTest/Queryable_SortAndPage_Test.cs → src/Test/Vit.Linq.MsTest/Extensions/Queryable_SortAndPage_Test.cs

@@ -1,66 +1,66 @@
-using Vit.Extensions.Linq_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 TestSortAndPage
-        [TestMethod]
-        public void TestSortAndPage()
-        {
-            var query = DataSource.GetQueryable();
-
-            #region #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 #2
-            {
-                var result = query
-                    .Sort("id", false)
-                    .Page(2, 10)
-                    .ToList();
-                Assert.AreEqual(result.Count, 10);
-                Assert.AreEqual(result[0].id, 989);
-            }
-            #endregion
-
-
-            #region #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
-
-    }
-}
+using Vit.Extensions.Linq_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.Extensions
+{
+    [TestClass]
+    public class Queryable_SortAndPage_Test
+    {
+
+        #region TestSortAndPage
+        [TestMethod]
+        public void TestSortAndPage()
+        {
+            var query = DataSource.GetQueryable();
+
+            #region #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 #2
+            {
+                var result = query
+                    .Sort("id", false)
+                    .Page(2, 10)
+                    .ToList();
+                Assert.AreEqual(result.Count, 10);
+                Assert.AreEqual(result[0].id, 989);
+            }
+            #endregion
+
+
+            #region #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 - 66
src/Test/Vit.Linq.MsTest/Queryable_Sort_ByReflection_Test.cs → src/Test/Vit.Linq.MsTest/Extensions/Queryable_Sort_ByReflection_Test.cs

@@ -1,66 +1,66 @@
-using Vit.Extensions.Linq_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 TestSortAndPage
-        [TestMethod]
-        public void TestSortAndPage()
-        {
-            var query = DataSource.GetQueryable();
-
-            #region #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 #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 #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
-
-    }
-}
+using Vit.Extensions.Linq_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.Extensions
+{
+    [TestClass]
+    public class Queryable_Sort_ByReflection_Test
+    {
+
+        #region TestSortAndPage
+        [TestMethod]
+        public void TestSortAndPage()
+        {
+            var query = DataSource.GetQueryable();
+
+            #region #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 #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 #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
+
+    }
+}

+ 43 - 0
src/Test/Vit.Linq.MsTest/Extensions/Queryable_ToList_Test.cs

@@ -0,0 +1,43 @@
+using Vit.Extensions.Linq_Extensions;
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using System.Collections.Generic;
+using System.Linq;
+
+namespace Vit.Linq.MsTest.Extensions
+{
+    [TestClass]
+    public class Queryable_ToList_Test
+    {
+
+
+        [TestMethod]
+        public void Test_ToList()
+        {
+            var query = DataSource.GetQueryable();
+
+            #region Count ToList ToArray
+            {
+
+                int count = query.Count();
+                Assert.AreEqual(1000, count);
+
+
+                var list1 = query.ToList<ModelA>();
+                Assert.AreEqual(1000, list1.Count);
+
+                var list2 = query.ToList();
+                Assert.AreEqual(1000, list2.Count);
+
+
+                var array1 = query.ToArray<ModelA>();
+                Assert.AreEqual(1000, array1.Length);
+
+                var array2 = query.ToArray();
+                Assert.AreEqual(1000, array2.Length);
+            }
+            #endregion
+        }
+
+
+    }
+}

+ 45 - 0
src/Test/Vit.Linq.MsTest/QueryBuilder/CustomeValue_Test.cs

@@ -0,0 +1,45 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using Vit.Core.Module.Serialization;
+using Vit.Linq.QueryBuilder;
+using Vit.Extensions.Linq_Extensions;
+using Newtonsoft.Json.Linq;
+
+namespace Vit.Linq.MsTest.QueryBuilder
+{
+
+    [TestClass]
+    public class CustomeValue_Test
+    {
+        [TestMethod]
+        public void Test_CustomeValue()
+        {
+            {
+                var service = new QueryBuilderService();
+                service.GetRuleValue = (object? value, IFilterRule rule, Type fieldType) =>
+                {
+                    // to deal with null value
+                    if (value is JValue jv) value = jv.Value;
+                    if (value is string str && rule.@operator?.Contains("in", StringComparison.OrdinalIgnoreCase) == true)
+                    {
+                        return str.Split(',');
+                    }
+                    return value;
+                };
+
+                var query = DataSource.GetQueryable();
+
+                var strRule = "{'field':'name',  'operator': 'in',  'value':'name3,name4' }".Replace("'", "\"");
+                var rule = Json.Deserialize<FilterRule>(strRule);
+                var result = query.Where(rule, service).ToList();
+                Assert.AreEqual(2, result.Count);
+                Assert.AreEqual("name3", result[0].name);
+                Assert.AreEqual("name4", result[1].name);
+            }
+        }
+    }
+}

+ 172 - 113
src/Test/Vit.Linq.MsTest/QueryBuilder/Queryable_QueryBuilder_Test.cs → src/Test/Vit.Linq.MsTest/QueryBuilder/Filter_TestBase.cs

@@ -1,83 +1,55 @@
-using System.Linq;
-using Microsoft.VisualStudio.TestTools.UnitTesting;
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Reflection;
 
-using Vit.Core.Module.Serialization;
 using Vit.Linq.QueryBuilder;
-using Vit.Extensions.Linq_Extensions;
 
 namespace Vit.Linq.MsTest.QueryBuilder
 {
-    [TestClass]
-    public class Queryable_QueryBuilder_Test
-    {
 
+    public abstract class Filter_TestBase<Query>
+    {
         IQueryable<ModelA> GetQueryable() => DataSource.GetQueryable();
 
 
-        [TestMethod]
-        public void Test_ToList()
-        {
-            var query = GetQueryable();
-
-            #region Count ToList ToArray
-            {
+        public abstract IFilterRule GetRule(string filterRule);
+        public abstract Query ToQuery(IQueryable<ModelA> query);
 
-                int count = query.Count();
-                Assert.AreEqual(1000, count);
+        public abstract List<ModelA> Filter(Query query, IFilterRule rule);
 
 
-                var list1 = query.ToList<ModelA>();
-                Assert.AreEqual(1000, list1.Count);
 
-                var list2 = query.ToList();
-                Assert.AreEqual(1000, list2.Count);
-
-
-                var array1 = query.ToArray<ModelA>();
-                Assert.AreEqual(1000, array1.Length);
-
-                var array2 = query.ToArray();
-                Assert.AreEqual(1000, array2.Length);
-            }
-            #endregion
-        }
+        protected void TestFilterRule()
+        {
 
+            #region #1 [object] IsNull | IsNotNull
 
-        [TestMethod]
-        public void Test_OperatorMap()
-        {
+            #region ##1 IsNull
             {
                 var query = GetQueryable();
-                QueryBuilderService.Instance.AddOperatorMap("Equal", FilterRuleOperator.Equal);
 
+                var item = query.Skip(10).FirstOrDefault();
+                item.name = null;
 
-                var strRule = "{'field':'isEven',  'operator': 'eQual',  'value':true }".Replace("'", "\"");
-                var rule = Json.Deserialize<FilterRule>(strRule);
-                var result = query.Where(rule).ToList();
-                Assert.AreEqual(result.Count, 500);
-                Assert.AreEqual(0, result[0].id);
+                var strRule = "{'field':'name',  'operator': 'IsNull'  }".Replace("'", "\"");
+                var rule = GetRule(strRule);
+                var result = Filter(ToQuery(query), rule);
+                Assert.AreEqual(1, result.Count);
+                Assert.AreEqual(10, result[0].id);
             }
-        }
-
-
-
-        #region Test_FilterRule
-        [TestMethod]
-        public void Test_FilterRule()
-        {
 
-            #region #1 [object] IsNull | IsNotNull
-
-            #region ##1 IsNull
             {
                 var query = GetQueryable();
 
                 var item = query.Skip(10).FirstOrDefault();
                 item.name = null;
 
-                var strRule = "{'field':'name',  'operator': 'IsNull'  }".Replace("'", "\"");
-                var rule = Json.Deserialize<FilterRule>(strRule);
-                var result = query.Where(rule).ToList();
+                var strRule = "{'field':'name',  'operator': '=',  'value': null  }".Replace("'", "\"");
+                var rule = GetRule(strRule);
+                var result = Filter(ToQuery(query), rule);
                 Assert.AreEqual(1, result.Count);
                 Assert.AreEqual(10, result[0].id);
             }
@@ -91,8 +63,20 @@ namespace Vit.Linq.MsTest.QueryBuilder
                 item.name = null;
 
                 var strRule = "{'field':'name',  'operator': 'IsNotNull'  }".Replace("'", "\"");
-                var rule = Json.Deserialize<FilterRule>(strRule);
-                var result = query.Where(rule).ToList();
+                var rule = GetRule(strRule);
+                var result = Filter(ToQuery(query), rule);
+                Assert.AreEqual(999, result.Count);
+            }
+
+            {
+                var query = GetQueryable();
+
+                var item = query.Skip(10).FirstOrDefault();
+                item.name = null;
+
+                var strRule = "{'field':'name',  'operator': '!=',  'value': null  }".Replace("'", "\"");
+                var rule = GetRule(strRule);
+                var result = Filter(ToQuery(query), rule);
                 Assert.AreEqual(999, result.Count);
             }
             #endregion
@@ -109,8 +93,8 @@ namespace Vit.Linq.MsTest.QueryBuilder
                     var query = GetQueryable();
 
                     var strRule = "{'field':'id',  'operator': '=',  'value':10 }".Replace("'", "\"");
-                    var rule = Json.Deserialize<FilterRule>(strRule);
-                    var result = query.Where(rule).ToList();
+                    var rule = GetRule(strRule);
+                    var result = Filter(ToQuery(query), rule);
                     Assert.AreEqual(1, result.Count);
                     Assert.AreEqual(10, result[0].id);
                 }
@@ -120,8 +104,8 @@ namespace Vit.Linq.MsTest.QueryBuilder
                     var query = GetQueryable();
 
                     var strRule = "{'field':'id',  'operator': '=',  'value': '10' }".Replace("'", "\"");
-                    var rule = Json.Deserialize<FilterRule>(strRule);
-                    var result = query.Where(rule).ToList();
+                    var rule = GetRule(strRule);
+                    var result = Filter(ToQuery(query), rule);
                     Assert.AreEqual(1, result.Count);
                     Assert.AreEqual(10, result[0].id);
                 }
@@ -135,9 +119,9 @@ namespace Vit.Linq.MsTest.QueryBuilder
                     item.pid = null;
 
                     var strRule = "{'field':'pid',  'operator': '=',  'value': null }".Replace("'", "\"");
-                    var rule = Json.Deserialize<FilterRule>(strRule);
-                    var result = query.Where(rule).FirstOrDefault();
-                    Assert.AreEqual(10, result.id);
+                    var rule = GetRule(strRule);
+                    var result = Filter(ToQuery(query), rule);
+                    Assert.AreEqual(10, result.FirstOrDefault().id);
                 }
 
 
@@ -151,8 +135,8 @@ namespace Vit.Linq.MsTest.QueryBuilder
                     var query = GetQueryable();
 
                     var strRule = "{'field':'isEven',  'operator': '=',  'value':true }".Replace("'", "\"");
-                    var rule = Json.Deserialize<FilterRule>(strRule);
-                    var result = query.Where(rule).ToList();
+                    var rule = GetRule(strRule);
+                    var result = Filter(ToQuery(query), rule);
                     Assert.AreEqual(result.Count, 500);
                     Assert.AreEqual(0, result[0].id);
                 }
@@ -162,8 +146,8 @@ namespace Vit.Linq.MsTest.QueryBuilder
                     var query = GetQueryable();
 
                     var strRule = "{'field':'isEven',  'operator': '=',  'value': 'false' }".Replace("'", "\"");
-                    var rule = Json.Deserialize<FilterRule>(strRule);
-                    var result = query.Where(rule).ToList();
+                    var rule = GetRule(strRule);
+                    var result = Filter(ToQuery(query), rule);
                     Assert.AreEqual(result.Count, 500);
                     Assert.AreEqual(1, result[0].id);
                 }
@@ -177,8 +161,8 @@ namespace Vit.Linq.MsTest.QueryBuilder
                     var query = GetQueryable();
 
                     var strRule = "{'field':'id',  'operator': '!=',  'value':10 }".Replace("'", "\"");
-                    var rule = Json.Deserialize<FilterRule>(strRule);
-                    var result = query.Where(rule).ToList();
+                    var rule = GetRule(strRule);
+                    var result = Filter(ToQuery(query), rule);
                     Assert.AreEqual(999, result.Count);
                 }
 
@@ -190,8 +174,8 @@ namespace Vit.Linq.MsTest.QueryBuilder
                     item.pid = null;
 
                     var strRule = "{'field':'pid',  'operator': '!=',  'value': null }".Replace("'", "\"");
-                    var rule = Json.Deserialize<FilterRule>(strRule);
-                    var result = query.Where(rule).ToList();
+                    var rule = GetRule(strRule);
+                    var result = Filter(ToQuery(query), rule);
                     Assert.AreEqual(999, result.Count);
                 }
             }
@@ -204,8 +188,8 @@ namespace Vit.Linq.MsTest.QueryBuilder
                     var query = GetQueryable();
 
                     var strRule = "{'field':'isEven',  'operator': '!=',  'value':true }".Replace("'", "\"");
-                    var rule = Json.Deserialize<FilterRule>(strRule);
-                    var result = query.Where(rule).ToList();
+                    var rule = GetRule(strRule);
+                    var result = Filter(ToQuery(query), rule);
                     Assert.AreEqual(result.Count, 500);
                     Assert.AreEqual(1, result[0].id);
                 }
@@ -215,8 +199,8 @@ namespace Vit.Linq.MsTest.QueryBuilder
                     var query = GetQueryable();
 
                     var strRule = "{'field':'isEven',  'operator': '!=',  'value': 'false' }".Replace("'", "\"");
-                    var rule = Json.Deserialize<FilterRule>(strRule);
-                    var result = query.Where(rule).ToList();
+                    var rule = GetRule(strRule);
+                    var result = Filter(ToQuery(query), rule);
                     Assert.AreEqual(result.Count, 500);
                     Assert.AreEqual(0, result[0].id);
                 }
@@ -233,8 +217,8 @@ namespace Vit.Linq.MsTest.QueryBuilder
                                         {'field':'id',  'operator': '>',  'value':10  },
                                         {'field':'id',  'operator': '<',  'value': '20' } 
                                     ]}".Replace("'", "\"");
-                    var rule = Json.Deserialize<FilterRule>(strRule);
-                    var result = query.Where(rule).ToList();
+                    var rule = GetRule(strRule);
+                    var result = Filter(ToQuery(query), rule);
                     Assert.AreEqual(9, result.Count);
                     Assert.AreEqual(11, result[0].id);
                 }
@@ -251,8 +235,8 @@ namespace Vit.Linq.MsTest.QueryBuilder
                                         {'field':'id',  'operator': '>=',  'value':10  },
                                         {'field':'id',  'operator': '<=',  'value': '20' } 
                                     ]}".Replace("'", "\"");
-                    var rule = Json.Deserialize<FilterRule>(strRule);
-                    var result = query.Where(rule).ToList();
+                    var rule = GetRule(strRule);
+                    var result = Filter(ToQuery(query), rule);
                     Assert.AreEqual(11, result.Count);
                     Assert.AreEqual(10, result[0].id);
                 }
@@ -270,8 +254,8 @@ namespace Vit.Linq.MsTest.QueryBuilder
                     var query = GetQueryable();
 
                     var strRule = "{'field':'id',  'operator': 'In',  'value': [3,4,5] }".Replace("'", "\"");
-                    var rule = Json.Deserialize<FilterRule>(strRule);
-                    var result = query.Where(rule).ToList();
+                    var rule = GetRule(strRule);
+                    var result = Filter(ToQuery(query), rule);
                     Assert.AreEqual(3, result.Count);
                     Assert.AreEqual(5, result[2].id);
                 }
@@ -280,22 +264,34 @@ namespace Vit.Linq.MsTest.QueryBuilder
                     var query = GetQueryable();
 
                     var strRule = "{'field':'name',  'operator': 'In',  'value': [ 'name3', 'name4'] }".Replace("'", "\"");
-                    var rule = Json.Deserialize<FilterRule>(strRule);
-                    var result = query.Where(rule).ToList();
+                    var rule = GetRule(strRule);
+                    var result = Filter(ToQuery(query), rule);
                     Assert.AreEqual(2, result.Count);
                     Assert.AreEqual("name3", result[0].name);
                 }
 
                 {
                     var query = GetQueryable();
-                    query.FirstOrDefault().name = null;
+                    query.First().name = null;
 
                     var strRule = @"{'condition':'or', 'rules':[
                                         {'field':'name',  'operator': 'IsNull' },
                                         {'field':'name',  'operator': 'In',  'value': [ 'name3', 'name4'] } 
                                     ]}".Replace("'", "\"");
-                    var rule = Json.Deserialize<FilterRule>(strRule);
-                    var result = query.Where(rule).ToList();
+                    var rule = GetRule(strRule);
+                    var result = Filter(ToQuery(query), rule);
+                    Assert.AreEqual(3, result.Count);
+                    Assert.AreEqual(null, result[0].name);
+                    Assert.AreEqual("name4", result[2].name);
+                }
+
+                {
+                    var query = GetQueryable();
+                    query.First().name = null;
+
+                    var strRule = @"{'field':'name',  'operator': 'In',  'value': [ 'name3', 'name4', null ] }".Replace("'", "\"");
+                    var rule = GetRule(strRule);
+                    var result = Filter(ToQuery(query), rule);
                     Assert.AreEqual(3, result.Count);
                     Assert.AreEqual(null, result[0].name);
                     Assert.AreEqual("name4", result[2].name);
@@ -312,8 +308,8 @@ namespace Vit.Linq.MsTest.QueryBuilder
                                         {'field':'name',  'operator': 'IsNotNull' },
                                         {'field':'name',  'operator': 'NotIn',  'value': [ 'name3', 'name4'] } 
                                     ]}".Replace("'", "\"");
-                var rule = Json.Deserialize<FilterRule>(strRule);
-                var result = query.Where(rule).ToList();
+                var rule = GetRule(strRule);
+                var result = Filter(ToQuery(query), rule);
                 Assert.AreEqual(997, result.Count);
             }
             #endregion
@@ -328,8 +324,8 @@ namespace Vit.Linq.MsTest.QueryBuilder
                 var query = GetQueryable();
 
                 var strRule = "{'field':'name',  'operator': 'Contains',  'value': '987' }".Replace("'", "\"");
-                var rule = Json.Deserialize<FilterRule>(strRule);
-                var result = query.Where(rule).ToList();
+                var rule = GetRule(strRule);
+                var result = Filter(ToQuery(query), rule);
 
                 Assert.AreEqual(1, result.Count);
                 Assert.AreEqual(987, result.First().id);
@@ -343,8 +339,8 @@ namespace Vit.Linq.MsTest.QueryBuilder
                     var query = GetQueryable();
 
                     var strRule = "{'field':'name',  'operator': 'NotContains',  'value': '987' }".Replace("'", "\"");
-                    var rule = Json.Deserialize<FilterRule>(strRule);
-                    var result = query.Where(rule).ToList();
+                    var rule = GetRule(strRule);
+                    var result = Filter(ToQuery(query), rule);
                     Assert.AreEqual(999, result.Count);
                 }
 
@@ -355,8 +351,8 @@ namespace Vit.Linq.MsTest.QueryBuilder
                     query.Skip(987).FirstOrDefault().name = null;
 
                     var strRule = "{'field':'name',  'operator': 'NotContains',  'value': '987' }".Replace("'", "\"");
-                    var rule = Json.Deserialize<FilterRule>(strRule);
-                    var result = query.Where(rule).ToList();
+                    var rule = GetRule(strRule);
+                    var result = Filter(ToQuery(query), rule);
                     Assert.AreEqual(1000, result.Count);
                 }
 
@@ -366,8 +362,8 @@ namespace Vit.Linq.MsTest.QueryBuilder
                     query.Skip(987).FirstOrDefault().name = "";
 
                     var strRule = "{'field':'name',  'operator': 'NotContains',  'value': '987' }".Replace("'", "\"");
-                    var rule = Json.Deserialize<FilterRule>(strRule);
-                    var result = query.Where(rule).ToList();
+                    var rule = GetRule(strRule);
+                    var result = Filter(ToQuery(query), rule);
                     Assert.AreEqual(1000, result.Count);
                 }
             }
@@ -378,8 +374,8 @@ namespace Vit.Linq.MsTest.QueryBuilder
                 var query = GetQueryable();
 
                 var strRule = "{'field':'name',  'operator': 'StartsWith',  'value': 'name98' }".Replace("'", "\"");
-                var rule = Json.Deserialize<FilterRule>(strRule);
-                var result = query.Where(rule).ToList();
+                var rule = GetRule(strRule);
+                var result = Filter(ToQuery(query), rule);
 
                 Assert.AreEqual(11, result.Count);
             }
@@ -390,8 +386,8 @@ namespace Vit.Linq.MsTest.QueryBuilder
                 var query = GetQueryable();
 
                 var strRule = "{'field':'name',  'operator': 'EndsWith',  'value': '987' }".Replace("'", "\"");
-                var rule = Json.Deserialize<FilterRule>(strRule);
-                var result = query.Where(rule).ToList();
+                var rule = GetRule(strRule);
+                var result = Filter(ToQuery(query), rule);
 
                 Assert.AreEqual(1, result.Count);
             }
@@ -405,8 +401,8 @@ namespace Vit.Linq.MsTest.QueryBuilder
                     query.Skip(10).FirstOrDefault().name = null;
 
                     var strRule = "{'field':'name',  'operator': 'IsNullOrEmpty' }".Replace("'", "\"");
-                    var rule = Json.Deserialize<FilterRule>(strRule);
-                    var result = query.Where(rule).ToList();
+                    var rule = GetRule(strRule);
+                    var result = Filter(ToQuery(query), rule);
 
                     Assert.AreEqual(1, result.Count);
 
@@ -418,8 +414,8 @@ namespace Vit.Linq.MsTest.QueryBuilder
                     query.Skip(10).FirstOrDefault().name = "";
 
                     var strRule = "{'field':'name',  'operator': 'IsNullOrEmpty' }".Replace("'", "\"");
-                    var rule = Json.Deserialize<FilterRule>(strRule);
-                    var result = query.Where(rule).ToList();
+                    var rule = GetRule(strRule);
+                    var result = Filter(ToQuery(query), rule);
 
                     Assert.AreEqual(1, result.Count);
                 }
@@ -434,8 +430,8 @@ namespace Vit.Linq.MsTest.QueryBuilder
                     query.Skip(10).FirstOrDefault().name = null;
 
                     var strRule = "{'field':'name',  'operator': 'IsNotNullOrEmpty' }".Replace("'", "\"");
-                    var rule = Json.Deserialize<FilterRule>(strRule);
-                    var result = query.Where(rule).ToList();
+                    var rule = GetRule(strRule);
+                    var result = Filter(ToQuery(query), rule);
 
                     Assert.AreEqual(999, result.Count);
                 }
@@ -445,8 +441,8 @@ namespace Vit.Linq.MsTest.QueryBuilder
                     query.Skip(10).FirstOrDefault().name = "";
 
                     var strRule = "{'field':'name',  'operator': 'IsNotNullOrEmpty' }".Replace("'", "\"");
-                    var rule = Json.Deserialize<FilterRule>(strRule);
-                    var result = query.Where(rule).ToList();
+                    var rule = GetRule(strRule);
+                    var result = Filter(ToQuery(query), rule);
 
                     Assert.AreEqual(999, result.Count);
                 }
@@ -457,13 +453,78 @@ namespace Vit.Linq.MsTest.QueryBuilder
             #endregion
 
 
-            #region #5  nested field
+            #region #5  DateTime
+            {
+                // ##1 string =
+                {
+                    var query = GetQueryable();
+                    var model = query.Skip(100).First();
+                    var strTime = model.addTime.ToString("yyyy-MM-dd HH:mm:ss");
+                    model.addTime = DateTime.Parse(strTime);
+                    var strRule = $"{{'field':'addTime',  'operator': '=',  'value': '{strTime}' }}".Replace("'", "\"");
+                    var rule = GetRule(strRule);
+
+                    var result = Filter(ToQuery(query), rule);
+
+                    var model2 = result.First();
+                    Assert.AreEqual(1, result.Count);
+                    Assert.AreEqual(model.id, model2.id);
+                }
+
+                // ##2 string <
+                {
+                    var query = GetQueryable();
+                    var model = query.Skip(100).First();
+                    var strTime = model.addTime.ToString("yyyy-MM-dd HH:mm:ss");
+                    model.addTime = DateTime.Parse(strTime);
+                    var strRule = $"{{'field':'addTime',  'operator': '<',  'value': '{strTime}' }}".Replace("'", "\"");
+                    var rule = GetRule(strRule);
+
+                    var result = Filter(ToQuery(query), rule);
+
+                    Assert.AreEqual(100, result.Count);
+                }
+
+
+                // ##3 DateTime =
+                {
+                    var query = GetQueryable();
+                    var model = query.Skip(100).First(); 
+                    var strRule = $"{{'field':'addTime',  'operator': '=' }}".Replace("'", "\"");
+                    var rule = GetRule(strRule);
+                    rule.value = model.addTime;
+
+                    var result = Filter(ToQuery(query), rule);
+
+                    var model2 = result.First();
+                    Assert.AreEqual(1, result.Count);
+                    Assert.AreEqual(model.id, model2.id);
+                }
+
+                // ##4 DateTime <
+                {
+                    var query = GetQueryable();
+                    var model = query.Skip(100).First();
+                    var strRule = $"{{'field':'addTime',  'operator': '<' }}".Replace("'", "\"");
+                    var rule = GetRule(strRule);
+                    rule.value = model.addTime;
+
+                    var result = Filter(ToQuery(query), rule);
+
+                    Assert.AreEqual(100, result.Count);
+                }
+            }
+            #endregion
+
+
+
+            #region #6  nested field
             {
                 var query = GetQueryable();
                 var strRule = "{'field':'b1.name',  'operator': '=',  'value': 'name987_b1' }".Replace("'", "\"");
-                var rule = Json.Deserialize<FilterRule>(strRule);
+                var rule = GetRule(strRule);
 
-                var result = query.Where(rule).ToList();
+                var result = Filter(ToQuery(query), rule);
 
                 Assert.AreEqual(1, result.Count);
                 Assert.AreEqual(987, result[0].id);
@@ -472,10 +533,8 @@ namespace Vit.Linq.MsTest.QueryBuilder
 
         }
 
-        #endregion
-
-
 
 
     }
 }
+

+ 51 - 0
src/Test/Vit.Linq.MsTest/QueryBuilder/IQueryableTest/Filter_Test.cs

@@ -0,0 +1,51 @@
+using Vit.Extensions.Linq_Extensions;
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using System.Collections.Generic;
+using System.Linq;
+using Vit.Core.Module.Serialization;
+using Vit.Linq.QueryBuilder;
+using System;
+using Newtonsoft.Json.Linq;
+using Queryable = System.Linq.IQueryable;
+
+namespace Vit.Linq.MsTest.QueryBuilder.IQueryableTest
+{
+    [TestClass]
+    public class Filter_Test : Filter_TestBase<Queryable>
+    {
+
+        [TestMethod]
+        public void Test_FilterRule_()
+        {
+            base.TestFilterRule();
+        }
+
+
+        public override IFilterRule GetRule(string filterRule)
+        {
+            return Json.Deserialize<FilterRule>(filterRule);
+        }
+
+        public override Queryable ToQuery(IQueryable<ModelA> query) => query;
+
+        public override List<ModelA> Filter(Queryable query, IFilterRule rule)
+        {
+            return query.IQueryable_Where(rule, GetService()).IQueryable_ToList<ModelA>();
+        }
+
+        public virtual QueryBuilderService GetService()
+        {
+            QueryBuilderService service = new QueryBuilderService();
+            service.GetRuleValue = (object value, IFilterRule rule, Type fieldType) =>
+            {
+                // to deal with null value
+                if (value is JValue jv) return jv.Value;
+                return value;
+            };
+            return service;
+        }
+
+    }
+}
+
+

+ 31 - 0
src/Test/Vit.Linq.MsTest/QueryBuilder/IQueryableTest/Filter_Test_Newtonsoft.cs

@@ -0,0 +1,31 @@
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+
+using Vit.Core.Module.Serialization;
+using Vit.Linq.QueryBuilder;
+using Vit.Linq.QueryBuilder.NewtonsoftJson;
+
+namespace Vit.Linq.MsTest.QueryBuilder.IQueryableTest
+{
+    [TestClass]
+    public class Filter_Test_Newtonsoft : Filter_Test
+    {
+
+        public override IFilterRule GetRule(string filterRule)
+        {
+            return Json.Deserialize<FilterRule_Newtonsoft>(filterRule);
+        }
+
+        public override QueryBuilderService GetService()
+        {
+            QueryBuilderService service = new QueryBuilderService();
+            return service;
+        }
+
+
+        [TestMethod]
+        public void Test_FilterRule()
+        {
+            base.TestFilterRule();
+        }
+    }
+}

+ 29 - 0
src/Test/Vit.Linq.MsTest/QueryBuilder/IQueryableTest/Filter_Test_Newtonsoft2.cs

@@ -0,0 +1,29 @@
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+
+using Vit.Linq.QueryBuilder;
+using Vit.Linq.QueryBuilder.NewtonsoftJson;
+
+namespace Vit.Linq.MsTest.QueryBuilder.IQueryableTest
+{
+    [TestClass]
+    public class Filter_Test_Newtonsoft2 : Filter_Test
+    {
+
+        public override IFilterRule GetRule(string filterRule)
+        {
+            return FilterRule_Newtonsoft.FromString(filterRule);
+        }
+        public override QueryBuilderService GetService()
+        {
+            QueryBuilderService service = new QueryBuilderService();
+            return service;
+        }
+
+        [TestMethod]
+        public void Test_FilterRule()
+        {
+            base.TestFilterRule();
+        }
+
+    }
+}

+ 32 - 0
src/Test/Vit.Linq.MsTest/QueryBuilder/IQueryableTest/Filter_Test_SystemTextJson.cs

@@ -0,0 +1,32 @@
+using System.Text.Json;
+
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+
+using Vit.Linq.QueryBuilder;
+using Vit.Linq.QueryBuilder.SystemTextJson;
+
+namespace Vit.Linq.MsTest.QueryBuilder.IQueryableTest
+{
+    [TestClass]
+    public class Filter_Test_SystemTextJson : Filter_Test
+    {
+
+        public override IFilterRule GetRule(string filterRule)
+        {
+            return JsonSerializer.Deserialize<FilterRule_SystemTextJson>(filterRule);
+        }
+
+        public override QueryBuilderService GetService()
+        {
+            QueryBuilderService service = new QueryBuilderService();
+            return service;
+        }
+
+
+        [TestMethod]
+        public void Test_FilterRule()
+        {
+            base.TestFilterRule();
+        }
+    }
+}

+ 29 - 0
src/Test/Vit.Linq.MsTest/QueryBuilder/IQueryableTest/Filter_Test_SystemTextJson2.cs

@@ -0,0 +1,29 @@
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+
+using Vit.Linq.QueryBuilder;
+using Vit.Linq.QueryBuilder.SystemTextJson;
+
+namespace Vit.Linq.MsTest.QueryBuilder.IQueryableTest
+{
+    [TestClass]
+    public class Filter_Test_SystemTextJson2 : Filter_Test
+    {
+
+        public override IFilterRule GetRule(string filterRule)
+        {
+            return FilterRule_SystemTextJson.FromString(filterRule);
+        }
+        public override QueryBuilderService GetService()
+        {
+            QueryBuilderService service = new QueryBuilderService();
+            return service;
+        }
+
+        [TestMethod]
+        public void Test_FilterRule()
+        {
+            base.TestFilterRule();
+        }
+
+    }
+}

+ 0 - 477
src/Test/Vit.Linq.MsTest/QueryBuilder/IQueryable_QueryBuilder_Test.cs

@@ -1,477 +0,0 @@
-using Vit.Extensions.Linq_Extensions;
-using Microsoft.VisualStudio.TestTools.UnitTesting;
-using System.Collections.Generic;
-using System.Linq;
-using Vit.Core.Module.Serialization;
-using Vit.Linq.QueryBuilder;
-using Vit.Linq.MsTest.Extensions;
-
-namespace Vit.Linq.MsTest
-{
-
-    [TestClass]
-    public class IQueryable_QueryBuilder_Test
-    {
-        IQueryable GetQueryable() => DataSource.GetIQueryable();
-
-
-        [TestMethod]
-        public void Test_ToList()
-        {
-            var query = GetQueryable();
-
-            #region Count ToList ToArray
-            {
-
-                int count = query.IQueryable_Count();
-                Assert.AreEqual(1000, count);
-
-
-                var list1 = query.IQueryable_ToList<ModelA>();
-                Assert.AreEqual(1000, list1.Count);
-
-                var list2 = query.IQueryable_ToList() as List<ModelA>;
-                Assert.AreEqual(1000, list2.Count);
-
-
-                var array1 = query.IQueryable_ToArray<ModelA>();
-                Assert.AreEqual(1000, array1.Length);
-
-                var array2 = query.IQueryable_ToArray() as ModelA[];
-                Assert.AreEqual(1000, array2.Length);
-            }
-            #endregion
-        }
-
-
-        #region Test_FilterRule
-        [TestMethod]
-        public void Test_FilterRule()
-        {
-
-            #region #1 [object] IsNull | IsNotNull
-
-            #region ##1 IsNull
-            {
-                var query = GetQueryable();
-
-                var item = query.Skip(10).FirstOrDefault();
-                item.name = null;
-
-                var strRule = "{'field':'name',  'operator': 'IsNull'  }".Replace("'", "\"");
-                var rule = Json.Deserialize<FilterRule>(strRule);
-                var result = query.Where(rule).ToList();
-                Assert.AreEqual(1, result.Count);
-                Assert.AreEqual(10, result[0].id);
-            }
-            #endregion
-
-            #region ##2 IsNotNull
-            {
-                var query = GetQueryable();
-
-                var item = query.Skip(10).FirstOrDefault();
-                item.name = null;
-
-                var strRule = "{'field':'name',  'operator': 'IsNotNull'  }".Replace("'", "\"");
-                var rule = Json.Deserialize<FilterRule>(strRule);
-                var result = query.Where(rule).ToList();
-                Assert.AreEqual(999, result.Count);
-            }
-            #endregion
-
-            #endregion
-
-
-            #region #2 [number | string | bool] compare
-
-            #region ##1.1 [number] =
-            {
-                // ###1
-                {
-                    var query = GetQueryable();
-
-                    var strRule = "{'field':'id',  'operator': '=',  'value':10 }".Replace("'", "\"");
-                    var rule = Json.Deserialize<FilterRule>(strRule);
-                    var result = query.Where(rule).ToList();
-                    Assert.AreEqual(1, result.Count);
-                    Assert.AreEqual(10, result[0].id);
-                }
-
-                // ###2
-                {
-                    var query = GetQueryable();
-
-                    var strRule = "{'field':'id',  'operator': '=',  'value': '10' }".Replace("'", "\"");
-                    var rule = Json.Deserialize<FilterRule>(strRule);
-                    var result = query.Where(rule).ToList();
-                    Assert.AreEqual(1, result.Count);
-                    Assert.AreEqual(10, result[0].id);
-                }
-
-
-                // ###3  = null
-                {
-                    var query = GetQueryable();
-
-                    var item = query.Skip(10).FirstOrDefault();
-                    item.pid = null;
-
-                    var strRule = "{'field':'pid',  'operator': '=',  'value': null }".Replace("'", "\"");
-                    var rule = Json.Deserialize<FilterRule>(strRule);
-                    var result = query.Where(rule).FirstOrDefault();
-                    Assert.AreEqual(10, result.id);
-                }
-
-
-            }
-            #endregion
-
-            #region ##1.2 [bool] =
-            {
-                // ###1
-                {
-                    var query = GetQueryable();
-
-                    var strRule = "{'field':'isEven',  'operator': '=',  'value':true }".Replace("'", "\"");
-                    var rule = Json.Deserialize<FilterRule>(strRule);
-                    var result = query.Where(rule).ToList();
-                    Assert.AreEqual(result.Count, 500);
-                    Assert.AreEqual(0, result[0].id);
-                }
-
-                // ###2
-                {
-                    var query = GetQueryable();
-
-                    var strRule = "{'field':'isEven',  'operator': '=',  'value': 'false' }".Replace("'", "\"");
-                    var rule = Json.Deserialize<FilterRule>(strRule);
-                    var result = query.Where(rule).ToList();
-                    Assert.AreEqual(result.Count, 500);
-                    Assert.AreEqual(1, result[0].id);
-                }
-            }
-            #endregion
-
-            #region ##2.1 [number] !=
-            {
-                // ###1
-                {
-                    var query = GetQueryable();
-
-                    var strRule = "{'field':'id',  'operator': '!=',  'value':10 }".Replace("'", "\"");
-                    var rule = Json.Deserialize<FilterRule>(strRule);
-                    var result = query.Where(rule).ToList();
-                    Assert.AreEqual(999, result.Count);
-                }
-
-                // ###2 != null
-                {
-                    var query = GetQueryable();
-
-                    var item = query.Skip(10).FirstOrDefault();
-                    item.pid = null;
-
-                    var strRule = "{'field':'pid',  'operator': '!=',  'value': null }".Replace("'", "\"");
-                    var rule = Json.Deserialize<FilterRule>(strRule);
-                    var result = query.Where(rule).ToList();
-                    Assert.AreEqual(999, result.Count);
-                }
-            }
-            #endregion
-
-            #region ##2.2 [bool] !=
-            {
-                // ###1
-                {
-                    var query = GetQueryable();
-
-                    var strRule = "{'field':'isEven',  'operator': '!=',  'value':true }".Replace("'", "\"");
-                    var rule = Json.Deserialize<FilterRule>(strRule);
-                    var result = query.Where(rule).ToList();
-                    Assert.AreEqual(result.Count, 500);
-                    Assert.AreEqual(1, result[0].id);
-                }
-
-                // ###2
-                {
-                    var query = GetQueryable();
-
-                    var strRule = "{'field':'isEven',  'operator': '!=',  'value': 'false' }".Replace("'", "\"");
-                    var rule = Json.Deserialize<FilterRule>(strRule);
-                    var result = query.Where(rule).ToList();
-                    Assert.AreEqual(result.Count, 500);
-                    Assert.AreEqual(0, result[0].id);
-                }
-            }
-            #endregion
-
-
-            #region ##3 [number] > <
-            {
-                {
-                    var query = GetQueryable();
-
-                    var strRule = @"{'condition':'and', 'rules':[   
-                                        {'field':'id',  'operator': '>',  'value':10  },
-                                        {'field':'id',  'operator': '<',  'value': '20' } 
-                                    ]}".Replace("'", "\"");
-                    var rule = Json.Deserialize<FilterRule>(strRule);
-                    var result = query.Where(rule).ToList();
-                    Assert.AreEqual(9, result.Count);
-                    Assert.AreEqual(11, result[0].id);
-                }
-            }
-            #endregion
-
-
-            #region ##4 [number] >= <=
-            {
-                {
-                    var query = GetQueryable();
-
-                    var strRule = @"{'condition':'and', 'rules':[   
-                                        {'field':'id',  'operator': '>=',  'value':10  },
-                                        {'field':'id',  'operator': '<=',  'value': '20' } 
-                                    ]}".Replace("'", "\"");
-                    var rule = Json.Deserialize<FilterRule>(strRule);
-                    var result = query.Where(rule).ToList();
-                    Assert.AreEqual(11, result.Count);
-                    Assert.AreEqual(10, result[0].id);
-                }
-            }
-            #endregion
-
-            #endregion
-
-
-            #region #3  In | NotIn
-
-            #region ##1 In
-            {
-                {
-                    var query = GetQueryable();
-
-                    var strRule = "{'field':'id',  'operator': 'In',  'value': [3,4,5] }".Replace("'", "\"");
-                    var rule = Json.Deserialize<FilterRule>(strRule);
-                    var result = query.Where(rule).ToList();
-                    Assert.AreEqual(3, result.Count);
-                    Assert.AreEqual(5, result[2].id);
-                }
-
-                {
-                    var query = GetQueryable();
-
-                    var strRule = "{'field':'name',  'operator': 'In',  'value': [ 'name3', 'name4'] }".Replace("'", "\"");
-                    var rule = Json.Deserialize<FilterRule>(strRule);
-                    var result = query.Where(rule).ToList();
-                    Assert.AreEqual(2, result.Count);
-                    Assert.AreEqual("name3", result[0].name);
-                }
-
-                {
-                    var query = GetQueryable();
-                    query.FirstOrDefault().name = null;
-
-                    var strRule = @"{'condition':'or', 'rules':[
-                                        {'field':'name',  'operator': 'IsNull' },
-                                        {'field':'name',  'operator': 'In',  'value': [ 'name3', 'name4'] } 
-                                    ]}".Replace("'", "\"");
-                    var rule = Json.Deserialize<FilterRule>(strRule);
-                    var result = query.Where(rule).ToList();
-                    Assert.AreEqual(3, result.Count);
-                    Assert.AreEqual(null, result[0].name);
-                    Assert.AreEqual("name4", result[2].name);
-                }
-            }
-            #endregion
-
-            #region ##2  NotIn
-            {
-                var query = GetQueryable();
-                query.FirstOrDefault().name = null;
-
-                var strRule = @"{'condition':'and', 'rules':[   
-                                        {'field':'name',  'operator': 'IsNotNull' },
-                                        {'field':'name',  'operator': 'NotIn',  'value': [ 'name3', 'name4'] } 
-                                    ]}".Replace("'", "\"");
-                var rule = Json.Deserialize<FilterRule>(strRule);
-                var result = query.Where(rule).ToList();
-                Assert.AreEqual(997, result.Count);
-            }
-            #endregion
-
-            #endregion
-
-
-            #region #4 [string] operate
-
-            #region ##1  Contains
-            {
-                var query = GetQueryable();
-
-                var strRule = "{'field':'name',  'operator': 'Contains',  'value': '987' }".Replace("'", "\"");
-                var rule = Json.Deserialize<FilterRule>(strRule);
-                var result = query.Where(rule).ToList();
-
-                Assert.AreEqual(1, result.Count);
-                Assert.AreEqual(987, result.First().id);
-            }
-            #endregion
-
-            #region ##2  NotContains
-            {
-                //###1
-                {
-                    var query = GetQueryable();
-
-                    var strRule = "{'field':'name',  'operator': 'NotContains',  'value': '987' }".Replace("'", "\"");
-                    var rule = Json.Deserialize<FilterRule>(strRule);
-                    var result = query.Where(rule).ToList();
-                    Assert.AreEqual(999, result.Count);
-                }
-
-
-                //###2
-                {
-                    var query = GetQueryable();
-                    query.Skip(987).FirstOrDefault().name = null;
-
-                    var strRule = "{'field':'name',  'operator': 'NotContains',  'value': '987' }".Replace("'", "\"");
-                    var rule = Json.Deserialize<FilterRule>(strRule);
-                    var result = query.Where(rule).ToList();
-                    Assert.AreEqual(1000, result.Count);
-                }
-
-                //###3
-                {
-                    var query = GetQueryable();
-                    query.Skip(987).FirstOrDefault().name = "";
-
-                    var strRule = "{'field':'name',  'operator': 'NotContains',  'value': '987' }".Replace("'", "\"");
-                    var rule = Json.Deserialize<FilterRule>(strRule);
-                    var result = query.Where(rule).ToList();
-                    Assert.AreEqual(1000, result.Count);
-                }
-            }
-            #endregion
-
-            #region ##3  StartsWith
-            {
-                var query = GetQueryable();
-
-                var strRule = "{'field':'name',  'operator': 'StartsWith',  'value': 'name98' }".Replace("'", "\"");
-                var rule = Json.Deserialize<FilterRule>(strRule);
-                var result = query.Where(rule).ToList();
-
-                Assert.AreEqual(11, result.Count);
-            }
-            #endregion
-
-            #region ##4  EndsWith
-            {
-                var query = GetQueryable();
-
-                var strRule = "{'field':'name',  'operator': 'EndsWith',  'value': '987' }".Replace("'", "\"");
-                var rule = Json.Deserialize<FilterRule>(strRule);
-                var result = query.Where(rule).ToList();
-
-                Assert.AreEqual(1, result.Count);
-            }
-            #endregion
-
-            #region ##5 IsNullOrEmpty
-            {
-                //###1
-                {
-                    var query = GetQueryable();
-                    query.Skip(10).FirstOrDefault().name = null;
-
-                    var strRule = "{'field':'name',  'operator': 'IsNullOrEmpty' }".Replace("'", "\"");
-                    var rule = Json.Deserialize<FilterRule>(strRule);
-                    var result = query.Where(rule).ToList();
-
-                    Assert.AreEqual(1, result.Count);
-
-                }
-
-                //###2
-                {
-                    var query = GetQueryable();
-                    query.Skip(10).FirstOrDefault().name = "";
-
-                    var strRule = "{'field':'name',  'operator': 'IsNullOrEmpty' }".Replace("'", "\"");
-                    var rule = Json.Deserialize<FilterRule>(strRule);
-                    var result = query.Where(rule).ToList();
-
-                    Assert.AreEqual(1, result.Count);
-                }
-            }
-            #endregion
-
-            #region ##6  IsNotNullOrEmpty
-            {
-                //###1
-                {
-                    var query = GetQueryable();
-                    query.Skip(10).FirstOrDefault().name = null;
-
-                    var strRule = "{'field':'name',  'operator': 'IsNotNullOrEmpty' }".Replace("'", "\"");
-                    var rule = Json.Deserialize<FilterRule>(strRule);
-                    var result = query.Where(rule).ToList();
-
-                    Assert.AreEqual(999, result.Count);
-                }
-                //###2
-                {
-                    var query = GetQueryable();
-                    query.Skip(10).FirstOrDefault().name = "";
-
-                    var strRule = "{'field':'name',  'operator': 'IsNotNullOrEmpty' }".Replace("'", "\"");
-                    var rule = Json.Deserialize<FilterRule>(strRule);
-                    var result = query.Where(rule).ToList();
-
-                    Assert.AreEqual(999, result.Count);
-                }
-            }
-            #endregion
-
-
-            #endregion
-
-
-            #region #5  nested field
-            {
-                var query = GetQueryable();
-                var strRule = "{'field':'b1.name',  'operator': '=',  'value': 'name987_b1' }".Replace("'", "\"");
-                var rule = Json.Deserialize<FilterRule>(strRule);
-
-                var result = query.Where(rule).ToList();
-
-                Assert.AreEqual(1, result.Count);
-                Assert.AreEqual(987, result[0].id);
-            }
-            #endregion
-
-        }
-
-        #endregion
-
-    }
-}
-
-
-
-
-namespace Vit.Linq.MsTest.Extensions
-{
-    static class IQueryableExtensions
-    {
-        public static IQueryable Where(this IQueryable source, IFilterRule rule) => source.IQueryable_Where(rule);
-
-        public static IQueryable Skip(this IQueryable source, int count) => source.IQueryable_Skip(count);
-
-        public static ModelA FirstOrDefault(this IQueryable source) => source.IQueryable_FirstOrDefault<ModelA>();
-        public static List<ModelA> ToList(this IQueryable source) => source.IQueryable_ToList<ModelA>();
-    }
-}

+ 31 - 0
src/Test/Vit.Linq.MsTest/QueryBuilder/OperatorMap_Test.cs

@@ -0,0 +1,31 @@
+using System.Linq;
+
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using Vit.Core.Module.Serialization;
+using Vit.Linq.QueryBuilder;
+using Vit.Extensions.Linq_Extensions;
+
+namespace Vit.Linq.MsTest.QueryBuilder
+{
+
+    [TestClass]
+    public class OperatorMap_Test
+    {
+        [TestMethod]
+        public void Test_OperatorMap()
+        {
+            {
+                var service = new QueryBuilderService();
+                var query = DataSource.GetQueryable();
+                service.AddOperatorMap("Equal", FilterRuleOperator.Equal);
+
+
+                var strRule = "{'field':'isEven',  'operator': 'eQual',  'value':true }".Replace("'", "\"");
+                var rule = Json.Deserialize<FilterRule>(strRule);
+                var result = query.Where(rule, service).ToList();
+                Assert.AreEqual(result.Count, 500);
+                Assert.AreEqual(0, result[0].id);
+            }
+        }
+    }
+}

+ 51 - 0
src/Test/Vit.Linq.MsTest/QueryBuilder/QueryableTest/Filter_Test.cs

@@ -0,0 +1,51 @@
+using Vit.Extensions.Linq_Extensions;
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using System.Collections.Generic;
+using System.Linq;
+using Vit.Core.Module.Serialization;
+using Vit.Linq.QueryBuilder;
+using System;
+using Newtonsoft.Json.Linq;
+using Queryable = System.Linq.IQueryable<Vit.Linq.MsTest.ModelA>;
+
+namespace Vit.Linq.MsTest.QueryBuilder.QueryableTest
+{
+    [TestClass]
+    public class Filter_Test : Filter_TestBase<Queryable>
+    {
+
+        [TestMethod]
+        public void Test_FilterRule_()
+        {
+            base.TestFilterRule();
+        }
+
+
+        public override IFilterRule GetRule(string filterRule)
+        {
+            return Json.Deserialize<FilterRule>(filterRule);
+        }
+
+        public override Queryable ToQuery(IQueryable<ModelA> query) => query;
+
+        public override List<ModelA> Filter(Queryable query, IFilterRule rule)
+        {
+            return query.Where(rule, GetService()).ToList<ModelA>();
+        }
+
+        public virtual QueryBuilderService GetService()
+        {
+            QueryBuilderService service = new QueryBuilderService();
+            service.GetRuleValue = (object value, IFilterRule rule, Type fieldType) =>
+            {
+                // to deal with null value
+                if (value is JValue jv) return jv.Value;
+                return value;
+            };
+            return service;
+        }
+
+    }
+}
+
+

+ 31 - 0
src/Test/Vit.Linq.MsTest/QueryBuilder/QueryableTest/Filter_Test_Newtonsoft.cs

@@ -0,0 +1,31 @@
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+
+using Vit.Core.Module.Serialization;
+using Vit.Linq.QueryBuilder;
+using Vit.Linq.QueryBuilder.NewtonsoftJson;
+
+namespace Vit.Linq.MsTest.QueryBuilder.QueryableTest
+{
+    [TestClass]
+    public class Filter_Test_Newtonsoft : Filter_Test
+    {
+
+        public override IFilterRule GetRule(string filterRule)
+        {
+            return Json.Deserialize<FilterRule_Newtonsoft>(filterRule);
+        }
+
+        public override QueryBuilderService GetService()
+        {
+            QueryBuilderService service = new QueryBuilderService();
+            return service;
+        }
+
+
+        [TestMethod]
+        public void Test_FilterRule()
+        {
+            base.TestFilterRule();
+        }
+    }
+}

+ 29 - 0
src/Test/Vit.Linq.MsTest/QueryBuilder/QueryableTest/Filter_Test_Newtonsoft2.cs

@@ -0,0 +1,29 @@
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+
+using Vit.Linq.QueryBuilder;
+using Vit.Linq.QueryBuilder.NewtonsoftJson;
+
+namespace Vit.Linq.MsTest.QueryBuilder.QueryableTest
+{
+    [TestClass]
+    public class Filter_Test_Newtonsoft2 : Filter_Test
+    {
+
+        public override IFilterRule GetRule(string filterRule)
+        {
+            return FilterRule_Newtonsoft.FromString(filterRule);
+        }
+        public override QueryBuilderService GetService()
+        {
+            QueryBuilderService service = new QueryBuilderService();
+            return service;
+        }
+
+        [TestMethod]
+        public void Test_FilterRule()
+        {
+            base.TestFilterRule();
+        }
+
+    }
+}

+ 32 - 0
src/Test/Vit.Linq.MsTest/QueryBuilder/QueryableTest/Filter_Test_SystemTextJson.cs

@@ -0,0 +1,32 @@
+using System.Text.Json;
+
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+
+using Vit.Linq.QueryBuilder;
+using Vit.Linq.QueryBuilder.SystemTextJson;
+
+namespace Vit.Linq.MsTest.QueryBuilder.QueryableTest
+{
+    [TestClass]
+    public class Filter_Test_SystemTextJson : Filter_Test
+    {
+
+        public override IFilterRule GetRule(string filterRule)
+        {
+            return JsonSerializer.Deserialize<FilterRule_SystemTextJson>(filterRule);
+        }
+
+        public override QueryBuilderService GetService()
+        {
+            QueryBuilderService service = new QueryBuilderService();
+            return service;
+        }
+
+
+        [TestMethod]
+        public void Test_FilterRule()
+        {
+            base.TestFilterRule();
+        }
+    }
+}

+ 29 - 0
src/Test/Vit.Linq.MsTest/QueryBuilder/QueryableTest/Filter_Test_SystemTextJson2.cs

@@ -0,0 +1,29 @@
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+
+using Vit.Linq.QueryBuilder;
+using Vit.Linq.QueryBuilder.SystemTextJson;
+
+namespace Vit.Linq.MsTest.QueryBuilder.QueryableTest
+{
+    [TestClass]
+    public class Filter_Test_SystemTextJson2 : Filter_Test
+    {
+
+        public override IFilterRule GetRule(string filterRule)
+        {
+            return FilterRule_SystemTextJson.FromString(filterRule);
+        }
+        public override QueryBuilderService GetService()
+        {
+            QueryBuilderService service = new QueryBuilderService();
+            return service;
+        }
+
+        [TestMethod]
+        public void Test_FilterRule()
+        {
+            base.TestFilterRule();
+        }
+
+    }
+}

+ 19 - 13
src/Test/Vit.Linq.MsTest/Vit.Linq.MsTest.csproj

@@ -1,19 +1,25 @@
-<Project Sdk="Microsoft.NET.Sdk">
+<Project Sdk="Microsoft.NET.Sdk">
 
-  <PropertyGroup>
-    <TargetFramework>netcoreapp2.1</TargetFramework>
+    <PropertyGroup>
+        <TargetFramework>net6.0</TargetFramework>
+        <ImplicitUsings>enable</ImplicitUsings>
+        <Nullable>enable</Nullable>
 
-    <IsPackable>false</IsPackable>
-  </PropertyGroup>
+        <IsPackable>false</IsPackable>
+        <IsTestProject>true</IsTestProject>
+    </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>
+        <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.5.0" />
+        <PackageReference Include="MSTest.TestAdapter" Version="2.2.10" />
+        <PackageReference Include="MSTest.TestFramework" Version="2.2.10" />
+        <PackageReference Include="coverlet.collector" Version="3.2.0" />
+    </ItemGroup>
 
-  <ItemGroup>
-    <ProjectReference Include="..\..\Vit.Linq\Vit.Linq.csproj" />
-  </ItemGroup>
+    <ItemGroup>
+        <ProjectReference Include="..\..\Vit.Linq.QueryBuilder.NewtonsoftJson\Vit.Linq.QueryBuilder.NewtonsoftJson.csproj" />
+        <ProjectReference Include="..\..\Vit.Linq.QueryBuilder.SystemTextJson\Vit.Linq.QueryBuilder.SystemTextJson.csproj" />
+        <ProjectReference Include="..\..\Vit.Linq\Vit.Linq.csproj" />
+    </ItemGroup>
 
 </Project>

+ 28 - 0
src/Test/Vit.Linq.MsTest31/Vit.Linq.MsTest31.csproj

@@ -0,0 +1,28 @@
+<Project Sdk="Microsoft.NET.Sdk">
+
+    <PropertyGroup>
+        <TargetFramework>netcoreapp3.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.QueryBuilder.NewtonsoftJson\Vit.Linq.QueryBuilder.NewtonsoftJson.csproj" />
+        <ProjectReference Include="..\..\Vit.Linq.QueryBuilder.SystemTextJson\Vit.Linq.QueryBuilder.SystemTextJson.csproj" />
+        <ProjectReference Include="..\..\Vit.Linq\Vit.Linq.csproj" />
+    </ItemGroup>
+
+    <ItemGroup>
+        <Compile Include="..\Vit.Linq.MsTest\Extensions\*.cs" Link="Extensions\%(RecursiveDir)%(FileName)%(Extension)" />
+        <Compile Include="..\Vit.Linq.MsTest\QueryBuilder\**\*.cs" Link="QueryBuilder\%(RecursiveDir)%(FileName)%(Extension)" />
+        <Compile Include="..\Vit.Linq.MsTest\DataSource.cs" />
+    </ItemGroup>
+
+
+</Project>

+ 104 - 0
src/Vit.Linq.QueryBuilder.NewtonsoftJson/FilterRule_Newtonsoft.cs

@@ -0,0 +1,104 @@
+using System;
+using System.Collections.Generic;
+using System.Diagnostics.CodeAnalysis;
+
+using Newtonsoft.Json;
+using Newtonsoft.Json.Linq;
+
+namespace Vit.Linq.QueryBuilder.NewtonsoftJson
+{
+    /// <summary>
+    /// This class is used to define a hierarchical filter for a given collection. This type can be serialized/deserialized by JSON.NET without needing to modify the data structure from QueryBuilder.
+    /// </summary>
+    [ExcludeFromCodeCoverage]
+    public class FilterRule_Newtonsoft : IFilterRule
+    {
+        /// <summary>
+        /// condition - acceptable values are "and" and "or".
+        /// </summary>
+        public string condition { get; set; }
+
+
+        public string field { get; set; }
+
+
+        public string @operator { get; set; }
+
+        /// <summary>
+        ///  nested filter rules.
+        /// </summary>
+        public List<FilterRule_Newtonsoft> rules { get; set; }
+
+
+        /// <summary>
+        /// Gets or sets the value of the filter.
+        /// </summary>
+        /// <value>
+        /// The value.
+        /// </value>
+        public object value
+        {
+            get
+            {
+                if (_value is JToken jt)
+                {
+                    return GetPrimitiveValue(jt);
+                }
+                return _value;
+            }
+            set => _value = value;
+        }
+
+        private object _value;
+
+        IEnumerable<IFilterRule> IFilterRule.rules => rules;
+
+
+        public static FilterRule_Newtonsoft FromString(string filter)
+        {
+            return JsonConvert.DeserializeObject<FilterRule_Newtonsoft>(filter, serializeSetting);
+        }
+
+        static readonly global::Newtonsoft.Json.JsonSerializerSettings serializeSetting = new global::Newtonsoft.Json.JsonSerializerSettings() { Converters = { new ValueConverter() } };
+
+
+        public static object GetPrimitiveValue(JToken value)
+        {
+            if (value is JValue jv)
+            {
+                return jv.Value;
+            }
+
+            if (value is JArray ja)
+            {
+                List<object> values = new List<object>();
+                foreach (JValue item in ja)
+                {
+                    values.Add(item.Value);
+                }
+                return values;
+            }
+            return value;
+        }
+
+
+        class ValueConverter : JsonConverter
+        {
+            public override bool CanConvert(Type objectType)
+            {
+                return objectType == typeof(object);
+            }
+
+            public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
+            {
+                JToken token = JToken.Load(reader);
+                return GetPrimitiveValue(token);
+            }
+
+            public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
+            {
+                throw new NotImplementedException();
+            }
+        }
+    }
+}

+ 25 - 0
src/Vit.Linq.QueryBuilder.NewtonsoftJson/Vit.Linq.QueryBuilder.NewtonsoftJson.csproj

@@ -0,0 +1,25 @@
+<Project Sdk="Microsoft.NET.Sdk">
+
+    <PropertyGroup>
+        <pack>nuget</pack>
+    </PropertyGroup>
+
+    <PropertyGroup>
+        <TargetFramework>netstandard2.0</TargetFramework>
+        <Version>2.2.15-temp</Version>
+    </PropertyGroup>
+
+    <PropertyGroup>
+        <Authors>Lith</Authors>
+        <Description>Linq extensions (QueryBuilder)</Description>
+        <PackageProjectUrl>https://github.com/serset/Vit.Linq</PackageProjectUrl>
+    </PropertyGroup>
+
+    <ItemGroup>
+        <ProjectReference Include="..\Vit.Linq.QueryBuilder\Vit.Linq.QueryBuilder.csproj" />
+    </ItemGroup>
+
+    <ItemGroup>
+        <PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
+    </ItemGroup>
+</Project>

+ 100 - 0
src/Vit.Linq.QueryBuilder.SystemTextJson/FilterRule_SystemTextJson.cs

@@ -0,0 +1,100 @@
+using System;
+using System.Collections.Generic;
+using System.Diagnostics.CodeAnalysis;
+using System.Linq;
+using System.Text.Json;
+using System.Text.Unicode;
+
+namespace Vit.Linq.QueryBuilder.SystemTextJson
+{
+    /// <summary>
+    /// This class is used to define a hierarchical filter for a given collection. This type can be serialized/deserialized by JSON.NET without needing to modify the data structure from QueryBuilder.
+    /// </summary>
+    [ExcludeFromCodeCoverage]
+    public class FilterRule_SystemTextJson : IFilterRule
+    {
+        /// <summary>
+        /// condition - acceptable values are "and" and "or".
+        /// </summary>
+        public string condition { get; set; }
+
+
+        public string field { get; set; }
+
+
+        public string @operator { get; set; }
+
+        /// <summary>
+        ///  nested filter rules.
+        /// </summary>
+        public List<FilterRule_SystemTextJson> rules { get; set; }
+
+
+        /// <summary>
+        /// Gets or sets the value of the filter.
+        /// </summary>
+        /// <value>
+        /// The value.
+        /// </value>
+        public object value
+        {
+            get
+            {
+                if (_value is JsonElement je)
+                {
+                    return GetPrimitiveValue(je);
+                }
+                return _value;
+            }
+            set => _value = value;
+        }
+
+        private object _value;
+
+        IEnumerable<IFilterRule> IFilterRule.rules => rules;
+
+
+        public static FilterRule_SystemTextJson FromString(string filter)
+        {
+            return JsonSerializer.Deserialize<FilterRule_SystemTextJson>(filter, options); 
+        }
+
+        static readonly JsonSerializerOptions options = GetDefaultOptions();
+
+
+        public static JsonSerializerOptions GetDefaultOptions(  )
+        {
+          var options=  new JsonSerializerOptions
+            {
+                // avoid transfer chinese character, for example {"title":"\u4ee3\u7801\u6539\u53d8\u4e16\u754c"}
+                Encoder = System.Text.Encodings.Web.JavaScriptEncoder.Create(UnicodeRanges.All),
+                IncludeFields = true,
+                DefaultIgnoreCondition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingNull
+            }; 
+
+            return options;
+        }
+
+
+
+        public static object GetPrimitiveValue(JsonElement value)
+        {
+            switch (value.ValueKind)
+            {
+                case JsonValueKind.Null: return null;
+                case JsonValueKind.Undefined: return null;
+                case JsonValueKind.True: return true;
+                case JsonValueKind.False: return false;
+                case JsonValueKind.Number: return value.GetDecimal();
+                case JsonValueKind.String: return value.GetString();
+                case JsonValueKind.Array:
+                    return value.EnumerateArray().Select(GetPrimitiveValue).ToList();
+            }
+
+            return value;
+        }
+
+
+        
+    }
+}

+ 27 - 0
src/Vit.Linq.QueryBuilder.SystemTextJson/Vit.Linq.QueryBuilder.SystemTextJson.csproj

@@ -0,0 +1,27 @@
+<Project Sdk="Microsoft.NET.Sdk">
+
+    <PropertyGroup>
+        <pack>nuget</pack>
+    </PropertyGroup>
+
+    <PropertyGroup>
+        <TargetFramework>netstandard2.0</TargetFramework>
+        <Version>2.2.15-temp</Version>
+    </PropertyGroup>
+
+    <PropertyGroup>
+        <Authors>Lith</Authors>
+        <Description>Linq extensions (QueryBuilder)</Description>
+        <PackageProjectUrl>https://github.com/serset/Vit.Linq</PackageProjectUrl>
+    </PropertyGroup>
+
+    <ItemGroup>
+        <ProjectReference Include="..\Vit.Linq.QueryBuilder\Vit.Linq.QueryBuilder.csproj" />
+    </ItemGroup>
+
+    <ItemGroup>
+        <PackageReference Include="System.Text.Json" Version="6.0.0" />
+    </ItemGroup>
+
+
+</Project>

+ 2 - 2
src/Vit.Linq.QueryBuilder/Extensions/IQueryable_Where_Extensions.cs

@@ -12,9 +12,9 @@ namespace Vit.Extensions.Linq_Extensions
         #region Where
 
         [MethodImpl(MethodImplOptions.AggressiveInlining)]
-        public static IQueryable IQueryable_Where(this IQueryable query, IFilterRule rule)
+        public static IQueryable IQueryable_Where(this IQueryable query, IFilterRule rule, QueryBuilderService service = null)
         {
-            LambdaExpression lambda = QueryBuilderService.Instance.ToLambdaExpression(rule, query.ElementType);
+            LambdaExpression lambda = (service ?? QueryBuilderService.Instance).ToLambdaExpression(rule, query.ElementType);
             if (lambda == null) return query;
             return query.Provider.CreateQuery(
                 Expression.Call(

+ 3 - 4
src/Vit.Linq.QueryBuilder/Extensions/Queryable_Where_Extensions.cs

@@ -8,14 +8,13 @@ namespace Vit.Extensions.Linq_Extensions
     public static partial class Queryable_Where_Extensions
     {
         #region Where
-
         [MethodImpl(MethodImplOptions.AggressiveInlining)]
-        public static IQueryable<T> Where<T>(this IQueryable<T> query, IFilterRule rule)
-          where T : class
+        public static IQueryable<T> Where<T>(this IQueryable<T> query, IFilterRule rule, QueryBuilderService service = null)
+        where T : class
         {
             if (query == null || rule == null) return query;
 
-            var predicate = QueryBuilderService.Instance.ToExpression<T>(rule);
+            var predicate = (service ?? QueryBuilderService.Instance).ToExpression<T>(rule);
             if (predicate == null)
             {
                 return query;

+ 6 - 6
src/Vit.Linq.QueryBuilder/IFilterRule.cs

@@ -10,11 +10,11 @@ namespace Vit.Linq.QueryBuilder
         /// <summary>
         ///  condition - acceptable values are "and" and "or".
         /// </summary>
-        string condition { get; }
+        string condition { get; set; }
         /// <summary>
         /// could be nested, example: b1.name
         /// </summary>
-        string field { get; }
+        string field { get; set; }
 
 
         /// <summary>
@@ -27,18 +27,18 @@ namespace Vit.Linq.QueryBuilder
         ///     
         ///    //TODO [array]   "is empty", "is not empty"
         /// </summary>
-        string @operator { get; }
+        string @operator { get; set; }
 
         /// <summary>
         /// nested filter rules
         /// </summary>
         IEnumerable<IFilterRule> rules { get; }
- 
- 
+
+
 
         /// <summary>
         /// value of the filter. Supported value types are "integer", "double", "string", "date", "datetime", and "boolean".
         /// </summary>
-        object value { get; }
+        object value { get; set; }
     }
 }

+ 28 - 12
src/Vit.Linq.QueryBuilder/QueryBuilderService.cs

@@ -80,6 +80,14 @@ namespace Vit.Linq.QueryBuilder
             return operate;
         }
 
+        public Func<object, IFilterRule, Type, object> GetRuleValue;
+        protected virtual object GetRulePrimitiveValue(object value, IFilterRule rule, Type fieldType)
+        {
+            if (GetRuleValue != null) return GetRuleValue(value, rule, fieldType);
+            return value;
+        }
+
+
         Expression ConvertToExpression(IFilterRule rule, ParameterExpression parameter)
         {
             if (rule == null) return null;
@@ -211,7 +219,7 @@ namespace Vit.Linq.QueryBuilder
             #region Method ConvertValueExp
             UnaryExpression ConvertValueExp()
             {
-                object value = rule.value;
+                object value = GetRulePrimitiveValue(rule.value,rule,fieldType);
                 if (value != null)
                 {
                     Type valueType = Nullable.GetUnderlyingType(fieldType) ?? fieldType;
@@ -244,11 +252,8 @@ namespace Vit.Linq.QueryBuilder
                     object value = null;
                     if (rule.value != null)
                     {
-                        //value = Vit.Core.Module.Serialization.Json.Deserialize(Vit.Core.Module.Serialization.Json.Serialize(rule.value), valueType);
-                        if (rule.value is IEnumerable arr)
-                        {
-                            value = ConvertToList(arr, fieldType);
-                        }
+                        //value = Vit.Core.Module.Serialization.Json.Deserialize(Vit.Core.Module.Serialization.Json.Serialize(rule.value), valueType);                        
+                        value = ConvertToList(rule.value, rule, fieldType);
                     }
                     Expression<Func<object>> valueLamba = () => value;
                     arrayExp = Expression.Convert(valueLamba.Body, valueType);
@@ -299,18 +304,29 @@ namespace Vit.Linq.QueryBuilder
         }
 
         #region ConvertToList
-        internal object ConvertToList(IEnumerable values, Type fieldType)
+        internal object ConvertToList(object value,IFilterRule rule, Type fieldType)
         {
-            var methodInfo = typeof(QueryBuilderService).GetMethod("ConvertToListByType", System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic).MakeGenericMethod(fieldType);
-            return methodInfo.Invoke(this, new object[] { values });
+            if (value is string str)
+            {
+                var itemValue = GetRulePrimitiveValue(str, rule, fieldType);
+                if (itemValue is IEnumerable)
+                    return itemValue;
+            }
+            else if (value is IEnumerable values)
+            {
+                var methodInfo = typeof(QueryBuilderService).GetMethod("ConvertToListByType", System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic).MakeGenericMethod(fieldType);
+                return methodInfo.Invoke(this, new object[] { values , rule });
+            }
+            return null;
         }
-        internal List<T> ConvertToListByType<T>(IEnumerable values)
+        internal List<T> ConvertToListByType<T>(IEnumerable values, IFilterRule rule)
         {
-            Type valueType = typeof(T);
+            Type fieldType = typeof(T);
             var list = new List<T>();
             foreach (var item in values)
             {
-                var value = (T)Convert.ChangeType(item, valueType);
+                var itemValue = GetRulePrimitiveValue(item, rule, fieldType);
+                T value = (T)Convert.ChangeType(itemValue, fieldType);
                 list.Add(value);
             }
             return list;

+ 1 - 1
src/Vit.Linq.QueryBuilder/Vit.Linq.QueryBuilder.csproj

@@ -12,7 +12,7 @@
     <PropertyGroup>
         <Authors>Lith</Authors>
         <Description>Linq extensions (QueryBuilder)</Description>
-        <PackageProjectUrl>https://github.com/serset/Vit.Library</PackageProjectUrl>
+        <PackageProjectUrl>https://github.com/serset/Vit.Linq</PackageProjectUrl>
     </PropertyGroup>
 
     <PropertyGroup>

+ 19 - 0
src/Vit.Linq.sln

@@ -11,6 +11,12 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Vit.Linq", "Vit.Linq\Vit.Li
 EndProject
 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Vit.Linq.QueryBuilder", "Vit.Linq.QueryBuilder\Vit.Linq.QueryBuilder.csproj", "{FB5B2005-AF15-423D-81E5-545F111C9848}"
 EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Vit.Linq.QueryBuilder.SystemTextJson", "Vit.Linq.QueryBuilder.SystemTextJson\Vit.Linq.QueryBuilder.SystemTextJson.csproj", "{4A09A6CE-F8E7-4609-8628-50DA53CAD650}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Vit.Linq.QueryBuilder.NewtonsoftJson", "Vit.Linq.QueryBuilder.NewtonsoftJson\Vit.Linq.QueryBuilder.NewtonsoftJson.csproj", "{3F182219-4F27-4FD2-A0C6-A4C6130C96E2}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Vit.Linq.MsTest31", "Test\Vit.Linq.MsTest31\Vit.Linq.MsTest31.csproj", "{7E513E2C-BF52-4662-AD8F-5910F283178D}"
+EndProject
 Global
 	GlobalSection(SolutionConfigurationPlatforms) = preSolution
 		Debug|Any CPU = Debug|Any CPU
@@ -29,12 +35,25 @@ Global
 		{FB5B2005-AF15-423D-81E5-545F111C9848}.Debug|Any CPU.Build.0 = Debug|Any CPU
 		{FB5B2005-AF15-423D-81E5-545F111C9848}.Release|Any CPU.ActiveCfg = Release|Any CPU
 		{FB5B2005-AF15-423D-81E5-545F111C9848}.Release|Any CPU.Build.0 = Release|Any CPU
+		{4A09A6CE-F8E7-4609-8628-50DA53CAD650}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{4A09A6CE-F8E7-4609-8628-50DA53CAD650}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{4A09A6CE-F8E7-4609-8628-50DA53CAD650}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{4A09A6CE-F8E7-4609-8628-50DA53CAD650}.Release|Any CPU.Build.0 = Release|Any CPU
+		{3F182219-4F27-4FD2-A0C6-A4C6130C96E2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{3F182219-4F27-4FD2-A0C6-A4C6130C96E2}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{3F182219-4F27-4FD2-A0C6-A4C6130C96E2}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{3F182219-4F27-4FD2-A0C6-A4C6130C96E2}.Release|Any CPU.Build.0 = Release|Any CPU
+		{7E513E2C-BF52-4662-AD8F-5910F283178D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{7E513E2C-BF52-4662-AD8F-5910F283178D}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{7E513E2C-BF52-4662-AD8F-5910F283178D}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{7E513E2C-BF52-4662-AD8F-5910F283178D}.Release|Any CPU.Build.0 = Release|Any CPU
 	EndGlobalSection
 	GlobalSection(SolutionProperties) = preSolution
 		HideSolutionNode = FALSE
 	EndGlobalSection
 	GlobalSection(NestedProjects) = preSolution
 		{5801E323-C03D-48D2-BEFF-DE060B3293B0} = {0062F400-558C-4084-8004-3C8D4CBBFDE4}
+		{7E513E2C-BF52-4662-AD8F-5910F283178D} = {0062F400-558C-4084-8004-3C8D4CBBFDE4}
 	EndGlobalSection
 	GlobalSection(ExtensibilityGlobals) = postSolution
 		SolutionGuid = {C7DA16E3-9949-49FA-B0B4-F830636DE60F}

+ 1 - 1
src/Vit.Linq/Extensions/IQueryable_PageExtensions.cs → src/Vit.Linq/Extensions/IQueryable_Page_Extensions.cs

@@ -6,7 +6,7 @@ using Vit.Core.Util.ComponentModel.Data;
 namespace Vit.Extensions.Linq_Extensions
 {
 
-    public static partial class IQueryable_PageExtensions
+    public static partial class IQueryable_Page_Extensions
     {
 
 

+ 1 - 1
src/Vit.Linq/Extensions/IQueryable_SortExtensions.cs → src/Vit.Linq/Extensions/IQueryable_Sort_Extensions.cs

@@ -9,7 +9,7 @@ using Vit.Linq.QueryBuilder;
 namespace Vit.Extensions.Linq_Extensions
 {
 
-    public static partial class IQueryable_SortExtensions
+    public static partial class IQueryable_Sort_Extensions
     {
 
         #region Sort

+ 1 - 1
src/Vit.Linq/Extensions/IQueryable_ToPageDataExtensions.cs → src/Vit.Linq/Extensions/IQueryable_ToPageData_Extensions.cs

@@ -9,7 +9,7 @@ using Vit.Core.Util.ComponentModel.Query;
 namespace Vit.Extensions.Linq_Extensions
 {
 
-    public static partial class IQueryable_ToPageDataExtensions
+    public static partial class IQueryable_ToPageData_Extensions
     {
         #region IQueryable_ToPageData       
         [MethodImpl(MethodImplOptions.AggressiveInlining)]

+ 1 - 1
src/Vit.Linq/Extensions/Queryable_PageExtensions.cs → src/Vit.Linq/Extensions/Queryable_Page_Extensions.cs

@@ -5,7 +5,7 @@ using Vit.Core.Util.ComponentModel.Data;
 namespace Vit.Extensions.Linq_Extensions
 {
 
-    public static partial class Queryable_PageExtensions
+    public static partial class Queryable_Page_Extensions
     {
 
 

+ 1 - 1
src/Vit.Linq/Extensions/Queryable_SortExtensions.cs → src/Vit.Linq/Extensions/Queryable_Sort_Extensions.cs

@@ -12,7 +12,7 @@ namespace Vit.Extensions.Linq_Extensions
     /// <summary>
     /// ref https://www.cnblogs.com/Extnet/p/9848272.html
     /// </summary>
-    public static partial class Queryable_SortExtensions
+    public static partial class Queryable_Sort_Extensions
     {
 
         [MethodImpl(MethodImplOptions.AggressiveInlining)]

+ 1 - 1
src/Vit.Linq/Extensions/Queryable_ToPageDataExtensions.cs → src/Vit.Linq/Extensions/Queryable_ToPageData_Extensions.cs

@@ -10,7 +10,7 @@ using Vit.Core.Util.ComponentModel.Query;
 namespace Vit.Extensions.Linq_Extensions
 {
 
-    public static partial class Queryable_ToPageDataExtensions
+    public static partial class Queryable_ToPageData_Extensions
     {
 
         #region ToPageData       

+ 1 - 1
src/Vit.Linq/Vit.Linq.csproj

@@ -12,7 +12,7 @@
     <PropertyGroup>
         <Authors>Lith</Authors>
         <Description>Linq extensions</Description>
-        <PackageProjectUrl>https://github.com/serset/Vit.Library</PackageProjectUrl>
+        <PackageProjectUrl>https://github.com/serset/Vit.Linq</PackageProjectUrl>
     </PropertyGroup>
 
     <PropertyGroup>