.net core 实例教程(四)Ef Core实现动态查询及排序条件拼接

本文源码下载地址:http://www.80cxy.com/Blog/ResourceView?arId=202403191532545995NAAqJh

系列教程地址:http://www.80cxy.com/Blog/ArticleView?arId=202403191517574161ay3s5V

本文实现动态查询拼接功能,前台传入查询条件参数格式如下:

wheres: '[{\"name\":\"Name\",\"value\":\"11\",\"option\":\"EQ\"},{\"name\":\"IdCard\",\"value\":\"22\",\"option\":\"EQ\"}]',

name为查询字段名,value为查询值,option为查询条件。

前台传入排序条件参数为:字符串sort:排序字段, 字符串order:排序方式。

后台接收到查询条件参数后先转换为List,然后通过DataFilterConvertor类构造Lambda表达式树构造查询条件。

将排序字段转换为Dictionary,让后通过DataOrderConvertor类构造Lambda表达式树构造排序条件

一、相关代码如下:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

112

113

114

115

116

117

118

119

120

121

122

123

124

125

126

127

128

129

130

131

132

133

134

135

136

137

138

139

140

141

142

143

144

145

146

147

148

149

150

151

152

153

154

155

156

157

158

159

160

161

162

163

164

165

166

167

168

169

170

171

172

173

174

175

176

177

178

179

180

181

182

183

184

185

186

187

188

189

190

191

192

193

194

195

196

197

198

199

200

201

202

203

204

205

206

207

208

209

210

211

212

213

214

215

216

217

218

219

220

221

222

223

224

225

226

227

228

229

230

231

232

233

234

235

236

237

238

239

240

241

242

243

244

245

246

247

248

249

250

251

252

253

254

255

256

257

258

259

260

261

262

263

264

265

266

267

268

269

270

271

272

273

274

275

276

277

278

279

280

281

282

283

284

285

286

287

288

289

290

291

292

293

294

295

296

297

298

299

300

301

302

303

304

305

306

307

308

309

310

311

312

313

314

315

316

317

318

319

320

321

322

323

324

325

326

327

328

329

330

331

332

333

334

335

336

337

338

339

340

341

342

343

344

345

346

347

348

349

350

351

352

353

354

355

356

357

358

359

360

361

362

363

364

365

366

367

368

369

370

371

372

373

374

375

376

377

378

379

380

381

382

383

384

385

386

387

388

389

390

391

392

393

394

395

396

397

398

399

400

401

402

403

404

405

406

407

408

409

410

411

412

413

414

415

416

417

418

419

420

421

422

423

424

425

426

427

428

429

430

431

432

433

434

435

436

437

438

439

440

441

442

443

444

445

446

447

448

449

450

451

452

453

454

455

456

457

458

459

460

461

462

463

464

465

466

467

468

469

470

471

472

473

474

475

476

477

478

479

480

481

482

483

484

485

486

487

488

489

490

491

492

493

494

495

496

497

498

499

500

using SignUp.Common.Enum;

using SignUp.Common.Extensions;

namespace SignUp.Common.DataFilter

{

    /// <summary>

    /// 参数转换

    /// </summary>

    public static class FormatParameters

    {

        public static List<DataFilter> GetSearchParameters(string wheres)

        {

            List<DataFilter> searchParametersList = new List<DataFilter>();

            if (!string.IsNullOrEmpty(wheres))

            {

                try

                {

                    searchParametersList = wheres.DeserializeObject<List<DataFilter>>();

                }

                catch { }

            }

            return searchParametersList;

        }

        public static Dictionary<string, QueryOrderBy> GetOrderParameters(string sort, string order)

        {

            Dictionary<string, QueryOrderBy> orderBy = new Dictionary<string, QueryOrderBy>();

            if (order.ToLower() == "desc")

                orderBy.Add(sort, QueryOrderBy.Desc);

            else

                orderBy.Add(sort, QueryOrderBy.Asc);

            return orderBy;

        }

    }

}

