如何在Dapper中传入列表参数?现有查询实现求优化
优化你的用户课程查询方法
你的现有代码能运行,但存在多次数据库往返、手动SQL拼接易出错的问题,下面是几种更优的实现方式:
1. 合并为单条SQL查询(首推)
直接通过JOIN或子查询把两个操作合并成一次数据库请求,减少网络开销,代码更简洁:
方式一:使用INNER JOIN
public List<Course> GetMyCourses(int id) { string query = @" SELECT c.Id, c.Image, c.CourseStateId, c.Price, c.Title, c.TeacherId, u.UserName, u.Id FROM Courses c INNER JOIN Users u ON u.Id = c.TeacherId INNER JOIN CourseUsers cu ON cu.CourseId = c.Id WHERE cu.UserId = @id"; var courses = db.Query<Course>(query, new { id }).ToList(); return courses.Count == 0 ? null : courses; }
方式二:使用子查询
public List<Course> GetMyCourses(int id) { string query = @" SELECT c.Id, c.Image, c.CourseStateId, c.Price, c.Title, c.TeacherId, u.UserName, u.Id FROM Courses c INNER JOIN Users u ON u.Id = c.TeacherId WHERE c.Id IN (SELECT CourseId FROM CourseUsers WHERE UserId = @id)"; var courses = db.Query<Course>(query, new { id }).ToList(); return courses.Count == 0 ? null : courses; }
这两种方式都只需要一次数据库查询,避免了先查ID列表再拼接SQL的繁琐,同时语法更规范,不易出错。
2. 必须分两次查询时,用参数化IN语句
如果因为业务限制必须先获取ID列表,不要手动拼接字符串,改用参数化方式处理IN子句,避免潜在的语法错误和SQL注入风险:
public List<Course> GetMyCourses(int id) { string secQuery = "SELECT CourseId FROM CourseUsers WHERE UserId = @id"; var courseIds = db.Query<int>(secQuery, new { id }).ToList(); if (!courseIds.Any()) { return null; } // 生成带参数的IN子句 var paramList = courseIds.Select((cid, idx) => new { ParamName = $"@cid{idx}", Value = cid }).ToList(); string inClause = string.Join(", ", paramList.Select(p => p.ParamName)); string query = $@" SELECT c.Id, c.Image, c.CourseStateId, c.Price, c.Title, c.TeacherId, u.UserName, u.Id FROM Courses c INNER JOIN Users u ON u.Id = c.TeacherId WHERE c.Id IN ({inClause})"; // 构建参数集合 var dynamicParams = new DynamicParameters(); foreach (var param in paramList) { dynamicParams.Add(param.ParamName, param.Value); } return db.Query<Course>(query, dynamicParams).ToList(); }
3. 用ORM的LINQ查询(如果使用EF Core等ORM)
如果你的项目用的是Entity Framework Core这类ORM,可以直接用LINQ查询,代码可读性更强,ORM会自动生成优化的SQL:
public List<Course> GetMyCourses(int id) { var courses = _context.Courses .Include(c => c.Teacher) // 假设Course实体有Teacher导航属性 .Where(c => c.CourseUsers.Any(cu => cu.UserId == id)) .Select(c => new Course { Id = c.Id, Image = c.Image, CourseStateId = c.CourseStateId, Price = c.Price, Title = c.Title, TeacherId = c.TeacherId, UserName = c.Teacher.UserName }) .ToList(); return courses.Count == 0 ? null : courses; }
原代码的几个小问题
- 手动拼接SQL时逻辑复杂,容易出现语法错误(比如括号遗漏、逗号位置不对);
- 两次数据库查询增加了网络延迟;
- 虽然整数ID的SQL注入风险低,但手动拼接字符串不是最佳实践,参数化查询才是规范做法。
内容的提问来源于stack exchange,提问作者Bob-Programming
相关产品推荐
相关产品推荐

