mirror of
https://github.com/nsnail/FreeSql.git
synced 2025-04-22 02:32:50 +08:00
- 增加 DynamicFilter Custom 自定义解析;
This commit is contained in:
parent
59f14fcd13
commit
8a83fea60a
@ -40,7 +40,7 @@ namespace FreeSql
|
|||||||
{
|
{
|
||||||
_dbsetPriv?.Dispose();
|
_dbsetPriv?.Dispose();
|
||||||
_dbPriv?.Dispose();
|
_dbPriv?.Dispose();
|
||||||
this.DataFilter.Dispose();
|
this.DataFilter?.Dispose();
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
|
@ -1,11 +1,13 @@
|
|||||||
using FreeSql.DataAnnotations;
|
using FreeSql.DataAnnotations;
|
||||||
using FreeSql.Internal.Model;
|
using FreeSql.Internal.Model;
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
|
using Newtonsoft.Json.Linq;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using System.Text;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
|
||||||
namespace FreeSql.Tests.Sqlite
|
namespace FreeSql.Tests.Sqlite
|
||||||
@ -1241,7 +1243,9 @@ WHERE (((cast(a.""Id"" as character)) in (SELECT b.""Title""
|
|||||||
.Where(a => a.model2id <= model1.id)
|
.Where(a => a.model2id <= model1.id)
|
||||||
.ToList(a => new
|
.ToList(a => new
|
||||||
{
|
{
|
||||||
a.model2id, a.childs, childs2 = a.childs
|
a.model2id,
|
||||||
|
a.childs,
|
||||||
|
childs2 = a.childs
|
||||||
});
|
});
|
||||||
|
|
||||||
var at1 = g.sqlite.Select<TestInclude_OneToManyModel1>()
|
var at1 = g.sqlite.Select<TestInclude_OneToManyModel1>()
|
||||||
@ -1253,7 +1257,9 @@ WHERE (((cast(a.""Id"" as character)) in (SELECT b.""Title""
|
|||||||
.Where(a => a.id <= model1.id)
|
.Where(a => a.id <= model1.id)
|
||||||
.ToList(a => new
|
.ToList(a => new
|
||||||
{
|
{
|
||||||
a.id, a.model2.childs, childs2 = a.model2.childs
|
a.id,
|
||||||
|
a.model2.childs,
|
||||||
|
childs2 = a.model2.childs
|
||||||
});
|
});
|
||||||
|
|
||||||
var at2 = g.sqlite.Select<TestInclude_OneToManyModel1>()
|
var at2 = g.sqlite.Select<TestInclude_OneToManyModel1>()
|
||||||
@ -1267,7 +1273,9 @@ WHERE (((cast(a.""Id"" as character)) in (SELECT b.""Title""
|
|||||||
.Where(a => a.id <= model1.id)
|
.Where(a => a.id <= model1.id)
|
||||||
.ToList(a => new
|
.ToList(a => new
|
||||||
{
|
{
|
||||||
a.id, a.model2.childs, childs2 = a.model2.childs
|
a.id,
|
||||||
|
a.model2.childs,
|
||||||
|
childs2 = a.model2.childs
|
||||||
});
|
});
|
||||||
|
|
||||||
var at00 = g.sqlite.Select<TestInclude_OneToManyModel2>()
|
var at00 = g.sqlite.Select<TestInclude_OneToManyModel2>()
|
||||||
@ -1279,7 +1287,9 @@ WHERE (((cast(a.""Id"" as character)) in (SELECT b.""Title""
|
|||||||
.Where(a => a.model2id <= model1.id)
|
.Where(a => a.model2id <= model1.id)
|
||||||
.ToList(a => new
|
.ToList(a => new
|
||||||
{
|
{
|
||||||
a.model2id, a.childs, childs2 = a.childs
|
a.model2id,
|
||||||
|
a.childs,
|
||||||
|
childs2 = a.childs
|
||||||
});
|
});
|
||||||
|
|
||||||
var at11 = g.sqlite.Select<TestInclude_OneToManyModel1>()
|
var at11 = g.sqlite.Select<TestInclude_OneToManyModel1>()
|
||||||
@ -1291,7 +1301,9 @@ WHERE (((cast(a.""Id"" as character)) in (SELECT b.""Title""
|
|||||||
.Where(a => a.id <= model1.id)
|
.Where(a => a.id <= model1.id)
|
||||||
.ToList(a => new
|
.ToList(a => new
|
||||||
{
|
{
|
||||||
a.id, a.model2.childs, childs2 = a.model2.childs
|
a.id,
|
||||||
|
a.model2.childs,
|
||||||
|
childs2 = a.model2.childs
|
||||||
});
|
});
|
||||||
|
|
||||||
var at22 = g.sqlite.Select<TestInclude_OneToManyModel1>()
|
var at22 = g.sqlite.Select<TestInclude_OneToManyModel1>()
|
||||||
@ -1305,7 +1317,9 @@ WHERE (((cast(a.""Id"" as character)) in (SELECT b.""Title""
|
|||||||
.Where(a => a.id <= model1.id)
|
.Where(a => a.id <= model1.id)
|
||||||
.ToList(a => new
|
.ToList(a => new
|
||||||
{
|
{
|
||||||
a.id, a.model2.childs, childs2 = a.model2.childs
|
a.id,
|
||||||
|
a.model2.childs,
|
||||||
|
childs2 = a.model2.childs
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1724,7 +1738,9 @@ WHERE (((cast(a.""Id"" as character)) in (SELECT b.""Title""
|
|||||||
.Where(a => a.Id == song1.Id || a.Id == song2.Id || a.Id == song3.Id)
|
.Where(a => a.Id == song1.Id || a.Id == song2.Id || a.Id == song3.Id)
|
||||||
.ToList(a => new
|
.ToList(a => new
|
||||||
{
|
{
|
||||||
a.Id, a.Is_deleted, a.Tags
|
a.Id,
|
||||||
|
a.Is_deleted,
|
||||||
|
a.Tags
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2481,6 +2497,33 @@ ORDER BY a__Parent.""Name""", sql);
|
|||||||
Assert.Equal(@"SELECT a.""id"", a.""name"", a.""no"", a.""status""
|
Assert.Equal(@"SELECT a.""id"", a.""name"", a.""no"", a.""status""
|
||||||
FROM ""ts_dyfilter_enum01"" a
|
FROM ""ts_dyfilter_enum01"" a
|
||||||
WHERE ((not((a.""name"") LIKE '%testname01') OR not((a.""no"") LIKE '%testname01') OR not((a.""id"") LIKE '%testname01') OR a.""status"" = 2))", sql);
|
WHERE ((not((a.""name"") LIKE '%testname01') OR not((a.""no"") LIKE '%testname01') OR not((a.""id"") LIKE '%testname01') OR a.""status"" = 2))", sql);
|
||||||
|
|
||||||
|
sql = fsql.Select<ts_dyfilter_enum01>().WhereDynamicFilter(JsonConvert.DeserializeObject<DynamicFilterInfo>(@"
|
||||||
|
{
|
||||||
|
""Logic"" : ""Or"",
|
||||||
|
""Filters"" :
|
||||||
|
[
|
||||||
|
{
|
||||||
|
""Field"" : ""MyRawSql FreeSql.Tests.Sqlite.DynamicFilterMyCustom,FreeSql.Tests"",
|
||||||
|
""Operator"" : ""Custom"",
|
||||||
|
""Value"" : ""(name,no) in (('testname01','testname01'))""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
""Field"" : ""no"",
|
||||||
|
""Operator"" : ""NotEndsWith"",
|
||||||
|
""Value"" : ""testname01""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
""Field"" : ""MyRawSql FreeSql.Tests.Sqlite.DynamicFilterMyCustom,FreeSql.Tests"",
|
||||||
|
""Operator"" : ""Custom"",
|
||||||
|
""Value"" : ""(id,status) in (('testname01','finished'))""
|
||||||
|
},
|
||||||
|
]
|
||||||
|
}
|
||||||
|
")).ToSql();
|
||||||
|
Assert.Equal(@"SELECT a.""id"", a.""name"", a.""no"", a.""status""
|
||||||
|
FROM ""ts_dyfilter_enum01"" a
|
||||||
|
WHERE (((name,no) in (('testname01','testname01')) OR not((a.""no"") LIKE '%testname01') OR (id,status) in (('testname01','finished'))))", sql);
|
||||||
}
|
}
|
||||||
|
|
||||||
class ts_dyfilter_enum01
|
class ts_dyfilter_enum01
|
||||||
@ -2492,4 +2535,36 @@ WHERE ((not((a.""name"") LIKE '%testname01') OR not((a.""no"") LIKE '%testname01
|
|||||||
}
|
}
|
||||||
public enum ts_dyfilter_enum01_status { staring, stoped, finished }
|
public enum ts_dyfilter_enum01_status { staring, stoped, finished }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public class DynamicFilterMyCustom
|
||||||
|
{
|
||||||
|
public static string MyRawSql(string value) => value;
|
||||||
|
|
||||||
|
public static string TupleIn(string value)
|
||||||
|
{
|
||||||
|
var sb = new StringBuilder();
|
||||||
|
var array = JArray.Parse(value);
|
||||||
|
var row = 0;
|
||||||
|
foreach(JObject item in array)
|
||||||
|
{
|
||||||
|
if (row++ == 0)
|
||||||
|
{
|
||||||
|
sb.Append("(");
|
||||||
|
var propNames = item.Properties().Select(a => a.Name);
|
||||||
|
sb.Append(string.Join(", ", propNames));
|
||||||
|
sb.Append(") IN (");
|
||||||
|
}
|
||||||
|
if (row == array.Count - 1)
|
||||||
|
{
|
||||||
|
sb.Append(")");
|
||||||
|
}
|
||||||
|
if (row > 1) sb.Append(", ");
|
||||||
|
sb.Append("(");
|
||||||
|
var propValues = item.Values().Select(a => a?.ToString().Replace("'", "''")); //注入处理
|
||||||
|
sb.Append(string.Join(", ", propValues));
|
||||||
|
sb.Append(")");
|
||||||
|
}
|
||||||
|
return sb.ToString();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -140,6 +140,17 @@ namespace FreeSql
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 注意:使用者自己承担【注入风险】
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="sql"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
static bool InternalRawSql([RawValue] string sql)
|
||||||
|
{
|
||||||
|
expContext.Value.Result = sql;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
#region 大小判断
|
#region 大小判断
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 大于 >
|
/// 大于 >
|
||||||
|
@ -1202,6 +1202,13 @@
|
|||||||
<param name="column"></param>
|
<param name="column"></param>
|
||||||
<returns></returns>
|
<returns></returns>
|
||||||
</member>
|
</member>
|
||||||
|
<member name="M:FreeSql.SqlExt.InternalRawSql(System.String)">
|
||||||
|
<summary>
|
||||||
|
注意:使用者自己承担【注入风险】
|
||||||
|
</summary>
|
||||||
|
<param name="sql"></param>
|
||||||
|
<returns></returns>
|
||||||
|
</member>
|
||||||
<member name="M:FreeSql.SqlExt.GreaterThan``1(``0,``0)">
|
<member name="M:FreeSql.SqlExt.GreaterThan``1(``0,``0)">
|
||||||
<summary>
|
<summary>
|
||||||
大于 >
|
大于 >
|
||||||
@ -4167,6 +4174,21 @@
|
|||||||
此时 Value 的值格式为逗号分割:value1,value2,value3... 或者数组
|
此时 Value 的值格式为逗号分割:value1,value2,value3... 或者数组
|
||||||
</summary>
|
</summary>
|
||||||
</member>
|
</member>
|
||||||
|
<member name="F:FreeSql.Internal.Model.DynamicFilterOperator.Custom">
|
||||||
|
<summary>
|
||||||
|
自定义解析,此时 Field 为反射信息,Value 为静态方法的参数(string)<para></para>
|
||||||
|
示范:{ Operator: "Custom", Field: "RawSql webapp1.DynamicFilterCustom,webapp1", Value: "(id,name) in ((1,'k'),(2,'m'))" }<para></para>
|
||||||
|
注意:使用者自己承担【注入风险】<para></para>
|
||||||
|
静态方法定义示范:<para></para>
|
||||||
|
namespace webapp1<para></para>
|
||||||
|
{<para></para>
|
||||||
|
public class DynamicFilterCustom<para></para>
|
||||||
|
{<para></para>
|
||||||
|
public static string RawSql(string value) => value;<para></para>
|
||||||
|
}<para></para>
|
||||||
|
}<para></para>
|
||||||
|
</summary>
|
||||||
|
</member>
|
||||||
<member name="P:FreeSql.Internal.Model.FetchCallbackArgs`1.IsBreak">
|
<member name="P:FreeSql.Internal.Model.FetchCallbackArgs`1.IsBreak">
|
||||||
<summary>
|
<summary>
|
||||||
是否放弃继续读取
|
是否放弃继续读取
|
||||||
|
@ -544,17 +544,34 @@ namespace FreeSql.Internal.CommonProvider
|
|||||||
{
|
{
|
||||||
if (string.IsNullOrEmpty(fi.Field) == false)
|
if (string.IsNullOrEmpty(fi.Field) == false)
|
||||||
{
|
{
|
||||||
Expression exp = ConvertStringPropertyToExpression(fi.Field);
|
Expression exp = null;
|
||||||
switch (fi.Operator)
|
switch (fi.Operator)
|
||||||
{
|
{
|
||||||
|
case DynamicFilterOperator.Custom:
|
||||||
|
var fiValueCustomArray = fi.Field?.ToString().Split(new[] { ' ' }, 2);
|
||||||
|
if (fiValueCustomArray.Length != 2) throw new ArgumentException("Custom 要求 Field 应该空格分割,并且长度为 2,格式:{静态方法名}{空格}{反射信息}");
|
||||||
|
if (string.IsNullOrWhiteSpace(fiValueCustomArray[0])) throw new ArgumentException("Custom {静态方法名}不能为空,格式:{静态方法名}{空格}{反射信息}");
|
||||||
|
if (string.IsNullOrWhiteSpace(fiValueCustomArray[1])) throw new ArgumentException("Custom {反射信息}不能为空,格式:{静态方法名}{空格}{反射信息}");
|
||||||
|
var fiValue1Type = Type.GetType(fiValueCustomArray[1]);
|
||||||
|
if (fiValue1Type == null) throw new ArgumentException($"Custom 找到对应的{{反射信息}}:{fiValueCustomArray[1]}");
|
||||||
|
var fiValue0Method = fiValue1Type.GetMethod(fiValueCustomArray[0], new Type[] { typeof(string) });
|
||||||
|
if (fiValue0Method == null) throw new ArgumentException($"Custom 找到对应的{{静态方法名}}:{fiValueCustomArray[0]}");
|
||||||
|
var fiValue0MethodReturn = fiValue0Method?.Invoke(null, new object[] { fi.Value?.ToString() })?.ToString();
|
||||||
|
exp = Expression.Call(typeof(SqlExt).GetMethod("InternalRawSql", BindingFlags.NonPublic | BindingFlags.Static), Expression.Constant(fiValue0MethodReturn, typeof(string)));
|
||||||
|
break;
|
||||||
|
|
||||||
case DynamicFilterOperator.Contains:
|
case DynamicFilterOperator.Contains:
|
||||||
case DynamicFilterOperator.StartsWith:
|
case DynamicFilterOperator.StartsWith:
|
||||||
case DynamicFilterOperator.EndsWith:
|
case DynamicFilterOperator.EndsWith:
|
||||||
case DynamicFilterOperator.NotContains:
|
case DynamicFilterOperator.NotContains:
|
||||||
case DynamicFilterOperator.NotStartsWith:
|
case DynamicFilterOperator.NotStartsWith:
|
||||||
case DynamicFilterOperator.NotEndsWith:
|
case DynamicFilterOperator.NotEndsWith:
|
||||||
|
exp = ConvertStringPropertyToExpression(fi.Field);
|
||||||
if (exp.Type != typeof(string)) exp = Expression.TypeAs(exp, typeof(string));
|
if (exp.Type != typeof(string)) exp = Expression.TypeAs(exp, typeof(string));
|
||||||
break;
|
break;
|
||||||
|
default:
|
||||||
|
exp = ConvertStringPropertyToExpression(fi.Field);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
switch (fi.Operator)
|
switch (fi.Operator)
|
||||||
{
|
{
|
||||||
|
@ -120,6 +120,21 @@ namespace FreeSql.Internal.Model
|
|||||||
/// not in (1,2,3)<para></para>
|
/// not in (1,2,3)<para></para>
|
||||||
/// 此时 Value 的值格式为逗号分割:value1,value2,value3... 或者数组
|
/// 此时 Value 的值格式为逗号分割:value1,value2,value3... 或者数组
|
||||||
/// </summary>
|
/// </summary>
|
||||||
NotAny
|
NotAny,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 自定义解析,此时 Field 为反射信息,Value 为静态方法的参数(string)<para></para>
|
||||||
|
/// 示范:{ Operator: "Custom", Field: "RawSql webapp1.DynamicFilterCustom,webapp1", Value: "(id,name) in ((1,'k'),(2,'m'))" }<para></para>
|
||||||
|
/// 注意:使用者自己承担【注入风险】<para></para>
|
||||||
|
/// 静态方法定义示范:<para></para>
|
||||||
|
/// namespace webapp1<para></para>
|
||||||
|
/// {<para></para>
|
||||||
|
/// public class DynamicFilterCustom<para></para>
|
||||||
|
/// {<para></para>
|
||||||
|
/// public static string RawSql(string value) => value;<para></para>
|
||||||
|
/// }<para></para>
|
||||||
|
/// }<para></para>
|
||||||
|
/// </summary>
|
||||||
|
Custom
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user