using SignUp.Common.Enum;

using System.Linq.Expressions;

namespace SignUp.Common.DataFilter

{

    /// <summary>

    /// 前端查询条件转换为Expression供EFCore使用

    /// </summary>

    /// <typeparam name="T"></typeparam>

    public static class DataFilterConvertor<T> where T : class

    {

        #region 查询条件

        /// <summary>

        /// 入口方法,把数据过滤器转换为Expression

        /// </summary>

        /// <param name="dataFilterList"></param>

        /// <returns></returns>

        public static Expression<Func<T, bool>> ToExpression(List<DataFilter> dataFilterList)

        {

            if (dataFilterList == null || dataFilterList.Count < 1)

            {

                return (T) => true;

            }

            ParameterExpression parameterExpression = Expression.Parameter(typeof(T));

            Expression firstLambdaExpression = GetSingleFilterExpression(parameterExpression, dataFilterList[0]);

            Expression binaryExpression = Expression.AndAlso(Expression.Constant(true), firstLambdaExpression);

            foreach (DataFilter dataFilter in dataFilterList.Skip(1))

            {

                Expression lambdaExpression = GetSingleFilterExpression(parameterExpression, dataFilter);

                binaryExpression = Expression.AndAlso(binaryExpression, lambdaExpression);

            }

            return Expression.Lambda<Func<T, bool>>(binaryExpression, parameterExpression);

        }

        /// <summary>

        /// 获取单个过滤条件的Expression

        /// </summary>

        /// <param name="parameterExpression"></param>

        /// <param name="dataFilter"></param>

        /// <returns></returns>

        private static Expression GetSingleFilterExpression(ParameterExpression parameterExpression, DataFilter dataFilter)

        {

            MemberExpression? memberExpression = null;

            if (dataFilter.Name.Contains('.'))

            {

                string[] multiLevelProperties = dataFilter.Name.Split('.');

                memberExpression = Expression.Property(parameterExpression, multiLevelProperties[0].ToString());

                foreach (string propertyName in multiLevelProperties.Skip(1))

                {

                    memberExpression = Expression.Property(memberExpression, propertyName);

                }

            }

            else

            {

                memberExpression = Expression.Property(parameterExpression, dataFilter.Name);

            }

            ConstantExpression? constantExpression = null;

            if (!string.IsNullOrEmpty(dataFilter.Value))

            {

                string fieldsType = "";

                if (memberExpression.Type.FullName.IndexOf("DateTime") != -1) fieldsType = "DateTime";

                else if (memberExpression.Type.FullName.IndexOf("System.Guid") != -1) fieldsType = "Guid";

                else fieldsType = memberExpression.Type.Name;

                dynamic formatedValue = DataValueFormat(dataFilter.Value, fieldsType);

                constantExpression = Expression.Constant(formatedValue, memberExpression.Type);

            }

            else

            {

                constantExpression = Expression.Constant(dataFilter.ValueList, GetGenericListType(memberExpression.Type.Name));

                if (memberExpression.Type.Name == "String")

                {

                    constantExpression = Expression.Constant(dataFilter.ValueList, typeof(List<string>));

                }

                else

                {

                    constantExpression = Expression.Constant(dataFilter.ValueList, typeof(List<int>));

                }

            }

            Expression body = CreateExpressionBody(memberExpression, constantExpression, dataFilter.Option);

            return body;

        }

        /// <summary>

        /// 根据字段,比较值获取Expression

        /// </summary>

        /// <param name="memberExpression"></param>

        /// <param name="constantExpression"></param>

        /// <param name="operatorStr"></param>

        /// <returns></returns>

        /// <exception cref="ArgumentException"></exception>

        private static Expression CreateExpressionBody(

            MemberExpression memberExpression, ConstantExpression constantExpression, string operatorStr)

