Browse Source

Merge branch 'feature/gogs14_2024-01-19_SupportMethodLikeCount' of serset/Vit.Linq into develop

lith 1 year ago
parent
commit
33c4dc61cb
37 changed files with 1103 additions and 335 deletions
  1. 8 0
      readme.md
  2. 29 1
      src/Test/Vit.Linq.MsTest/DataSource.cs
  3. 2 2
      src/Test/Vit.Linq.MsTest/Filter/CustomeValue_Test.cs
  4. 112 1
      src/Test/Vit.Linq.MsTest/Filter/Filter_TestBase.cs
  5. 4 4
      src/Test/Vit.Linq.MsTest/Filter/IQueryableTest/Filter_Test_FilterRule.cs
  6. 39 0
      src/Test/Vit.Linq.MsTest/Filter/IQueryableTest/Filter_Test_FilterRuleWithMethod.cs
  7. 11 10
      src/Test/Vit.Linq.MsTest/Filter/IQueryableTest/Filter_Test_Newtonsoft.cs
  8. 11 10
      src/Test/Vit.Linq.MsTest/Filter/IQueryableTest/Filter_Test_Newtonsoft2.cs
  9. 11 10
      src/Test/Vit.Linq.MsTest/Filter/IQueryableTest/Filter_Test_SystemTextJson.cs
  10. 12 12
      src/Test/Vit.Linq.MsTest/Filter/IQueryableTest/Filter_Test_SystemTextJson2.cs
  11. 2 2
      src/Test/Vit.Linq.MsTest/Filter/OperatorMap_Test.cs
  12. 4 4
      src/Test/Vit.Linq.MsTest/Filter/QueryableTest/Filter_Test_FilterRule.cs
  13. 39 0
      src/Test/Vit.Linq.MsTest/Filter/QueryableTest/Filter_Test_FilterRuleWithMethod.cs
  14. 11 10
      src/Test/Vit.Linq.MsTest/Filter/QueryableTest/Filter_Test_Newtonsoft.cs
  15. 11 10
      src/Test/Vit.Linq.MsTest/Filter/QueryableTest/Filter_Test_Newtonsoft2.cs
  16. 11 10
      src/Test/Vit.Linq.MsTest/Filter/QueryableTest/Filter_Test_SystemTextJson.cs
  17. 12 12
      src/Test/Vit.Linq.MsTest/Filter/QueryableTest/Filter_Test_SystemTextJson2.cs
  18. 1 1
      src/Test/Vit.Linq.MsTest31/Vit.Linq.MsTest31.csproj
  19. 2 2
      src/Vit.Linq.Extensions/Extensions/IQueryable_Sort_Extensions.cs
  20. 2 3
      src/Vit.Linq.Extensions/Extensions/Queryable_Sort_ByReflection_Extensions.cs
  21. 4 3
      src/Vit.Linq.Extensions/Extensions/Queryable_Sort_Extensions.cs
  22. 10 36
      src/Vit.Linq.NewtonsoftJson/FilterRule_Newtonsoft.cs
  23. 56 46
      src/Vit.Linq.SystemTextJson/FilterRule_SystemTextJson.cs
  24. 1 1
      src/Vit.Linq/Filter/ECondition.cs
  25. 3 3
      src/Vit.Linq/Filter/Extensions/IQueryable_Where_Extensions.cs
  26. 3 3
      src/Vit.Linq/Filter/Extensions/Queryable_Where_Extensions.cs
  27. 14 0
      src/Vit.Linq/Filter/FilterRule.cs
  28. 52 0
      src/Vit.Linq/Filter/FilterRuleBase.cs
  29. 1 1
      src/Vit.Linq/Filter/FilterRuleOperator.cs
  30. 364 0
      src/Vit.Linq/Filter/FilterService.cs
  31. 6 2
      src/Vit.Linq/Filter/IFilterRule.cs
  32. 110 82
      src/Vit.Linq/Filter/QueryBuilderService.cs
  33. 1 1
      src/Vit.Linq/LinqHelp.Reflection.cs
  34. 37 11
      src/Vit.Linq/LinqHelp.cs
  35. 107 0
      src/Vit.Linq/MoreFilter/FilterRuleWithMethod.cs
  36. 0 39
      src/Vit.Linq/QueryBuilder/FilterRule.cs
  37. 0 3
      src/Vit.Linq/readme.md

+ 8 - 0
readme.md

@@ -0,0 +1,8 @@
+
+# Linq Filter Library
+Compatible with QueryBuilder
+
+example :   [Test](src/Test/Vit.Linq.MsTest/Filter/Filter_TestBase.cs)
+
+
+ref DynamicQueryable

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

@@ -1,4 +1,5 @@
 using System;
+using System.Collections;
 using System.Collections.Generic;
 using System.Linq;
 
@@ -16,19 +17,46 @@ namespace Vit.Linq.MsTest
         public ModelB b1;
 
         public ModelB[] ba;
+
+        public List<ModelB> bList;
         public ModelA BuildB()
         {
             b1 = new ModelB { name = name + "_b1", pid = pid };
 
-            ba = new[] { b1 };
+            if (id % 2 == 0)
+                ba = new[] { b1, b1 };
+            else
+                ba = new[] { b1 };
+
+            bList = ba.ToList();
+          
             return this;
         }
+
+        public int GetBCount()
+        {
+            return bList.Count;
+        }
+        public bool BExistAtIndex(int index)
+        {
+            if (index < bList.Count) return true;
+            return false;
+        }
+        public ModelB GetBAtIndex(int index)
+        {
+            if (index < bList.Count) return bList[index];
+            return null;
+        }
     }
 
     public class ModelB
     {
         public int? pid;
         public string name;
+        public string GetBName()
+        {
+            return name;
+        }
     }
 
 

+ 2 - 2
src/Test/Vit.Linq.MsTest/QueryBuilder/CustomeValue_Test.cs → src/Test/Vit.Linq.MsTest/Filter/CustomeValue_Test.cs

@@ -5,7 +5,7 @@ using System.Text;
 
 using Microsoft.VisualStudio.TestTools.UnitTesting;
 using Vit.Core.Module.Serialization;
-using Vit.Linq.QueryBuilder;
+using Vit.Linq.Filter;
 using Vit.Extensions.Linq_Extensions;
 using Newtonsoft.Json.Linq;
 
