동적 구조 임 의 복잡 한 Linq Where 표현 식
Linq 는 C \ # 에서 매우 사용 하기 좋 은 집합 처리 라 이브 러 리 입 니 다. 사용 하면 대량의 냄새 나 고 긴 내장 순환 을 간소화 하여 처리 논 리 를 뚜렷하게 볼 수 있 습 니 다.EF 조회 도 주로 Linq 에 의존한다.그러나 Linq 는 sql 에 비해 단점 이 있 습 니 다. 가장 중요 한 것 은 동적 구조 조회 의 난이도 입 니 다.sql 은 문자열 을 간단하게 연결 하기 만 하면 조작 난이도 가 낮 습 니 다. (물론 오류 도 상당히 쉽 습 니 다) Linq 표현 식 은 강 한 표현 식 트 리 에 대한 의존 으로 인해 동적 구조 조회 표현 식 은 기본적으로 손 으로 쓴 AST (추상 문법 트 리) 에 해당 하기 때문에 난이도 가 급 격 히 증가 했다 고 할 수 있 습 니 다.
AST 는 이미 컴 파일 원리 의 분야 에 들 어 갔 고 컴퓨터 시스템 에 대한 이해 정도 에 대한 수요 가 일반 crud 쓰기 업무 코드 보다 몇 개의 급 이 높 았 으 며 많은 사람들 이 EF 가 좋 지 않다 고 생각 하기 때문에 동적 조 회 를 쓰기 위해 컴 파일 원 리 를 배 워 야 한 다 는 대가 가 매우 높다.나중에 DynamicLinq 와 유사 한 라 이브 러 리 도 표현 식 문자열 로 동적 조 회 를 쓸 수 있 었 다.
학습 정신 에 따라 한동안 연 구 했 고 나의 상상력 범위 내 에서 임의의 복잡 한 Where 표현 식 을 동적 으로 구성 할 수 있 는 보조 클래스 를 썼 다.이 보조 클래스 의 여과 조건 은 JqGrid 의 고급 조회 데이터 구 조 를 사 용 했 습 니 다. 이것 은 제 가 복잡 한 내장 조 회 를 만 들 수 있다 는 것 을 처음 알 고 데 이 터 를 조회 할 때 json 이 쉽게 해석 할 수 있 는 js 표 플러그 인 을 사용 합 니 다.JqGrid 의 고급 조회 에 따라 where 표현 식 을 생 성 할 수 있 습 니 다.
본문
이루어지다
JqGrid 고급 조회 데이터 구조 정의, 역 직렬 화 사용:
1 public class JqGridParameter 2 { 3 ///
4 /// , bool,true 5 /// 6 public string _search { get; set; } 7 ///
8 /// , 9 /// 10 public long Nd { get; set; } 11 ///
12 /// 13 /// 14 public int Rows { get; set; } 15 ///
16 /// 17 /// 18 public int Page { get; set; } 19 ///
20 /// , + + , 。 :id asc,name desc 21 /// 22 public string Sidx { get; set; } 23 ///
24 /// 25 /// 26 public string[][] SIdx => Sidx.Split(", ").Select(s => s.Split(" ")).ToArray(); 27 ///
28 /// :asc、desc 29 /// 30 public string Sord { get; set; } 31 ///
32 /// json 33 /// 34 public string Filters { get; set; } 35 36 ///
37 /// 38 /// 39 public JqGridSearchRuleGroup FilterObject => Filters.IsNullOrWhiteSpace() 40 ? new JqGridSearchRuleGroup { Rules = new[] { new JqGridSearchRule { Op = SearchOper, Data = SearchString, Field = SearchField } } } 41 : JsonSerializer.Deserialize (Filters ?? string.Empty); 42 43 /// 44 /// 45 /// 46 public string SearchField { get; set; } 47 /// 48 /// 49 /// 50 public string SearchString { get; set; } 51 /// 52 /// 53 /// 54 public string SearchOper { get; set; } 55 56 } 57 58 /// 59 /// 60 /// 61 public class JqGridSearchRuleGroup 62 { 63 /// 64 /// :and、or 65 /// 66 public string GroupOp { get; set; } 67 /// 68 /// 69 /// 70 public JqGridSearchRule[] Rules { get; set; } 71 /// 72 /// 73 /// 74 public JqGridSearchRuleGroup[] Groups { get; set; } 75 } 76 77 /// 78 /// 79 /// 80 public class JqGridSearchRule 81 { 82 /// 83 /// 84 /// 85 public string Field { get; set; } 86 /// 87 /// 88 /// 89 public string PascalField => Field?.Length > 0 ? Field.Substring(0, 1).ToUpper() + Field.Substring(1) : Field; 90 /// 91 /// 92 /// 93 public string Op { get; set; } 94 /// 95 /// 96 /// 97 public string Data { get; set; } 98 }
Where , , 。 , :
1 ///
2 /// JqGrid
3 ///
4 public static class JqGridSearchExtensions
5 {
6 // ( ) json
7 //
8 // , json
9 ///
10 /// where , JqGrid
11 ///
12 ///
13 /// JqGrid
14 /// , , ,
15 /// where
16 public static Expressionbool>> BuildWhere(JqGridSearchRuleGroup ruleGroup, IDictionary<string, string> propertyMap)
17 {
18 ParameterExpression parameter = Expression.Parameter(typeof(T), "searchObject");
19
20 return Expression.Lambdabool>>(BuildGroupExpression(ruleGroup, parameter, propertyMap), parameter);
21 }
22
23 ///
24 /// ( )
25 ///
26 ///
27 ///
28 ///
29 ///
30 /// bool
31 private static Expression BuildGroupExpression(JqGridSearchRuleGroup group, ParameterExpression parameter, IDictionary<string, string> propertyMap)
32 {
33 List expressions = new List();
34 foreach (var rule in group.Rules ?? new JqGridSearchRule[0])
35 {
36 expressions.Add(BuildRuleExpression(rule, parameter, propertyMap));
37 }
38
39 foreach (var subGroup in group.Groups ?? new JqGridSearchRuleGroup[0])
40 {
41 expressions.Add(BuildGroupExpression(subGroup, parameter, propertyMap));
42 }
43
44 if (expressions.Count == 0)
45 {
46 throw new InvalidOperationException(" where , 0 。");
47 }
48
49 if (expressions.Count == 1)
50 {
51 return expressions[0];
52 }
53
54 var expression = expressions[0];
55 switch (group.GroupOp)
56 {
57 case "AND":
58 foreach (var exp in expressions.Skip(1))
59 {
60 expression = Expression.AndAlso(expression, exp);
61 }
62 break;
63 case "OR":
64 foreach (var exp in expressions.Skip(1))
65 {
66 expression = Expression.OrElse(expression, exp);
67 }
68 break;
69 default:
70 throw new InvalidOperationException($" {group.GroupOp} ");
71 }
72
73 return expression;
74 }
75
76 private static readonly string[] SpecialRuleOps = {"in", "ni", "nu", "nn"};
77
78 ///
79 ///
80 ///
81 ///
82 ///
83 ///
84 ///
85 /// bool
86 private static Expression BuildRuleExpression(JqGridSearchRule rule, ParameterExpression parameter,
87 IDictionary<string, string> propertyMap)
88 {
89 Expression l;
90
91 string[] names = null;
92 // , , ,
93 if (propertyMap?.ContainsKey(rule.Field) == true)
94 {
95 names = propertyMap[rule.Field].Split('.', StringSplitOptions.RemoveEmptyEntries);
96 l = Expression.Property(parameter, names[0]);
97 foreach (var name in names.Skip(1))
98 {
99 l = Expression.Property(l, name);
100 }
101 }
102 else
103 {
104 l = Expression.Property(parameter, rule.PascalField);
105 }
106
107 Expression r = null; //
108 Expression e; // bool
109
110 // , Contains ,
111 // null,
112 var specialRuleOps = SpecialRuleOps;
113
114 var isNullable = false;
115 var pt = typeof(T);
116 if(names != null)
117 {
118 foreach(var name in names)
119 {
120 pt = pt.GetProperty(name).PropertyType;
121 }
122 }
123 else
124 {
125 pt = pt.GetProperty(rule.PascalField).PropertyType;
126 }
127
128 // ,
129 if (pt.IsDerivedFrom(typeof(Nullable<>)))
130 {
131 isNullable = true;
132 pt = pt.GenericTypeArguments[0];
133 }
134
135 // ( r)
136 if (!specialRuleOps.Contains(rule.Op))
137 {
138 switch (pt)
139 {
140 case Type ct when ct == typeof(bool):
141 r = BuildConstantExpression(rule, bool.Parse);
142 break;
143
144 #region
145
146 case Type ct when ct == typeof(char):
147 r = BuildConstantExpression(rule, str => str[0]);
148 break;
149 case Type ct when ct == typeof(string):
150 r = BuildConstantExpression(rule, str => str);
151 break;
152
153 #endregion
154
155 #region
156
157 case Type ct when ct == typeof(sbyte):
158 r = BuildConstantExpression(rule, sbyte.Parse);
159 break;
160 case Type ct when ct == typeof(short):
161 r = BuildConstantExpression(rule, short.Parse);
162 break;
163 case Type ct when ct == typeof(int):
164 r = BuildConstantExpression(rule, int.Parse);
165 break;
166 case Type ct when ct == typeof(long):
167 r = BuildConstantExpression(rule, long.Parse);
168 break;
169
170 #endregion
171
172 #region
173
174 case Type ct when ct == typeof(byte):
175 r = BuildConstantExpression(rule, byte.Parse);
176 break;
177 case Type ct when ct == typeof(ushort):
178 r = BuildConstantExpression(rule, ushort.Parse);
179 break;
180 case Type ct when ct == typeof(uint):
181 r = BuildConstantExpression(rule, uint.Parse);
182 break;
183 case Type ct when ct == typeof(ulong):
184 r = BuildConstantExpression(rule, ulong.Parse);
185 break;
186
187 #endregion
188
189 #region
190
191 case Type ct when ct == typeof(float):
192 r = BuildConstantExpression(rule, float.Parse);
193 break;
194 case Type ct when ct == typeof(double):
195 r = BuildConstantExpression(rule, double.Parse);
196 break;
197 case Type ct when ct == typeof(decimal):
198 r = BuildConstantExpression(rule, decimal.Parse);
199 break;
200
201 #endregion
202
203 #region
204
205 case Type ct when ct == typeof(DateTime):
206 r = BuildConstantExpression(rule, DateTime.Parse);
207 break;
208 case Type ct when ct == typeof(DateTimeOffset):
209 r = BuildConstantExpression(rule, DateTimeOffset.Parse);
210 break;
211 case Type ct when ct == typeof(Guid):
212 r = BuildConstantExpression(rule, Guid.Parse);
213 break;
214 case Type ct when ct.IsEnum:
215 r = Expression.Constant(rule.Data.ToEnumObject(ct));
216 break;
217
218 #endregion
219
220 default:
221 throw new InvalidOperationException($" {pt.FullName} ");
222 }
223 }
224
225 if (r != null && pt.IsValueType && isNullable)
226 {
227 var gt = typeof(Nullable<>).MakeGenericType(pt);
228 r = Expression.Convert(r, gt);
229 }
230
231 switch (rule.Op)
232 {
233 case "eq": //
234 e = Expression.Equal(l, r);
235 break;
236 case "ne": //
237 e = Expression.NotEqual(l, r);
238 break;
239 case "lt": //
240 e = Expression.LessThan(l, r);
241 break;
242 case "le": //
243 e = Expression.LessThanOrEqual(l, r);
244 break;
245 case "gt": //
246 e = Expression.GreaterThan(l, r);
247 break;
248 case "ge": //
249 e = Expression.GreaterThanOrEqual(l, r);
250 break;
251 case "bw": // ( )
252 if (pt == typeof(string))
253 {
254 e = Expression.Call(l, pt.GetMethod(nameof(string.StartsWith), new[] {typeof(string)}), r);
255 }
256 else
257 {
258 throw new InvalidOperationException($" {pt.FullName} ");
259 }
260
261 break;
262 case "bn": // ( )
263 if (pt == typeof(string))
264 {
265 e = Expression.Not(Expression.Call(l, pt.GetMethod(nameof(string.StartsWith), new[] {typeof(string)}), r));
266 }
267 else
268 {
269 throw new InvalidOperationException($" {pt.FullName} ");
270 }
271
272 break;
273 case "ew": // ( )
274 if (pt == typeof(string))
275 {
276 e = Expression.Call(l, pt.GetMethod(nameof(string.EndsWith), new[] {typeof(string)}), r);
277 }
278 else
279 {
280 throw new InvalidOperationException($" {pt.FullName} ");
281 }
282
283 break;
284 case "en": // ( )
285 if (pt == typeof(string))
286 {
287 e = Expression.Not(Expression.Call(l, pt.GetMethod(nameof(string.EndsWith), new[] {typeof(string)}), r));
288 }
289 else
290 {
291 throw new InvalidOperationException($" {pt.FullName} ");
292 }
293
294 break;
295 case "cn": // ( )
296 if (pt == typeof(string))
297 {
298 e = Expression.Call(l, pt.GetMethod(nameof(string.Contains), new[] {typeof(string)}), r);
299 }
300 else
301 {
302 throw new InvalidOperationException($" {pt.FullName} ");
303 }
304
305 break;
306 case "nc": // ( )
307 if (pt == typeof(string))
308 {
309 e = Expression.Not(Expression.Call(l, pt.GetMethod(nameof(string.Contains), new[] {typeof(string)}), r));
310 }
311 else
312 {
313 throw new InvalidOperationException($" {pt.FullName} ");
314 }
315
316 break;
317 case "in": // ( )
318 e = BuildContainsExpression(rule, l, pt);
319 break;
320 case "ni": // ( )
321 e = Expression.Not(BuildContainsExpression(rule, l, pt));
322 break;
323 case "nu": //
324 r = Expression.Constant(null);
325 e = Expression.Equal(l, r);
326 break;
327 case "nn": //
328 r = Expression.Constant(null);
329 e = Expression.Not(Expression.Equal(l, r));
330 break;
331 case "bt": //
332 throw new NotImplementedException($" {rule.Op} ");
333 default:
334 throw new InvalidOperationException($" {rule.Op} ");
335 }
336
337 return e;
338
339 static Expression BuildConstantExpression(JqGridSearchRule jRule, Func<string, TValue> valueConvertor)
340 {
341 var rv = valueConvertor(jRule.Data);
342 return Expression.Constant(rv);
343 }
344 }
345
346 ///
347 /// Contains
348 ///
349 ///
350 ///
351 ///
352 /// Contains
353 private static Expression BuildContainsExpression(JqGridSearchRule rule, Expression parameter, Type parameterType)
354 {
355 Expression e = null;
356
357 var genMethod = typeof(Queryable).GetMethods()
358 .Single(m => m.Name == nameof(Queryable.Contains) && m.GetParameters().Length == 2);
359
360 var jsonArray = JsonSerializer.Deserialize<string[]>(rule.Data);
361
362 switch (parameterType)
363 {
364 #region
365
366 case Type ct when ct == typeof(char):
367 if (jsonArray.Any(o => o.Length != 1)) {throw new InvalidOperationException(" ");}
368 e = CallContains(parameter, jsonArray, str => str[0], genMethod, ct);
369 break;
370 case Type ct when ct == typeof(string):
371 e = CallContains(parameter, jsonArray, str => str, genMethod, ct);
372 break;
373
374 #endregion
375
376 #region
377
378 case Type ct when ct == typeof(sbyte):
379 e = CallContains(parameter, jsonArray, sbyte.Parse, genMethod, ct);
380 break;
381 case Type ct when ct == typeof(short):
382 e = CallContains(parameter, jsonArray, short.Parse, genMethod, ct);
383 break;
384 case Type ct when ct == typeof(int):
385 e = CallContains(parameter, jsonArray, int.Parse, genMethod, ct);
386 break;
387 case Type ct when ct == typeof(long):
388 e = CallContains(parameter, jsonArray, long.Parse, genMethod, ct);
389 break;
390
391 #endregion
392
393 #region
394
395 case Type ct when ct == typeof(byte):
396 e = CallContains(parameter, jsonArray, byte.Parse, genMethod, ct);
397 break;
398 case Type ct when ct == typeof(ushort):
399 e = CallContains(parameter, jsonArray, ushort.Parse, genMethod, ct);
400 break;
401 case Type ct when ct == typeof(uint):
402 e = CallContains(parameter, jsonArray, uint.Parse, genMethod, ct);
403 break;
404 case Type ct when ct == typeof(ulong):
405 e = CallContains(parameter, jsonArray, ulong.Parse, genMethod, ct);
406 break;
407
408 #endregion
409
410 #region
411
412 case Type ct when ct == typeof(float):
413 e = CallContains(parameter, jsonArray, float.Parse, genMethod, ct);
414 break;
415 case Type ct when ct == typeof(double):
416 e = CallContains(parameter, jsonArray, double.Parse, genMethod, ct);
417 break;
418 case Type ct when ct == typeof(decimal):
419 e = CallContains(parameter, jsonArray, decimal.Parse, genMethod, ct);
420 break;
421
422 #endregion
423
424 #region
425
426 case Type ct when ct == typeof(DateTime):
427 e = CallContains(parameter, jsonArray, DateTime.Parse, genMethod, ct);
428 break;
429 case Type ct when ct == typeof(DateTimeOffset):
430 e = CallContains(parameter, jsonArray, DateTimeOffset.Parse, genMethod, ct);
431 break;
432 case Type ct when ct == typeof(Guid):
433 e = CallContains(parameter, jsonArray, Guid.Parse, genMethod, ct);
434 break;
435 case Type ct when ct.IsEnum:
436 e = CallContains(Expression.Convert(parameter, typeof(object)), jsonArray, enumString => enumString.ToEnumObject(ct), genMethod, ct);
437 break;
438
439 #endregion
440 }
441
442 return e;
443
444 static MethodCallExpression CallContains(Expression pa, string[] jArray, Func<string, T> selector, MethodInfo genericMethod, Type type)
445 {
446 var data = jArray.Select(selector).ToArray().AsQueryable();
447 var method = genericMethod.MakeGenericMethod(type);
448
449 return Expression.Call(null, method, new[] { Expression.Constant(data), pa });
450 }
451 }
452 }
Razor Page , , GitHub :
1 public async Task OnGetUserListAsync([FromQuery]JqGridParameter jqGridParameter)
2 {
3 var usersQuery = _userManager.Users.AsNoTracking();
4 if (jqGridParameter._search == "true")
5 {
6 usersQuery = usersQuery.Where(BuildWhere(jqGridParameter.FilterObject, null));
7 }
8
9 var users = usersQuery.Include(u => u.UserRoles).ThenInclude(ur => ur.Role).OrderBy(u => u.InsertOrder)
10 .Skip((jqGridParameter.Page - 1) * jqGridParameter.Rows).Take(jqGridParameter.Rows).ToList();
11 var userCount = usersQuery.Count();
12 var pageCount = Ceiling((double) userCount / jqGridParameter.Rows);
13 return new JsonResult(
14 new
15 {
16 rows //
17 = users.Select(u => new
18 {
19 u.UserName,
20 u.Gender,
21 u.Email,
22 u.PhoneNumber,
23 u.EmailConfirmed,
24 u.PhoneNumberConfirmed,
25 u.CreationTime,
26 u.CreatorId,
27 u.Active,
28 u.LastModificationTime,
29 u.LastModifierId,
30 u.InsertOrder,
31 u.ConcurrencyStamp,
32 // JqGrid
33 u.Id // , , ,
34 }),
35 total = pageCount, //
36 page = jqGridParameter.Page, //
37 records = userCount //
38 }
39 );
40 }
프로젝트 를 시작 하면 / Identity / manage / Users / Index 에 접근 하여 사용 할 수 있 습 니 다.
결어
이번 실천 을 통 해 표현 식 트 리 에 관 한 지식 을 깊이 알 게 되 었 습 니 다. 표현 식 트 리 는 컴 파일 절차 에서 고급 구조 라 고 할 수 있 습 니 다. 간식 을 견 딜 수 있 는 지 알 수 있 습 니 다. IL 이 야 말로 정말 어 지 럽 고 원생 어 셈 블 리 보다 별로 좋 지 않 습 니 다.C \ # 정말 재 미 있 습 니 다. 입문 은 간단 하지만 내 부 는 아주 깊 습 니 다. 소 백 과 대신 에 게 는 완전히 두 가지 언어 입 니 다.자바 는 자바 8 시 에 Stream 과 Lambda 표현 식 기능 을 추 가 했 습 니 다. 딱 봐 도 Linq 를 표시 하 는 것 같 습 니 다. 하지만 그 이름 은 정말 한 마디 로 말 하기 어렵 습 니 다. 코드 를 쓰 는 것 을 보 니 목 에 가시 가 걸 린 것 같 습 니 다. 상당히 불쾌 합 니 다.... 때문에 Stream 시스템 에 표현 식 트 리 가 부족 합 니 다. 이러한 동적 구조 조회 식 기능 은 처음부터 지원 되 지 않 습 니 다.게다가 자바 에는 익명 형식 이 없고 대상 초기 화기 가 없 으 며 매번 사용 합 니 다. Stream 은 힘 든 부분 입 니 다. 중간 과정의 데이터 구조 도 전문 적 으로 종 류 를 써 야 합 니 다. 모든 중간 류 는 하나의 파일 을 독점 해 야 하기 때문에 정말 어 지 럽 습 니 다.베껴 도 불합격 이 야!
C \ # var 키 워드 를 도입 하 는 핵심 은 익명 형식 을 위 한 서비스 입 니 다. 컴 파일 러 가 자동 으로 생 성 되 는 유형 이기 때문에 코드 를 쓸 때 이름 이 전혀 없습니다. var 를 사용 하지 않 고 무엇 을 사용 합 니까?변 수 를 간소화 하고 코드 를 초기 화 하 는 것 은 순 조로 운 것 입 니 다.그 결과 자바 가 반 을 베 꼈 는 지 가장 중요 하지 않 은 반 을 베 꼈 는 지 변 수 를 간소화 하여 코드 를 초기 화 했다.자바 하 는 놈 들 이 무슨 생각 을 하 는 지 모 르 겠 어.
전 재 는 다음 과 같은 내용 을 완전 하 게 보류 하고 눈 에 띄 는 위치 에 표시 하 십시오. 권한 을 위 임 받 지 않 고 다음 과 같은 내용 을 삭제 하여 전재 도용 을 할 경우 법률 적 책임 을 추궁 할 권 리 를 보류 합 니 다!
본문 주소:https://www.cnblogs.com/coredx/p/12423929.html
전체 소스 코드: Github
안에 여러 가지 작은 것들 이 있 는데, 이것 은 그 중의 하나 일 뿐, 싫어 하지 않 는 다 면 스타 해 볼 수 있다.
이 내용에 흥미가 있습니까?
현재 기사가 여러분의 문제를 해결하지 못하는 경우 AI 엔진은 머신러닝 분석(스마트 모델이 방금 만들어져 부정확한 경우가 있을 수 있음)을 통해 가장 유사한 기사를 추천합니다:
다양한 언어의 JSONJSON은 Javascript 표기법을 사용하여 데이터 구조를 레이아웃하는 데이터 형식입니다. 그러나 Javascript가 코드에서 이러한 구조를 나타낼 수 있는 유일한 언어는 아닙니다. 저는 일반적으로 '객체'{}...
텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
CC BY-SA 2.5, CC BY-SA 3.0 및 CC BY-SA 4.0에 따라 라이센스가 부여됩니다.