        {

            if (!System.Enum.TryParse($"OPT_{operatorStr}".ToUpper(), true, out OperatorEnum operatorEnum))

            {

                throw new ArgumentException($"不支持操作符:{operatorStr}");

            }

            Type? genericListType = null;

            switch (operatorEnum)

            {

                case OperatorEnum.OPT_EQ:

                    return Expression.Equal(memberExpression, constantExpression);

                case OperatorEnum.OPT_NQ:

                    return Expression.NotEqual(memberExpression, constantExpression);

                case OperatorEnum.OPT_LIKE:

                    return Expression.Call(memberExpression, typeof(string).GetMethod("Contains", new Type[] { typeof(string) }), constantExpression);

                case OperatorEnum.OPT_NOTLIKE:

                    return Expression.Not(Expression.Call(memberExpression, typeof(string).GetMethod("Contains", new Type[] { typeof(string) }), constantExpression));

                case OperatorEnum.OPT_GT:

                case OperatorEnum.OPT_GE:

                case OperatorEnum.OPT_LT:

                case OperatorEnum.OPT_LE:

                    return GetCompareExpression(memberExpression, constantExpression, operatorEnum);

                case OperatorEnum.OPT_IN:

                    genericListType = GetGenericListType(memberExpression.Type.Name);

                    return Expression.Call(constantExpression, genericListType.GetMethod("Contains", new Type[] { memberExpression.Type }), memberExpression);

                case OperatorEnum.OPT_NOTIN:

                    genericListType = GetGenericListType(memberExpression.Type.Name);

                    return Expression.Not(Expression.Call(constantExpression, genericListType.GetMethod("Contains", new Type[] { memberExpression.Type }), memberExpression));

                default:

                    throw new ArgumentException($"不支持操作符:{operatorEnum}");

            }

        }

        /// <summary>

        /// 获取比较表达式,支持字符串、数字、日期比较大小

        /// </summary>

        /// <param name="memberExpression"></param>

        /// <param name="constantExpression"></param>

        /// <param name="operatorEnum"></param>

        /// <returns></returns>

        private static Expression GetCompareExpression(MemberExpression memberExpression, ConstantExpression constantExpression, OperatorEnum operatorEnum)

        {

            ConstantExpression constant0Expression = Expression.Constant(0);

            Expression strCompareExpression = null;

            if (memberExpression.Type.Name == BasicDataType.String.ToString())

            {

                strCompareExpression = Expression.Call(memberExpression, typeof(string).GetMethod("CompareTo", new Type[] { typeof(string) }), constantExpression);

            }

            switch (operatorEnum)

            {

                case OperatorEnum.OPT_GT:

                    if (memberExpression.Type.Name == BasicDataType.String.ToString())

                    {

                        return Expression.GreaterThan(strCompareExpression, constant0Expression);

                    }

                    else

                    {

                        return Expression.GreaterThan(memberExpression, constantExpression);

                    }

                case OperatorEnum.OPT_GE:

                    if (memberExpression.Type.Name == BasicDataType.String.ToString())

                    {

                        return Expression.GreaterThanOrEqual(strCompareExpression, constant0Expression);

                    }

                    else

                    {

                        return Expression.GreaterThanOrEqual(memberExpression, constantExpression);

                    }

                case OperatorEnum.OPT_LT:

                    if (memberExpression.Type.Name == BasicDataType.String.ToString())

                    {

                        return Expression.LessThan(strCompareExpression, constant0Expression);

                    }

                    else

                    {

                        return Expression.LessThan(memberExpression, constantExpression);

                    }

                case OperatorEnum.OPT_LE:

                    if (memberExpression.Type.Name == BasicDataType.String.ToString())

                    {

                        return Expression.LessThanOrEqual(strCompareExpression, constant0Expression);

                    }

                    else

                    {

                        return Expression.LessThanOrEqual(memberExpression, constantExpression);

                    }

                default:

                    return null;

            }

        }

        /// <summary>

        /// 根据属性类型对获取list泛型类型

        /// </summary>

        /// <param name="typeName"></param>

        /// <returns></returns>

        /// <exception cref="ArgumentException"></exception>