@@ -19,7 +19,7 @@ namespace Vit.Linq.MsTest.QueryBuilder
         public void Test_CustomeValue()
         {
             {
-                var service = new QueryBuilderService();
+                var service = new FilterService();
                 service.GetRuleValue = (object? value, IFilterRule rule, Type fieldType) =>
                 {
                     // to deal with null value

+ 112 - 1
src/Test/Vit.Linq.MsTest/QueryBuilder/Filter_TestBase.cs → src/Test/Vit.Linq.MsTest/Filter/Filter_TestBase.cs

@@ -5,7 +5,8 @@ using System.Collections.Generic;
 using System.Linq;
 using System.Reflection;
 
-using Vit.Linq.QueryBuilder;
+using Vit.Linq.MoreFilter;
+using Vit.Linq.Filter;
 
 namespace Vit.Linq.MsTest.QueryBuilder
 {
@@ -531,6 +532,116 @@ namespace Vit.Linq.MsTest.QueryBuilder
             }
             #endregion
 
+            #region #7 Array
+            {
+                var query = GetQueryable();
+                var strRule = "{'field':'ba[0].name',  'operator': '=',  'value': 'name987_b1' }".Replace("'", "\"");
+                var rule = GetRule(strRule);
+
+                var result = Filter(ToQuery(query), rule);
+
+                Assert.AreEqual(1, result.Count);
+                Assert.AreEqual(987, result[0].id);
+            }
+            {
+                var query = GetQueryable();
+                var strRule = "{'field':'ba.0.name',  'operator': '=',  'value': 'name987_b1' }".Replace("'", "\"");
+                var rule = GetRule(strRule);
+
+                var result = Filter(ToQuery(query), rule);
+
+                Assert.AreEqual(1, result.Count);
+                Assert.AreEqual(987, result[0].id);
+            }
+            {
+                var query = GetQueryable();
+                var strRule = "{'field':'ba.Length',  'operator': '=',  'value': 1 }".Replace("'", "\"");
+                var rule = GetRule(strRule);
+
+                var result = Filter(ToQuery(query), rule);
+
+                Assert.AreEqual(500, result.Count);
+            }
+            #endregion
+
+            #region #8 List
+            {
+                var query = GetQueryable();
+                var strRule = "{'field':'bList[0].name',  'operator': '=',  'value': 'name987_b1' }".Replace("'", "\"");
+                var rule = GetRule(strRule);
+
+                var result = Filter(ToQuery(query), rule);
+
+                Assert.AreEqual(1, result.Count);
+                Assert.AreEqual(987, result[0].id);
+            }
+            {
+                var query = GetQueryable();
+                var strRule = "{'field':'bList.0.name',  'operator': '=',  'value': 'name987_b1' }".Replace("'", "\"");
+                var rule = GetRule(strRule);
+
+                var result = Filter(ToQuery(query), rule);
+
+                Assert.AreEqual(1, result.Count);
+                Assert.AreEqual(987, result[0].id);
+            }
+            {
+                var query = GetQueryable();
+                var strRule = "{'field':'bList.Count',  'operator': '=',  'value': 1 }".Replace("'", "\"");
+                var rule = GetRule(strRule);
+
+                var result = Filter(ToQuery(query), rule);
+
+                Assert.AreEqual(500, result.Count);
+            }
+            #endregion
+
+            #region #9  method in fields
+            if (FilterRuleWithMethod.SupportFieldMethod(GetRule("{}")))
+            {
+                {
+                    var query = GetQueryable();
+                    var strRule = "{'field':'bList[0]', 'fields':[ {'method':'GetBName' }] ,  'operator': '=',  'value': 'name987_b1' }".Replace("'", "\"");
+                    var rule = GetRule(strRule);
+
+                    var result = Filter(ToQuery(query), rule);
+
+                    Assert.AreEqual(1, result.Count);
+                    Assert.AreEqual(987, result[0].id);
+                }
+                {
+                    var query = GetQueryable();
+                    var strRule = "{'fields':[ { 'method':'GetBCount'}] ,    'operator': '=',  'value': 1 }".Replace("'", "\"");
+                    var rule = GetRule(strRule);
+
+                    var result = Filter(ToQuery(query), rule);
+
+                    Assert.AreEqual(500, result.Count);
+                }
+
+                {
+                    var query = GetQueryable();
+                    var strRule = "{ 'fields':[ {'method':'GetBAtIndex', 'methodParameters':[0] }, {'field':'name'}] ,  'operator': '=',  'value': 'name987_b1' }".Replace("'", "\"");
+                    var rule = GetRule(strRule);
+
+                    var result = Filter(ToQuery(query), rule);
+
+                    Assert.AreEqual(1, result.Count);
+                    Assert.AreEqual(987, result[0].id);
+                }
+
+                {
+                    var query = GetQueryable();
+                    var strRule = "{ 'fields':[ {'method':'GetBAtIndex', 'methodParameters':[1] }] ,  'operator': 'IsNull' }".Replace("'", "\"");
+                    var rule = GetRule(strRule);
+
+                    var result = Filter(ToQuery(query), rule);
+
+                    Assert.AreEqual(500, result.Count);
+                }
+            }
+            #endregion
+
         }
 
 

+ 4 - 4
src/Test/Vit.Linq.MsTest/QueryBuilder/IQueryableTest/Filter_Test.cs → src/Test/Vit.Linq.MsTest/Filter/IQueryableTest/Filter_Test_FilterRule.cs

@@ -3,7 +3,7 @@ using Microsoft.VisualStudio.TestTools.UnitTesting;
 using System.Collections.Generic;
 using System.Linq;
 using Vit.Core.Module.Serialization;
-using Vit.Linq.QueryBuilder;
+using Vit.Linq.Filter;
 using System;
 using Newtonsoft.Json.Linq;
 using Queryable = System.Linq.IQueryable;
@@ -11,7 +11,7 @@ using Queryable = System.Linq.IQueryable;
 namespace Vit.Linq.MsTest.QueryBuilder.IQueryableTest
 {
     [TestClass]
-    public class Filter_Test : Filter_TestBase<Queryable>
+    public class Filter_Test_FilterRule : Filter_TestBase<Queryable>
     {
 
         [TestMethod]
@@ -33,9 +33,9 @@ namespace Vit.Linq.MsTest.QueryBuilder.IQueryableTest
             return query.IQueryable_Where(rule, GetService()).IQueryable_ToList<ModelA>();
         }
 
-        public virtual QueryBuilderService GetService()
+        public virtual FilterService GetService()
         {
-            QueryBuilderService service = new QueryBuilderService();
+            FilterService service = new FilterService();
             service.GetRuleValue = (object value, IFilterRule rule, Type fieldType) =>
             {
                 // to deal with null value

+ 39 - 0
src/Test/Vit.Linq.MsTest/Filter/IQueryableTest/Filter_Test_FilterRuleWithMethod.cs

@@ -0,0 +1,39 @@
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using Vit.Core.Module.Serialization;
+using Vit.Linq.Filter;
+using Vit.Linq.MoreFilter;
+using Newtonsoft.Json.Linq;
+using System;
+
+namespace Vit.Linq.MsTest.QueryBuilder.IQueryableTest
+{
+    [TestClass]
+    public class Filter_Test_FilterRuleWithMethod : Filter_Test_FilterRule
+    {
+        [TestMethod]
+        public void Test_FilterRule()
+        {
+            base.TestFilterRule();
+        }
+
+        public override IFilterRule GetRule(string filterRule)
+        {
+            return Json.Deserialize<FilterRuleWithMethod>(filterRule);
+        }
+
+        public virtual FilterService GetService()
+        {
+            FilterService service = new FilterService();
+            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;
+        }
+
+    }
+}
+
+

+ 11 - 10
src/Test/Vit.Linq.MsTest/QueryBuilder/IQueryableTest/Filter_Test_Newtonsoft.cs → src/Test/Vit.Linq.MsTest/Filter/IQueryableTest/Filter_Test_Newtonsoft.cs

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

+ 11 - 10
src/Test/Vit.Linq.MsTest/QueryBuilder/IQueryableTest/Filter_Test_Newtonsoft2.cs → src/Test/Vit.Linq.MsTest/Filter/IQueryableTest/Filter_Test_Newtonsoft2.cs

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

+ 11 - 10
src/Test/Vit.Linq.MsTest/QueryBuilder/IQueryableTest/Filter_Test_SystemTextJson.cs → src/Test/Vit.Linq.MsTest/Filter/IQueryableTest/Filter_Test_SystemTextJson.cs

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

+ 12 - 12
src/Test/Vit.Linq.MsTest/QueryBuilder/IQueryableTest/Filter_Test_SystemTextJson2.cs → src/Test/Vit.Linq.MsTest/Filter/IQueryableTest/Filter_Test_SystemTextJson2.cs

@@ -1,29 +1,29 @@
 using Microsoft.VisualStudio.TestTools.UnitTesting;
 
-using Vit.Linq.QueryBuilder;
-using Vit.Linq.QueryBuilder.SystemTextJson;
+using Vit.Linq.Filter;
+using Vit.Linq.SystemTextJson;
 
 namespace Vit.Linq.MsTest.QueryBuilder.IQueryableTest
 {
     [TestClass]
-    public class Filter_Test_SystemTextJson2 : Filter_Test
+    public class Filter_Test_SystemTextJson2 : Filter_Test_FilterRule
     {
+        [TestMethod]
+        public void Test_FilterRule()
+        {
+            base.TestFilterRule();
+        }
+
 
         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()
+        public override FilterService GetService()
         {
-            base.TestFilterRule();
+            FilterService service = new FilterService();
+            return service;
         }
-
     }
 }

+ 2 - 2
src/Test/Vit.Linq.MsTest/QueryBuilder/OperatorMap_Test.cs → src/Test/Vit.Linq.MsTest/Filter/OperatorMap_Test.cs

@@ -2,7 +2,7 @@
 
 using Microsoft.VisualStudio.TestTools.UnitTesting;
 using Vit.Core.Module.Serialization;
-using Vit.Linq.QueryBuilder;
+using Vit.Linq.Filter;
 using Vit.Extensions.Linq_Extensions;
 
 namespace Vit.Linq.MsTest.QueryBuilder
@@ -15,7 +15,7 @@ namespace Vit.Linq.MsTest.QueryBuilder
         public void Test_OperatorMap()
         {
             {
-                var service = new QueryBuilderService();
+                var service = new FilterService();
                 var query = DataSource.GetQueryable();
                 service.AddOperatorMap("Equal", FilterRuleOperator.Equal);
 

+ 4 - 4
src/Test/Vit.Linq.MsTest/QueryBuilder/QueryableTest/Filter_Test.cs → src/Test/Vit.Linq.MsTest/Filter/QueryableTest/Filter_Test_FilterRule.cs

@@ -3,7 +3,7 @@ using Microsoft.VisualStudio.TestTools.UnitTesting;
 using System.Collections.Generic;
 using System.Linq;
 using Vit.Core.Module.Serialization;
-using Vit.Linq.QueryBuilder;
+using Vit.Linq.Filter;
 using System;
 using Newtonsoft.Json.Linq;
 using Queryable = System.Linq.IQueryable<Vit.Linq.MsTest.ModelA>;
@@ -11,7 +11,7 @@ using Queryable = System.Linq.IQueryable<Vit.Linq.MsTest.ModelA>;
 namespace Vit.Linq.MsTest.QueryBuilder.QueryableTest
 {
     [TestClass]
-    public class Filter_Test : Filter_TestBase<Queryable>
+    public class Filter_Test_FilterRule : Filter_TestBase<Queryable>
     {
 
         [TestMethod]
@@ -33,9 +33,9 @@ namespace Vit.Linq.MsTest.QueryBuilder.QueryableTest
             return query.Where(rule, GetService()).ToList<ModelA>();
         }
 
-        public virtual QueryBuilderService GetService()
+        public virtual FilterService GetService()
         {
-            QueryBuilderService service = new QueryBuilderService();
+            FilterService service = new FilterService();
             service.GetRuleValue = (object value, IFilterRule rule, Type fieldType) =>
             {
                 // to deal with null value

+ 39 - 0
src/Test/Vit.Linq.MsTest/Filter/QueryableTest/Filter_Test_FilterRuleWithMethod.cs

@@ -0,0 +1,39 @@
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using Vit.Core.Module.Serialization;
+using Vit.Linq.Filter;
+using Vit.Linq.MoreFilter;
+using Newtonsoft.Json.Linq;
+using System;
+
+namespace Vit.Linq.MsTest.QueryBuilder.QueryableTest
+{
+    [TestClass]
+    public class Filter_Test_FilterRuleWithMethod : Filter_Test_FilterRule
+    {
+        [TestMethod]
+        public void Test_FilterRule()
+        {
+            base.TestFilterRule();
+        }
+
+        public override IFilterRule GetRule(string filterRule)
+        {
+            return Json.Deserialize<FilterRuleWithMethod>(filterRule);
+        }
+
+        public virtual FilterService GetService()
+        {
+            FilterService service = new FilterService();
+            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;
+        }
+
+    }
+}
+
+

+ 11 - 10
src/Test/Vit.Linq.MsTest/QueryBuilder/QueryableTest/Filter_Test_Newtonsoft.cs → src/Test/Vit.Linq.MsTest/Filter/QueryableTest/Filter_Test_Newtonsoft.cs

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

+ 11 - 10
src/Test/Vit.Linq.MsTest/QueryBuilder/QueryableTest/Filter_Test_Newtonsoft2.cs → src/Test/Vit.Linq.MsTest/Filter/QueryableTest/Filter_Test_Newtonsoft2.cs

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

+ 11 - 10
src/Test/Vit.Linq.MsTest/QueryBuilder/QueryableTest/Filter_Test_SystemTextJson.cs → src/Test/Vit.Linq.MsTest/Filter/QueryableTest/Filter_Test_SystemTextJson.cs

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

+ 12 - 12
src/Test/Vit.Linq.MsTest/QueryBuilder/QueryableTest/Filter_Test_SystemTextJson2.cs → src/Test/Vit.Linq.MsTest/Filter/QueryableTest/Filter_Test_SystemTextJson2.cs

@@ -1,29 +1,29 @@
 using Microsoft.VisualStudio.TestTools.UnitTesting;
 
-using Vit.Linq.QueryBuilder;
-using Vit.Linq.QueryBuilder.SystemTextJson;
+using Vit.Linq.Filter;
+using Vit.Linq.SystemTextJson;
 
 namespace Vit.Linq.MsTest.QueryBuilder.QueryableTest
 {
     [TestClass]
-    public class Filter_Test_SystemTextJson2 : Filter_Test
+    public class Filter_Test_SystemTextJson2 : Filter_Test_FilterRule
     {
+        [TestMethod]
+        public void Test_FilterRule()
+        {
+            base.TestFilterRule();
+        }
+
 
         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()
+        public override FilterService GetService()
         {
-            base.TestFilterRule();
+            FilterService service = new FilterService();
+            return service;
         }
-
     }
 }

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

@@ -20,7 +20,7 @@
 
     <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\Filter\**\*.cs" Link="Filter\%(RecursiveDir)%(FileName)%(Extension)" />
         <Compile Include="..\Vit.Linq.MsTest\DataSource.cs" />
     </ItemGroup>
 

+ 2 - 2
src/Vit.Linq.Extensions/Extensions/IQueryable_Sort_Extensions.cs

@@ -5,7 +5,7 @@ using System.Linq.Expressions;
 using System.Runtime.CompilerServices;
 
 using Vit.Core.Util.ComponentModel.Query;
-using Vit.Linq.QueryBuilder;
+using Vit.Linq;
 
 namespace Vit.Extensions.Linq_Extensions
 {
@@ -42,7 +42,7 @@ namespace Vit.Extensions.Linq_Extensions
             foreach (var item in sort)
             {
                 // get memberExp
-                MemberExpression memberExp = LinqHelp.GetFieldMemberExpression(parameter, item.field);
+                Expression memberExp = LinqHelp.GetFieldMemberExpression(parameter, item.field);
 
 
                 #region (x.2)Call

+ 2 - 3
src/Vit.Linq.Extensions/Extensions/Queryable_Sort_ByReflection_Extensions.cs

@@ -1,10 +1,9 @@
-using System;
-using System.Collections.Generic;
+using System.Collections.Generic;
 using System.Linq;
 using System.Runtime.CompilerServices;
 
 using Vit.Core.Util.ComponentModel.Query;
-using Vit.Linq.QueryBuilder;
+using Vit.Linq;
 
 namespace Vit.Extensions.Linq_Extensions
 {

+ 4 - 3
src/Vit.Linq.Extensions/Extensions/Queryable_Sort_Extensions.cs

@@ -5,7 +5,8 @@ using System.Reflection;
 using System.Runtime.CompilerServices;
 
 using Vit.Core.Util.ComponentModel.Query;
-using Vit.Linq.QueryBuilder;
+using Vit.Linq;
+using Vit.Linq.Filter;
 
 namespace Vit.Extensions.Linq_Extensions
 {
@@ -31,7 +32,7 @@ namespace Vit.Extensions.Linq_Extensions
             foreach (var item in sort)
             {
 
-                MemberExpression memberExp = LinqHelp.GetFieldMemberExpression(paramExp, item.field);
+                var memberExp = LinqHelp.GetFieldMemberExpression(paramExp, item.field);
                 LambdaExpression exp = Expression.Lambda(memberExp, paramExp);
 
                 if (orderedQuery == null)
@@ -62,7 +63,7 @@ namespace Vit.Extensions.Linq_Extensions
             if (string.IsNullOrEmpty(field)) return query;
 
             var paramExp = Expression.Parameter(typeof(T));
-            MemberExpression memberExp = LinqHelp.GetFieldMemberExpression(paramExp, field);
+            var memberExp = LinqHelp.GetFieldMemberExpression(paramExp, field);
             LambdaExpression expr = Expression.Lambda(memberExp, paramExp);
 
             return OrderBy(query, expr, asc);

+ 10 - 36
src/Vit.Linq.NewtonsoftJson/FilterRule_Newtonsoft.cs

@@ -5,53 +5,27 @@ using System.Diagnostics.CodeAnalysis;
 using Newtonsoft.Json;
 using Newtonsoft.Json.Linq;
 
-namespace Vit.Linq.QueryBuilder.NewtonsoftJson
+using Vit.Linq.MoreFilter;
+
+namespace Vit.Linq.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
+    public class FilterRule_Newtonsoft : FilterRuleWithMethod<FilterRule_Newtonsoft>
     {
-        /// <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
+        protected override object GetPrimitiveValue(Object value)
         {
-            get
+            if (value is JToken item)
             {
-                if (_value is JToken jt)
-                {
-                    return GetPrimitiveValue(jt);
-                }
-                return _value;
+                return GetPrimitiveValueFromJson(item);
             }
-            set => _value = value;
+            return value;
         }
 
-        private object _value;
 
-        IEnumerable<IFilterRule> IFilterRule.rules => rules;
 
 
         public static FilterRule_Newtonsoft FromString(string filter)
@@ -62,7 +36,7 @@ namespace Vit.Linq.QueryBuilder.NewtonsoftJson
         static readonly global::Newtonsoft.Json.JsonSerializerSettings serializeSetting = new global::Newtonsoft.Json.JsonSerializerSettings() { Converters = { new ValueConverter() } };
 
 
-        public static object GetPrimitiveValue(JToken value)
+        public static object GetPrimitiveValueFromJson(JToken value)
         {
             if (value is JValue jv)
             {
@@ -92,7 +66,7 @@ namespace Vit.Linq.QueryBuilder.NewtonsoftJson
             public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
             {
                 JToken token = JToken.Load(reader);
-                return GetPrimitiveValue(token);
+                return GetPrimitiveValueFromJson(token);
             }
 
             public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)

+ 56 - 46
src/Vit.Linq.SystemTextJson/FilterRule_SystemTextJson.cs

@@ -1,83 +1,56 @@
 using System;
-using System.Collections.Generic;
 using System.Diagnostics.CodeAnalysis;
 using System.Linq;
 using System.Text.Json;
+using System.Text.Json.Serialization;
 using System.Text.Unicode;
 
-namespace Vit.Linq.QueryBuilder.SystemTextJson
+using Vit.Linq.MoreFilter;
+
+namespace Vit.Linq.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
+    public class FilterRule_SystemTextJson : FilterRuleWithMethod<FilterRule_SystemTextJson>
     {
-        /// <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
+        protected override object GetPrimitiveValue(Object value)
         {
-            get
+            if (value is JsonElement je)
             {
-                if (_value is JsonElement je)
-                {
-                    return GetPrimitiveValue(je);
-                }
-                return _value;
+                return GetPrimitiveValueFromJson(je);
             }
-            set => _value = value;
+            return value;
         }
 
-        private object _value;
-
-        IEnumerable<IFilterRule> IFilterRule.rules => rules;
-
-
         public static FilterRule_SystemTextJson FromString(string filter)
         {
-            return JsonSerializer.Deserialize<FilterRule_SystemTextJson>(filter, options); 
+            return JsonSerializer.Deserialize<FilterRule_SystemTextJson>(filter, options);
         }
 
         static readonly JsonSerializerOptions options = GetDefaultOptions();
 
 
-        public static JsonSerializerOptions GetDefaultOptions(  )
+        public static JsonSerializerOptions GetDefaultOptions()
         {
-          var options=  new JsonSerializerOptions
+            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
-            }; 
+            };
+
+            options.Converters.Add(new CustomObjectConverter());
 
             return options;
         }
 
 
 
-        public static object GetPrimitiveValue(JsonElement value)
+
+        public static object GetPrimitiveValueFromJson(JsonElement value)
         {
             switch (value.ValueKind)
             {
@@ -88,13 +61,50 @@ namespace Vit.Linq.QueryBuilder.SystemTextJson
                 case JsonValueKind.Number: return value.GetDecimal();
                 case JsonValueKind.String: return value.GetString();
                 case JsonValueKind.Array:
-                    return value.EnumerateArray().Select(GetPrimitiveValue).ToList();
+                    return value.EnumerateArray().Select(GetPrimitiveValueFromJson).ToList();
             }
 
             return value;
         }
 
 
-        
+        class CustomObjectConverter : JsonConverter<object>
+        {
+            public override object Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
+            {
+                switch (reader.TokenType)
+                {
+                    case JsonTokenType.Number:
+                        if (reader.TryGetInt32(out int intValue))
+                        {
+                            return intValue;
+                        }
+                        if (reader.TryGetDouble(out double doubleValue))
+                        {
+                            return doubleValue;
+                        }
+                        break;
+                    case JsonTokenType.True:
+                        return true;
+                    case JsonTokenType.False:
+                        return false;
+                    case JsonTokenType.String:
+                        return reader.GetString();
+                    case JsonTokenType.Null:
+                        return null;
+                }
+                using (JsonDocument doc = JsonDocument.ParseValue(ref reader))
+                {
+                    return doc.RootElement.Clone();
+                }
+            }
+
+            public override void Write(Utf8JsonWriter writer, object value, JsonSerializerOptions options)
+            {
+                JsonSerializer.Serialize(writer, value, value.GetType(), options);
+            }
+        }
+
+
     }
 }

+ 1 - 1
src/Vit.Linq/QueryBuilder/ECondition.cs → src/Vit.Linq/Filter/ECondition.cs

@@ -1,4 +1,4 @@
-namespace Vit.Linq.QueryBuilder
+namespace Vit.Linq.Filter
 {
     public enum ECondition
     {

+ 3 - 3
src/Vit.Linq/QueryBuilder/Extensions/IQueryable_Where_Extensions.cs → src/Vit.Linq/Filter/Extensions/IQueryable_Where_Extensions.cs

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

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

@@ -1,7 +1,7 @@
 using System.Linq;
 using System.Runtime.CompilerServices;
 
-using Vit.Linq.QueryBuilder;
+using Vit.Linq.Filter;
 
 namespace Vit.Extensions.Linq_Extensions
 {
@@ -9,12 +9,12 @@ namespace Vit.Extensions.Linq_Extensions
     {
         #region Where
         [MethodImpl(MethodImplOptions.AggressiveInlining)]
-        public static IQueryable<T> Where<T>(this IQueryable<T> query, IFilterRule rule, QueryBuilderService service = null)
+        public static IQueryable<T> Where<T>(this IQueryable<T> query, IFilterRule rule, FilterService service = null)
         where T : class
         {
             if (query == null || rule == null) return query;
 
-            var predicate = (service ?? QueryBuilderService.Instance).ToExpression<T>(rule);
+            var predicate = (service ?? FilterService.Instance).ToExpression<T>(rule);
             if (predicate == null)
             {
                 return query;

+ 14 - 0
src/Vit.Linq/Filter/FilterRule.cs

@@ -0,0 +1,14 @@
+using System.Collections.Generic;
+using System.Diagnostics.CodeAnalysis;
+
+namespace Vit.Linq.Filter
+{
+    /// <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 : FilterRuleBase<FilterRule>
+    {
+
+    }
+}

+ 52 - 0
src/Vit.Linq/Filter/FilterRuleBase.cs

@@ -0,0 +1,52 @@
+using System;
+using System.Collections.Generic;
+using System.Diagnostics.CodeAnalysis;
+using System.Linq;
+using System.Linq.Expressions;
+
+namespace Vit.Linq.Filter
+{
+    /// <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 abstract class FilterRuleBase<RuleType> : IFilterRule
+        where RuleType : IFilterRule
+    {
+        /// <summary>
+        /// condition - acceptable values are "and" and "or".
+        /// </summary>
+        public virtual string condition { get; set; }
+
+
+        public virtual string field { get; set; }
+
+
+        public virtual string @operator { get; set; }
+
+        /// <summary>
+        ///  nested filter rules.
+        /// </summary>
+        public virtual List<RuleType> rules { get; set; }
+
+
+        /// <summary>
+        /// Gets or sets the value of the filter.
+        /// </summary>
+        /// <value>
+        /// The value.
+        /// </value>
+        public virtual object value { get; set; }
+
+        IEnumerable<IFilterRule> IFilterRule.rules => rules?.Select(r => (IFilterRule)r);
+
+
+        public virtual Expression GetLeftValueExpression(Expression valueExpression)
+        {
+            if (!string.IsNullOrWhiteSpace(field))
+                valueExpression = LinqHelp.GetFieldMemberExpression(valueExpression, field);
+            return valueExpression;
+        }
+
+    }
+}

+ 1 - 1
src/Vit.Linq/QueryBuilder/FilterRuleOperator.cs → src/Vit.Linq/Filter/FilterRuleOperator.cs

@@ -1,4 +1,4 @@
-namespace Vit.Linq.QueryBuilder
+namespace Vit.Linq.Filter
 {
     public class FilterRuleOperator
     {

+ 364 - 0
src/Vit.Linq/Filter/FilterService.cs

@@ -0,0 +1,364 @@
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Linq;
+using System.Linq.Expressions;
+
+
+namespace Vit.Linq.Filter
+{
+    public class FilterService
+    {
+        public static FilterService Instance = new FilterService();
+
+        /// <summary>
+        /// operatorName -> operatorType(in class FilterRuleOperator)
+        /// </summary>
+        public Dictionary<string, string> operatorMap = new Dictionary<string, string>();
+        public bool operatorIsIgnoreCase = true;
+        public bool ignoreError = false;
+
+        public FilterService AddOperatorMap(string operatorName, string operatorType)
+        {
+            if (operatorIsIgnoreCase) operatorName = operatorName?.ToLower();
+            operatorMap[operatorName] = operatorType;
+            return this;
+        }
+
+        public FilterService AddOperatorMap(IEnumerable<(string operatorName, string operatorType)> maps)
+        {
+            foreach (var map in maps)
+                AddOperatorMap(map.operatorName, map.operatorType);
+            return this;
+        }
+
+
+
+        public Func<T, bool> ToPredicate<T>(IFilterRule rule)
+        {
+            return ToExpression<T>(rule)?.Compile();
+        }
+
+        public string ToExpressionString<T>(IFilterRule rule)
+        {
+            return ToExpression<T>(rule)?.ToString();
+        }
+
+
+        public Expression<Func<T, bool>> ToExpression<T>(IFilterRule rule)
+        {
+            var exp = ToLambdaExpression(rule, typeof(T));
+            return (Expression<Func<T, bool>>)exp;
+        }
+
+
+        public LambdaExpression ToLambdaExpression(IFilterRule rule, Type targetType)
+        {
+            ParameterExpression parameter = Expression.Parameter(targetType);
+            var expression = ConvertToExpression(rule, parameter);
+            if (expression == null)
+            {
+                return null;
+            }
+            return Expression.Lambda(expression, parameter);
+        }
+
+
+        public virtual ECondition GetCondition(IFilterRule filter)
+        {
+            return filter.condition?.ToLower() == "or" ? ECondition.or : ECondition.and;
+        }
+
+
+
+        protected virtual Expression GetLeftValueExpression(IFilterRule rule, ParameterExpression valueExpression)
+        {
+            return rule.GetLeftValueExpression(valueExpression); 
+        }
+
+
+        public virtual string GetOperator(IFilterRule filter)
+        {
+            var operate = filter.@operator ?? "";
+            if (operatorIsIgnoreCase) operate = operate.ToLower();
+            if (operatorMap.TryGetValue(operate, out var op2)) return operatorIsIgnoreCase ? op2?.ToLower() : op2;
+            return operate;
+        }
+
+
+        #region GetRightValueExpression
+
+        public Func<object, IFilterRule, Type, object> GetRuleValue { get; set; }
+        protected virtual object GetRulePrimitiveValue(object value, IFilterRule rule, Type fieldType)
+        {
+            if (GetRuleValue != null) return GetRuleValue(value, rule, fieldType);
+            return value;
+        }
+
+
+
+
+        protected virtual Expression GetRightValueExpression(IFilterRule rule, ParameterExpression parameter, Type valueType)
+        {
+            object rightValue = rule.value;
+
+            // typeof(IEnumerable).IsAssignableFrom(valueType)
+            if (valueType.IsGenericType && typeof(IEnumerable<>).IsAssignableFrom(valueType.GetGenericTypeDefinition()))
+            {
+                // constant List
+                object value = null;
+                if (rule.value != null)
+                {
+                    //value = Vit.Core.Module.Serialization.Json.Deserialize(Vit.Core.Module.Serialization.Json.Serialize(rule.value), valueType);
+                    var leftFieldType = valueType.GetGenericArguments()[0];
+                    value = ConvertToList(rule.value, rule, leftFieldType);
+                }
+                rightValue = value;
+            }
+            else
+            {
+                // constant value
+                object value = GetRulePrimitiveValue(rule.value, rule, valueType);
+                if (value != null)
+                {
+                    Type type = Nullable.GetUnderlyingType(valueType) ?? valueType;
+                    value = Convert.ChangeType(value, type);
+                }
+                rightValue = value;
+            }
+
+            Expression<Func<object>> valueLamba = () => rightValue;
+            return Expression.Convert(valueLamba.Body, valueType);
+        }
+
+        #region ConvertToList
+        internal object ConvertToList(object value, IFilterRule rule, Type fieldType)
+        {
+            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(FilterService).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, IFilterRule rule)
+        {
+            Type fieldType = typeof(T);
+            var list = new List<T>();
+            foreach (var item in values)
+            {
+                var itemValue = GetRulePrimitiveValue(item, rule, fieldType);
+                T value = (T)Convert.ChangeType(itemValue, fieldType);
+                list.Add(value);
+            }
+            return list;
+        }
+        #endregion
+
+        #endregion
+
+
+        Expression ConvertToExpression(IFilterRule rule, ParameterExpression parameter)
+        {
+            if (rule == null) return null;
+
+            // #1 nested filter rules
+            if (rule.rules?.Any() == true)
+            {
+                return ConvertToExpression(rule.rules, parameter, GetCondition(rule));
+            }
+
+            // #2 simple rule
+            if (string.IsNullOrWhiteSpace(rule.@operator))
+            {
+                return null;
+            }
+
+            Expression leftValueExpression = GetLeftValueExpression(rule, parameter);
+            Type leftFieldType = leftValueExpression.Type;
+
+            #region Get Expression
+
+            var Operator = GetOperator(rule);
+            var cmpType = operatorIsIgnoreCase ? StringComparison.OrdinalIgnoreCase : StringComparison.Ordinal;
+
+
+            {
+                #region ##1  null
+                if (FilterRuleOperator.IsNull.Equals(Operator, cmpType))
+                {
+                    return IsNull();
+                }
+                if (FilterRuleOperator.IsNotNull.Equals(Operator, cmpType))
+                {
+                    return Expression.Not(IsNull());
+                }
+                #endregion
+
+
+                #region ##2 number
+                if (FilterRuleOperator.Equal.Equals(Operator, cmpType))
+                {
+                    return Expression.Equal(leftValueExpression, GetRightValueExpression());
+                }
+                if (FilterRuleOperator.NotEqual.Equals(Operator, cmpType))
+                {
+                    return Expression.NotEqual(leftValueExpression, GetRightValueExpression());
+                }
+
+                if (FilterRuleOperator.GreaterThan.Equals(Operator, cmpType))
+                {
+                    return Expression.GreaterThan(leftValueExpression, GetRightValueExpression());
+                }
+                if (FilterRuleOperator.GreaterThanOrEqual.Equals(Operator, cmpType))
+                {
+                    return Expression.GreaterThanOrEqual(leftValueExpression, GetRightValueExpression());
+                }
+                if (FilterRuleOperator.LessThan.Equals(Operator, cmpType))
+                {
+                    return Expression.LessThan(leftValueExpression, GetRightValueExpression());
+
+                }
+                if (FilterRuleOperator.LessThanOrEqual.Equals(Operator, cmpType))
+                {
+                    return Expression.LessThanOrEqual(leftValueExpression, GetRightValueExpression());
+                }
+                #endregion
+
+
+                #region ##3 array
+                if (FilterRuleOperator.In.Equals(Operator, cmpType))
+                {
+                    return In();
+                }
+                if (FilterRuleOperator.NotIn.Equals(Operator, cmpType))
+                {
+                    return Expression.Not(In());
+                }
+                #endregion
+
+
+                #region ##4 string
+                if (FilterRuleOperator.Contains.Equals(Operator, cmpType))
+                {
+                    var nullCheck = Expression.Call(typeof(string), "IsNullOrEmpty", null, leftValueExpression);
+                    var contains = Expression.Call(leftValueExpression, "Contains", null, GetRightValueExpression());
+
+                    return Expression.AndAlso(Expression.Not(nullCheck), contains);
+
+                }
+                if (FilterRuleOperator.NotContains.Equals(Operator, cmpType))
+                {
+                    var nullCheck = Expression.Call(typeof(string), "IsNullOrEmpty", null, leftValueExpression);
+                    var contains = Expression.Call(leftValueExpression, "Contains", null, GetRightValueExpression());
+
+                    return Expression.OrElse(nullCheck, Expression.Not(contains));
+                }
+                if (FilterRuleOperator.StartsWith.Equals(Operator, cmpType))
+                {
+                    var nullCheck = Expression.Not(Expression.Call(typeof(string), "IsNullOrEmpty", null, leftValueExpression));
+                    var startsWith = Expression.Call(leftValueExpression, "StartsWith", null, GetRightValueExpression());
+
+                    return Expression.AndAlso(nullCheck, startsWith);
+                }
+
+                if (FilterRuleOperator.EndsWith.Equals(Operator, cmpType))
+                {
+                    var nullCheck = Expression.Not(Expression.Call(typeof(string), "IsNullOrEmpty", null, leftValueExpression));
+                    var endsWith = Expression.Call(leftValueExpression, "EndsWith", null, GetRightValueExpression());
+                    return Expression.AndAlso(nullCheck, endsWith);
+                }
+                if (FilterRuleOperator.IsNullOrEmpty.Equals(Operator, cmpType))
+                {
+                    return Expression.Call(typeof(string), "IsNullOrEmpty", null, leftValueExpression);
+                }
+                if (FilterRuleOperator.IsNotNullOrEmpty.Equals(Operator, cmpType))
+                {
+                    return Expression.Not(Expression.Call(typeof(string), "IsNullOrEmpty", null, leftValueExpression));
+                }
+                #endregion
+
+            }
+            #endregion
+
+            if (!ignoreError) throw new Exception("unrecognized operator : " + rule.@operator);
+            return null;
+
+
+            #region inner Method
+            Expression GetRightValueExpression()
+            {
+                return this.GetRightValueExpression(rule, parameter, leftFieldType);
+            }
+
+            Expression IsNull()
+            {
+                var isNullable = !leftFieldType.IsValueType || Nullable.GetUnderlyingType(leftFieldType) != null;
+
+                if (isNullable)
+                {
+                    var nullValue = Expression.Constant(null, leftFieldType);
+                    return Expression.Equal(leftValueExpression, nullValue);
+                }
+                return Expression.Constant(false, typeof(bool));
+            }
+
+            Expression In()
+            {
+                Expression arrayExp = null;
+                Type valueType = typeof(IEnumerable<>).MakeGenericType(leftFieldType);
+                arrayExp = this.GetRightValueExpression(rule, parameter, valueType);
+
+                var inCheck = Expression.Call(typeof(System.Linq.Enumerable), "Contains", new[] { leftFieldType }, arrayExp, leftValueExpression);
+                return inCheck;
+            }
+            #endregion
+        }
+
+
+        Expression ConvertToExpression(IEnumerable<IFilterRule> rules, ParameterExpression parameter, ECondition condition = ECondition.and)
+        {
+            if (rules?.Any() != true)
+            {
+                return null;
+            }
+
+            Expression expression = null;
+
+            foreach (var rule in rules)
+            {
+                var curExp = ConvertToExpression(rule, parameter);
+                if (curExp != null)
+                    expression = Append(expression, curExp);
+            }
+
+            return expression;
+
+
+            #region Method Append
+            Expression Append(Expression exp1, Expression exp2)
+            {
+                if (exp1 == null)
+                {
+                    return exp2;
+                }
+
+                if (exp2 == null)
+                {
+                    return exp1;
+                }
+                return condition == ECondition.or ? Expression.OrElse(exp1, exp2) : Expression.AndAlso(exp1, exp2);
+            }
+            #endregion
+
+        }
+
+
+    }
+}

+ 6 - 2
src/Vit.Linq/QueryBuilder/IFilterRule.cs → src/Vit.Linq/Filter/IFilterRule.cs

@@ -1,6 +1,8 @@
-using System.Collections.Generic;
+using System;
+using System.Collections.Generic;
+using System.Linq.Expressions;
 
-namespace Vit.Linq.QueryBuilder
+namespace Vit.Linq.Filter
 {
     /// <summary>
     /// This interface is used to define a hierarchical filter for a given collection.
@@ -40,5 +42,7 @@ namespace Vit.Linq.QueryBuilder
         /// value of the filter. Supported value types are "integer", "double", "string", "date", "datetime", and "boolean".
         /// </summary>
         object value { get; set; }
+
+        Expression GetLeftValueExpression(Expression valueExpression);
     }
 }

+ 110 - 82
src/Vit.Linq/QueryBuilder/QueryBuilderService.cs → src/Vit.Linq/Filter/QueryBuilderService.cs

@@ -64,15 +64,20 @@ namespace Vit.Linq.QueryBuilder
         }
 
 
+        public virtual ECondition GetCondition(IFilterRule filter)
+        {
+            return filter.condition?.ToLower() == "or" ? ECondition.or : ECondition.and;
+        }
 
 
 
-        public ECondition GetCondition(IFilterRule filter)
+        protected virtual Expression GetLeftValueExpression(IFilterRule rule, ParameterExpression valueExpression)
         {
-            return filter.condition?.ToLower() == "or" ? ECondition.or : ECondition.and;
+            return rule.GetLeftValueExpression(valueExpression); 
         }
 
-        public string GetOperator(IFilterRule filter)
+
+        public virtual string GetOperator(IFilterRule filter)
         {
             var operate = filter.@operator ?? "";
             if (operatorIsIgnoreCase) operate = operate.ToLower();
@@ -80,7 +85,10 @@ namespace Vit.Linq.QueryBuilder
             return operate;
         }
 
-        public Func<object, IFilterRule, Type, object> GetRuleValue;
+
+        #region GetRightValueExpression
+
+        public Func<object, IFilterRule, Type, object> GetRuleValue { get; set; }
         protected virtual object GetRulePrimitiveValue(object value, IFilterRule rule, Type fieldType)
         {
             if (GetRuleValue != null) return GetRuleValue(value, rule, fieldType);
@@ -88,6 +96,74 @@ namespace Vit.Linq.QueryBuilder
         }
 
 
+
+
+        protected virtual Expression GetRightValueExpression(IFilterRule rule, ParameterExpression parameter, Type valueType)
+        {
+            object rightValue = rule.value;
+
+            // typeof(IEnumerable).IsAssignableFrom(valueType)
+            if (valueType.IsGenericType && typeof(IEnumerable<>).IsAssignableFrom(valueType.GetGenericTypeDefinition()))
+            {
+                // constant List
+                object value = null;
+                if (rule.value != null)
+                {
+                    //value = Vit.Core.Module.Serialization.Json.Deserialize(Vit.Core.Module.Serialization.Json.Serialize(rule.value), valueType);
+                    var leftFieldType = valueType.GetGenericArguments()[0];
+                    value = ConvertToList(rule.value, rule, leftFieldType);
+                }
+                rightValue = value;
+            }
+            else
+            {
+                // constant value
+                object value = GetRulePrimitiveValue(rule.value, rule, valueType);
+                if (value != null)
+                {
+                    Type type = Nullable.GetUnderlyingType(valueType) ?? valueType;
+                    value = Convert.ChangeType(value, type);
+                }
+                rightValue = value;
+            }
+
+            Expression<Func<object>> valueLamba = () => rightValue;
+            return Expression.Convert(valueLamba.Body, valueType);
+        }
+
+        #region ConvertToList
+        internal object ConvertToList(object value, IFilterRule rule, Type fieldType)
+        {
+            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, IFilterRule rule)
+        {
+            Type fieldType = typeof(T);
+            var list = new List<T>();
+            foreach (var item in values)
+            {
+                var itemValue = GetRulePrimitiveValue(item, rule, fieldType);
+                T value = (T)Convert.ChangeType(itemValue, fieldType);
+                list.Add(value);
+            }
+            return list;
+        }
+        #endregion
+
+        #endregion
+
+
         Expression ConvertToExpression(IFilterRule rule, ParameterExpression parameter)
         {
             if (rule == null) return null;
@@ -99,17 +175,16 @@ namespace Vit.Linq.QueryBuilder
             }
 
             // #2 simple rule
-            if (string.IsNullOrWhiteSpace(rule.field))
+            if (string.IsNullOrWhiteSpace(rule.@operator))
             {
                 return null;
             }
 
-            MemberExpression memberExp = LinqHelp.GetFieldMemberExpression(parameter, rule.field);
+            Expression leftValueExpression = GetLeftValueExpression(rule, parameter);
+            Type leftFieldType = leftValueExpression.Type;
 
             #region Get Expression
 
-            Type fieldType = memberExp.Type;
-
             var Operator = GetOperator(rule);
             var cmpType = operatorIsIgnoreCase ? StringComparison.OrdinalIgnoreCase : StringComparison.Ordinal;
 
@@ -130,29 +205,29 @@ namespace Vit.Linq.QueryBuilder
                 #region ##2 number
                 if (FilterRuleOperator.Equal.Equals(Operator, cmpType))
                 {
-                    return Expression.Equal(memberExp, ConvertValueExp());
+                    return Expression.Equal(leftValueExpression, GetRightValueExpression());
                 }
                 if (FilterRuleOperator.NotEqual.Equals(Operator, cmpType))
                 {
-                    return Expression.NotEqual(memberExp, ConvertValueExp());
+                    return Expression.NotEqual(leftValueExpression, GetRightValueExpression());
                 }
 
                 if (FilterRuleOperator.GreaterThan.Equals(Operator, cmpType))
                 {
-                    return Expression.GreaterThan(memberExp, ConvertValueExp());
+                    return Expression.GreaterThan(leftValueExpression, GetRightValueExpression());
                 }
                 if (FilterRuleOperator.GreaterThanOrEqual.Equals(Operator, cmpType))
                 {
-                    return Expression.GreaterThanOrEqual(memberExp, ConvertValueExp());
+                    return Expression.GreaterThanOrEqual(leftValueExpression, GetRightValueExpression());
                 }
                 if (FilterRuleOperator.LessThan.Equals(Operator, cmpType))
                 {
-                    return Expression.LessThan(memberExp, ConvertValueExp());
+                    return Expression.LessThan(leftValueExpression, GetRightValueExpression());
 
                 }
                 if (FilterRuleOperator.LessThanOrEqual.Equals(Operator, cmpType))
                 {
-                    return Expression.LessThanOrEqual(memberExp, ConvertValueExp());
+                    return Expression.LessThanOrEqual(leftValueExpression, GetRightValueExpression());
                 }
                 #endregion
 
@@ -172,40 +247,40 @@ namespace Vit.Linq.QueryBuilder
                 #region ##4 string
                 if (FilterRuleOperator.Contains.Equals(Operator, cmpType))
                 {
-                    var nullCheck = Expression.Call(typeof(string), "IsNullOrEmpty", null, memberExp);
-                    var contains = Expression.Call(memberExp, "Contains", null, ConvertValueExp());
+                    var nullCheck = Expression.Call(typeof(string), "IsNullOrEmpty", null, leftValueExpression);
+                    var contains = Expression.Call(leftValueExpression, "Contains", null, GetRightValueExpression());
 
                     return Expression.AndAlso(Expression.Not(nullCheck), contains);
 
                 }
                 if (FilterRuleOperator.NotContains.Equals(Operator, cmpType))
                 {
-                    var nullCheck = Expression.Call(typeof(string), "IsNullOrEmpty", null, memberExp);
-                    var contains = Expression.Call(memberExp, "Contains", null, ConvertValueExp());
+                    var nullCheck = Expression.Call(typeof(string), "IsNullOrEmpty", null, leftValueExpression);
+                    var contains = Expression.Call(leftValueExpression, "Contains", null, GetRightValueExpression());
 
                     return Expression.OrElse(nullCheck, Expression.Not(contains));
                 }
                 if (FilterRuleOperator.StartsWith.Equals(Operator, cmpType))
                 {
-                    var nullCheck = Expression.Not(Expression.Call(typeof(string), "IsNullOrEmpty", null, memberExp));
-                    var startsWith = Expression.Call(memberExp, "StartsWith", null, ConvertValueExp());
+                    var nullCheck = Expression.Not(Expression.Call(typeof(string), "IsNullOrEmpty", null, leftValueExpression));
+                    var startsWith = Expression.Call(leftValueExpression, "StartsWith", null, GetRightValueExpression());
 
                     return Expression.AndAlso(nullCheck, startsWith);
                 }
 
                 if (FilterRuleOperator.EndsWith.Equals(Operator, cmpType))
                 {
-                    var nullCheck = Expression.Not(Expression.Call(typeof(string), "IsNullOrEmpty", null, memberExp));
-                    var endsWith = Expression.Call(memberExp, "EndsWith", null, ConvertValueExp());
+                    var nullCheck = Expression.Not(Expression.Call(typeof(string), "IsNullOrEmpty", null, leftValueExpression));
+                    var endsWith = Expression.Call(leftValueExpression, "EndsWith", null, GetRightValueExpression());
                     return Expression.AndAlso(nullCheck, endsWith);
                 }
                 if (FilterRuleOperator.IsNullOrEmpty.Equals(Operator, cmpType))
                 {
-                    return Expression.Call(typeof(string), "IsNullOrEmpty", null, memberExp);
+                    return Expression.Call(typeof(string), "IsNullOrEmpty", null, leftValueExpression);
                 }
                 if (FilterRuleOperator.IsNotNullOrEmpty.Equals(Operator, cmpType))
                 {
-                    return Expression.Not(Expression.Call(typeof(string), "IsNullOrEmpty", null, memberExp));
+                    return Expression.Not(Expression.Call(typeof(string), "IsNullOrEmpty", null, leftValueExpression));
                 }
                 #endregion
 
@@ -216,29 +291,20 @@ namespace Vit.Linq.QueryBuilder
             return null;
 
 
-            #region Method ConvertValueExp
-            UnaryExpression ConvertValueExp()
+            #region inner Method
+            Expression GetRightValueExpression()
             {
-                object value = GetRulePrimitiveValue(rule.value,rule,fieldType);
-                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);
+                return this.GetRightValueExpression(rule, parameter, leftFieldType);
             }
 
-
             Expression IsNull()
             {
-                var isNullable = !fieldType.IsValueType || Nullable.GetUnderlyingType(fieldType) != null;
+                var isNullable = !leftFieldType.IsValueType || Nullable.GetUnderlyingType(leftFieldType) != null;
 
                 if (isNullable)
                 {
-                    var nullValue = Expression.Constant(null, fieldType);
-                    return Expression.Equal(memberExp, nullValue);
+                    var nullValue = Expression.Constant(null, leftFieldType);
+                    return Expression.Equal(leftValueExpression, nullValue);
                 }
                 return Expression.Constant(false, typeof(bool));
             }
@@ -246,20 +312,10 @@ namespace Vit.Linq.QueryBuilder
             Expression In()
             {
                 Expression arrayExp = null;
-                #region build arrayExp
-                {
-                    Type valueType = typeof(IEnumerable<>).MakeGenericType(fieldType);
-                    object value = null;
-                    if (rule.value != null)
-                    {
-                        //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);
-                }
-                #endregion
-                var inCheck = Expression.Call(typeof(System.Linq.Enumerable), "Contains", new[] { fieldType }, arrayExp, memberExp);
+                Type valueType = typeof(IEnumerable<>).MakeGenericType(leftFieldType);
+                arrayExp = this.GetRightValueExpression(rule, parameter, valueType);
+
+                var inCheck = Expression.Call(typeof(System.Linq.Enumerable), "Contains", new[] { leftFieldType }, arrayExp, leftValueExpression);
                 return inCheck;
             }
             #endregion
@@ -303,34 +359,6 @@ namespace Vit.Linq.QueryBuilder
 
         }
 
-        #region ConvertToList
-        internal object ConvertToList(object value,IFilterRule rule, Type fieldType)
-        {
-            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, IFilterRule rule)
-        {
-            Type fieldType = typeof(T);
-            var list = new List<T>();
-            foreach (var item in values)
-            {
-                var itemValue = GetRulePrimitiveValue(item, rule, fieldType);
-                T value = (T)Convert.ChangeType(itemValue, fieldType);
-                list.Add(value);
-            }
-            return list;
-        }
-        #endregion
+
     }
 }

+ 1 - 1
src/Vit.Linq/LinqHelp.Reflection.cs

@@ -2,7 +2,7 @@
 using System.Linq.Expressions;
 using System.Reflection;
 
-namespace Vit.Linq.QueryBuilder
+namespace Vit.Linq
 {
     public partial class LinqHelp
     {

+ 37 - 11
src/Vit.Linq/LinqHelp.cs

@@ -1,13 +1,36 @@
 using System;
+using System.Collections;
+using System.Linq;
 using System.Linq.Expressions;
 
-namespace Vit.Linq.QueryBuilder
+namespace Vit.Linq
 {
     public partial class LinqHelp
     {
-
-        public static MemberExpression GetFieldMemberExpression_ByName(Expression parameter, string propertyOrFieldName)
+        /// <summary>
+        /// 
+        /// </summary>
+        /// <param name="parameter"></param>
+        /// <param name="propertyOrFieldName"> could be array index, example:  "2"  "name"</param>
+        /// <returns></returns>
+        public static Expression GetFieldMemberExpression_ByName(Expression parameter, string propertyOrFieldName)
         {
+            var valueType = parameter.Type;
+            int index;
+            if (valueType.IsArray)
+            {
+                // Array
+                if (int.TryParse(propertyOrFieldName, out index))
+                    return Expression.ArrayAccess(parameter, Expression.Constant(index));
+            }
+            else
+            if (valueType.IsGenericType && typeof(IEnumerable).IsAssignableFrom(valueType))
+            {
+                // IEnumerable<>    List<>
+                if (int.TryParse(propertyOrFieldName, out index))
+                    return Expression.Call(typeof(Enumerable), "ElementAt", valueType.GetGenericArguments(), parameter, Expression.Constant(index));
+            }
+
             return Expression.PropertyOrField(parameter, propertyOrFieldName);
         }
 
@@ -15,16 +38,19 @@ namespace Vit.Linq.QueryBuilder
         /// 
         /// </summary>
         /// <param name="parameter"></param>
-        /// <param name="fieldPath"> could be nasted , example: "name"  "depart.name"</param>
+        /// <param name="fieldPath"> could be nasted , example: "name"  "depart.name"  "departs[1].name" "departs.1.name"</param>
         /// <returns></returns>
-        public static MemberExpression GetFieldMemberExpression(ParameterExpression parameter, string fieldPath)
+        public static Expression GetFieldMemberExpression(Expression parameter, string fieldPath)
         {
-            MemberExpression memberExp = null;
-            foreach (var fieldName in fieldPath?.Split('.'))
+            fieldPath = fieldPath?.Replace("]", "").Replace("[", ".");
+            if (!string.IsNullOrWhiteSpace(fieldPath))
             {
-                memberExp = GetFieldMemberExpression_ByName(((Expression)memberExp) ?? parameter, fieldName);
+                foreach (var fieldName in fieldPath.Split('.'))
+                {
+                    parameter = GetFieldMemberExpression_ByName(parameter, fieldName);
+                }
             }
-            return memberExp;
+            return parameter;
         }
 
 
@@ -34,7 +60,7 @@ namespace Vit.Linq.QueryBuilder
         /// <param name="type"></param>
         /// <param name="fieldPath"> could be nasted , example: "name"  "depart.name"</param>
         /// <returns></returns>
-        public static MemberExpression GetFieldMemberExpression(Type type, string fieldPath)
+        public static Expression GetFieldMemberExpression(Type type, string fieldPath)
         {
             return GetFieldMemberExpression(Expression.Parameter(type), fieldPath);
         }
@@ -44,7 +70,7 @@ namespace Vit.Linq.QueryBuilder
         public static Expression<Func<T, object>> GetFieldExpression<T>(string fieldPath)
         {
             var parammeter = Expression.Parameter(typeof(T));
-            MemberExpression memberExp = GetFieldMemberExpression(parammeter, fieldPath);
+            Expression memberExp = GetFieldMemberExpression(parammeter, fieldPath);
             var lambda = Expression.Lambda(memberExp, parammeter).Compile();
             return t => lambda.DynamicInvoke(t);
         }

+ 107 - 0
src/Vit.Linq/MoreFilter/FilterRuleWithMethod.cs

@@ -0,0 +1,107 @@
+using System;
+using System.Collections.Generic;
+using System.Diagnostics.CodeAnalysis;
+using System.Linq.Expressions;
+using System.Linq;
+using Vit.Linq.Filter;
+
+namespace Vit.Linq.MoreFilter
+{
+    internal interface IMethods { }
+
+    [ExcludeFromCodeCoverage]
+    public class FilterRuleWithMethod : FilterRuleWithMethod<FilterRuleWithMethod>
+    {
+        public static bool SupportFieldMethod(IFilterRule filter)
+        {
+            return typeof(IMethods).IsAssignableFrom(filter.GetType());
+        }
+    }
+
+
+
+    /// <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 FilterRuleWithMethod<RuleType> : FilterRuleBase<RuleType>, IMethods
+               where RuleType : IFilterRule
+    {
+        public class Field
+        {
+            public string field { get; set; }
+            public string method { get; set; }
+
+            public object[] methodParameters { get; set; }
+        }
+
+        public virtual List<Field> fields { get; set; }
+
+
+        public virtual Expression GetValueExpression(Expression valueExpression, Field field)
+        {
+            if (!string.IsNullOrWhiteSpace(field.field))
+            {
+                valueExpression = LinqHelp.GetFieldMemberExpression(valueExpression, field.field);
+            }
+
+            if (!string.IsNullOrWhiteSpace(field.method))
+            {
+                var paramCount = field.methodParameters?.Length ?? 0;
+                var valueType = valueExpression.Type;
+                var Method = valueType.GetMethods(System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Instance)
+                    ?.FirstOrDefault(m => m.Name == field.method && m.GetParameters().Length == paramCount);
+                if (Method != null)
+                {
+                    if (paramCount == 0)
+                    {
+                        valueExpression = Expression.Call(valueExpression, Method);
+                    }
+                    else
+                    {
+                        var argExpressions = Method.GetParameters().Select((paras, index) =>
+                        {
+                            var value = GetPrimitiveValue(field.methodParameters[index]);
+                            value = Convert.ChangeType(value, paras.ParameterType);
+                            return Expression.Constant(value);
+                        }).ToList();
+
+                        valueExpression = Expression.Call(valueExpression, Method, argExpressions);
+                    }
+                }
+            }
+            return valueExpression;
+        }
+
+        public override Expression GetLeftValueExpression(Expression valueExpression)
+        {
+            valueExpression = base.GetLeftValueExpression(valueExpression);
+
+            fields?.ForEach(field => { valueExpression = GetValueExpression(valueExpression, field); });
+
+            return valueExpression;
+        }
+
+
+        protected virtual object GetPrimitiveValue(Object value) => value;
+
+
+        /// <summary>
+        /// Gets or sets the value of the filter.
+        /// </summary>
+        /// <value>
+        /// The value.
+        /// </value>
+        public override object value
+        {
+            get => GetPrimitiveValue(_value);
+            set => _value = value;
+        }
+
+        private object _value;
+
+
+
+
+    }
+}

+ 0 - 39
src/Vit.Linq/QueryBuilder/FilterRule.cs

@@ -1,39 +0,0 @@
-using System.Collections.Generic;
-using System.Diagnostics.CodeAnalysis;
-
-namespace Vit.Linq.QueryBuilder
-{
-    /// <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 : 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> rules { get; set; }
-
-
-        /// <summary>
-        /// Gets or sets the value of the filter.
-        /// </summary>
-        /// <value>
-        /// The value.
-        /// </value>
-        public object value { get; set; }
-
-        IEnumerable<IFilterRule> IFilterRule.rules => rules;
-    }
-}

+ 0 - 3
src/Vit.Linq/readme.md

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