        private static Type GetGenericListType(string typeName)

        {

            if (!System.Enum.TryParse(typeName, true, out BasicDataType dataType))

            {

                throw new ArgumentException($"不支的数据类型:{typeName}");

            }

            switch (dataType)

            {

                case BasicDataType.String:

                    return typeof(List<string>);

                case BasicDataType.Int32:

                    return typeof(List<string>);

                default:

                    throw new ArgumentException("不支持的泛型类型转换");

            }

        }

        /// <summary>

        /// 输入value值根据对应的属性类型格式化

        /// </summary>

        /// <param name="value"></param>

        /// <param name="typeName"></param>

        /// <returns></returns>

        /// <exception cref="ArgumentException"></exception>

        /// <exception cref="Exception"></exception>

        private static dynamic DataValueFormat(string value, string typeName)

        {

            if (!System.Enum.TryParse(typeName, true, out BasicDataType dataType))

            {

                throw new ArgumentException($"不支的数据类型:{typeName}");

            }

            switch (dataType)

            {

                case BasicDataType.Int32:

                    return Convert.ToInt32(value);

                case BasicDataType.Int64:

                    return Convert.ToInt64(value);

                case BasicDataType.Single:

                    return Convert.ToSingle(value);

                case BasicDataType.Double:

                    return Convert.ToDouble(value);

                case BasicDataType.Decimal:

                    return Convert.ToDecimal(value);

                case BasicDataType.String:

                    return value;

                case BasicDataType.DateTime:

                    return Convert.ToDateTime(value);

                case BasicDataType.Guid:

                    return Guid.Parse(value);

                case BasicDataType.Boolean:

                    value = value.ToUpper();

                    if (value == "TRUE" || value == "1")

                    {

                        return true;

                    }

                    else if (value == "FALSE" || value == "0")

                    {

                        return false;

                    }

                    else

                    {

                        throw new Exception($"{value}不能被解析为Bool类型");

                    }

                default:

                    throw new Exception($"{value}不能被解析为{typeName}类型");

            }

        }

        #endregion

    }

}

using SignUp.Common.Enum;

using System.Linq.Expressions;

namespace SignUp.Common.DataFilter

{

    /// <summary>

    /// 动态实现排序

    /// </summary>

    public static class DataOrderConvertor

    {

        /// <summary>

        /// 创建lambda表达式:p=>true

        /// </summary>

        /// <typeparam name="T"></typeparam>

        /// <returns></returns>

        public static Expression<Func<T, bool>> True<T>()

        {

            return p => true;

        }

        /// <summary>

        /// 创建lambda表达式:p=>false

        /// </summary>

        /// <typeparam name="T"></typeparam>

        /// <returns></returns>

        public static Expression<Func<T, bool>> False<T>()

        {

            return p => false;

        }

        public static ParameterExpression GetExpressionParameter(this Type type)

        {

            return Expression.Parameter(type, "p");

        }

        /// <summary>

        /// 创建lambda表达式:p=>p.propertyName

        /// </summary>

        /// <typeparam name="T"></typeparam>

        /// <typeparam name="TKey"></typeparam>

        /// <param name="sort"></param>

        /// <returns></returns>

        public static Expression<Func<T, TKey>> GetExpression<T, TKey>(this string propertyName)

        {

            return propertyName.GetExpression<T, TKey>(typeof(T).GetExpressionParameter());

        }

        /// <summary>

        /// 创建委托有返回值的表达式:p=>p.propertyName

        /// </summary>

        /// <typeparam name="T"></typeparam>

        /// <typeparam name="TKey"></typeparam>

        /// <param name="sort"></param>

        /// <returns></returns>

        public static Func<T, TKey> GetFun<T, TKey>(this string propertyName)

        {

            return propertyName.GetExpression<T, TKey>(typeof(T).GetExpressionParameter()).Compile();

        }

        /// <summary>

        /// 创建lambda表达式:p=>false

        /// 在已知TKey字段类型时,如动态排序OrderBy(x=>x.ID)会用到此功能,返回的就是x=>x.ID

        /// Expression<Func<Out_Scheduling, DateTime>> expression = x => x.CreateDate;指定了类型

        /// </summary>

        /// <typeparam name="T"></typeparam>

        /// <returns></returns>

        public static Expression<Func<T, TKey>> GetExpression<T, TKey>(this string propertyName, ParameterExpression parameter)

        {

            if (typeof(TKey).Name == "Object")

                return Expression.Lambda<Func<T, TKey>>(Expression.Convert(Expression.Property(parameter, propertyName), typeof(object)), parameter);

            return Expression.Lambda<Func<T, TKey>>(Expression.Property(parameter, propertyName), parameter);

        }

        /// <summary>

        /// 创建lambda表达式:p=>false

        /// object不能确认字段类型(datetime,int,string),如动态排序OrderBy(x=>x.ID)会用到此功能,返回的就是x=>x.ID

        /// Expression<Func<Out_Scheduling, object>> expression = x => x.CreateDate;任意类型的字段

        /// </summary>

        /// <typeparam name="T"></typeparam>

        /// <returns></returns>

        public static Expression<Func<T, object>> GetExpression<T>(this string propertyName)

        {

            return propertyName.GetExpression<T, object>(typeof(T).GetExpressionParameter());

        }

        public static Expression<Func<T, object>> GetExpression<T>(this string propertyName, ParameterExpression parameter)

        {

            return Expression.Lambda<Func<T, object>>(Expression.Convert(Expression.Property(parameter, propertyName), typeof(object)), parameter);

        }

        /// <summary>

        /// 解析多字段排序

        /// </summary>

        /// <typeparam name="TEntity"></typeparam>

        /// <param name="queryable"></param>

        /// <param name="orderBySelector">string=排序的字段,bool=true降序/false升序</param>

        /// <returns></returns>

        public static IQueryable<TEntity> OrderConditions<TEntity>(this IQueryable<TEntity> queryable, Dictionary<string, QueryOrderBy> orderBySelector)

        {

            string[] orderByKeys = orderBySelector.Select(x => x.Key).ToArray();

            if (orderByKeys == null || orderByKeys.Length == 0) return queryable;

            IOrderedQueryable<TEntity> queryableOrderBy = null;

            //  string orderByKey = orderByKeys[^1];

            string orderByKey = orderByKeys[orderByKeys.Length - 1];

            queryableOrderBy = orderBySelector[orderByKey] == QueryOrderBy.Desc

                ? queryableOrderBy = queryable.OrderByDescending(orderByKey.GetExpression<TEntity>())

                : queryable.OrderBy(orderByKey.GetExpression<TEntity>());

            for (int i = orderByKeys.Length - 2; i >= 0; i--)

            {

                queryableOrderBy = orderBySelector[orderByKeys[i]] == QueryOrderBy.Desc

                    ? queryableOrderBy.ThenByDescending(orderByKeys[i].GetExpression<TEntity>())

                    : queryableOrderBy.ThenBy(orderByKeys[i].GetExpression<TEntity>());

            }

            return queryableOrderBy;

        }

    }

}

namespace SignUp.Common.DataFilter

{

    /// <summary>

    /// FE CORE动态查询条件拼接,接收前台查询条件

    /// </summary>

    public class DataFilter

    {

        public string Name { get; set; }

        public string Option { get; set; }

        public string Value { get; set; }

        public List<string> ValueList { get; set; }

    }

}

namespace SignUp.Common.Enum

{

    /// <summary>

    /// FE CORE动态查询条件拼接,支持的数据类型

    /// </summary>

    public enum BasicDataType

    {

        Int32,

        Int64,

        Single,

        Double,

        Decimal,

        Boolean,

        String,

        DateTime,

        Guid

    }

}

namespace SignUp.Common.Enum

{

    /// <summary>

    /// FE CORE动态查询条件拼接,支持的运算符

    /// </summary>

    public enum OperatorEnum

    {

        // 等于

        OPT_EQ,

        // 不等于

        OPT_NQ,

        // 包含

        OPT_LIKE,

        // 不包含

        OPT_NOTLIKE,

        // 大于

        OPT_GT,

        // 大于等于

        OPT_GE,

        // 小于

        OPT_LT,

        // 小于等于

        OPT_LE,

        // 数组包含

        OPT_IN,

        // 数组不包含

        OPT_NOTIN,

    }

}

namespace SignUp.Common.Enum

{

    /// <summary>

    /// 排序方式

    /// </summary>

    public enum QueryOrderBy

    {

        Desc = 1,

        Asc = 2

    }

}

二、调用方法

1

2

3

4

5

6

7

8

9

10

11

12

13

public async Task<(SysUser[], int total)> GetUserList(int pageIndex, int pageSize, string wheres, string sort, string order)

{

    //where查询条件

    List<DataFilter> searchParametersList = FormatParameters.GetSearchParameters(wheres);

    //order排序条件

    Dictionary<string, QueryOrderBy> orderBy = FormatParameters.GetOrderParameters(sort, order);

    //构造查询条件

    Expression<Func<SysUser, bool>> expression = DataFilterConvertor<SysUser>.ToExpression(searchParametersList);

    var listAll = _dbContext.SysUser.Where(expression);

    int total = listAll.Count();

    var list = await listAll.OrderConditions(orderBy).Skip((pageIndex - 1) * pageSize).Take(pageSize).ToArrayAsync();

    return (list, total);

}

学习交流

附笔者学习 .net core开发时参考相关项目实例源码:asp.net core webapi项目实例源代码锦集下载(72个)

  • 12
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
.NET Core 具有一些功能,可以帮助您实现动态 API。下面是一些实现动态 API 的方法: 1. 使用 ASP.NET Core 的路由属性 ASP.NET Core 路由属性使您能够在运行时动态更改 API 的路由。您可以将路由属性添加到控制器或操作方法上,并在运行时更改它们的值。 例如,以下代码演示如何将路由属性添加到控制器上: ``` [Route("api/[controller]")] public class DynamicController : ControllerBase { [HttpGet("{id}")] public IActionResult Get(int id) { // Code to retrieve data based on id } } ``` 在此示例中,控制器的路由模板为“api/[controller]”,其中 [controller] 将替换为控制器名称。您还可以在操作方法上使用路由属性,如上面的 [HttpGet("{id}")]。 2. 使用 ASP.NET Core动态路由 ASP.NET Core 还支持根据请求路径的不同部分动态生成路由。例如,您可以使用以下代码创建一个基于请求路径的动态路由: ``` app.UseEndpoints(endpoints => { endpoints.MapDynamicControllerRoute<DynamicController>("api/{controller}/{action}/{id?}"); }); ``` 在此示例中,路由模板为“api/{controller}/{action}/{id?}”,其中 {controller}、{action} 和 {id} 都是动态部分,可以根据请求路径进行替换。 3. 使用 ASP.NET Core 的中间件 ASP.NET Core 中的中间件是一种非常灵活的方法,可以用于实现动态 API。您可以创建一个中间件,它可以根据请求的路径、参数等动态生成响应。 例如,以下代码演示如何创建一个中间件,它将根据请求的路径返回不同的响应: ``` public class DynamicMiddleware { private readonly RequestDelegate _next; public DynamicMiddleware(RequestDelegate next) { _next = next; } public async Task InvokeAsync(HttpContext context) { var path = context.Request.Path; if (path.StartsWithSegments("/api/dynamic")) { // Code to generate dynamic response await context.Response.WriteAsync("This is a dynamic API response"); } else { await _next(context); } } } ``` 在此示例中,中间件检查请求的路径是否以“/api/dynamic”开头。如果是,则生成动态响应;否则,将请求传递给下一个中间件。 这些都是实现动态 API 的一些方法。您可以根据您的具体需求选择其中的任何一种。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值