ASP.NET EF多对多关系下,无需新ViewModel获取班级信息的方法?
我在ASP.NET和Entity Framework环境下开发,用户与班级是多对多关系,现在需要获取班级的学生列表。目前知道两种方案:一是创建用户/班级ViewModel,二是通过ViewBag.ClassId在控制器和视图间传递参数。
想请教:如果视图页面使用@model IEnumerable<Models.User>,能不能不用创建新ViewModel,直接从Model.Class获取班级信息?我习惯了Java JSP里可以传递任意变量,对ASP.NET的视图/模型限制不太熟悉。
用户和班级的模型是数据库生成的,用userclass表作为连接表,当前我的做法是在控制器里传递ViewBag.ClassId作为参数,代码示例如下:
public async Task<IActionResult> ClassStudents(int id) { var usersInClass = await _context.Classes .Where(cls => cls.Id == id) .Include(cls => cls.Users) // 预加载关联的用户 .Select(cls => cls.Users.Where(user => user.RoleSettingId == 5)) .FirstOrDefaultAsync(); ViewBag.ClassId = id; return View("/Views/Classroom/ClassStudents.cshtml", usersInClass); }
不行,你没法直接从@model IEnumerable<Models.User>里获取班级信息,原因很明确:当前传递给视图的模型只是用户集合,每个User对象的班级关联(如果存在)是反向导航属性,但你现在的查询仅返回了符合条件的用户列表,且这些用户的班级集合不会自动包含当前查询的目标班级(除非特意加载)。就算加载了,你也没法从用户集合里直接拿到统一的班级信息——毕竟一个学生可能属于多个班级,无法确定哪个是当前要展示的班级。
如果不想创建ViewModel,除了用ViewBag,还有几个可选方案:
传递包含班级和用户的匿名类型:修改控制器,返回包含班级实体和学生集合的匿名对象,视图用
@model dynamic接收。但这种方式没有强类型检查,容易出现运行时错误,不适合复杂场景。
示例代码:var classWithStudents = await _context.Classes .Where(cls => cls.Id == id) .Include(cls => cls.Users) .Select(cls => new { Class = cls, Students = cls.Users.Where(user => user.RoleSettingId == 5) }) .FirstOrDefaultAsync(); return View(classWithStudents);视图中可通过
Model.Class.Id、Model.Students访问数据,但无编译时校验。用ViewData传递班级信息:和ViewBag本质一致,只是语法不同,比如
ViewData["Class"] = 班级实体,视图中强转后使用。从用户导航属性中获取(不推荐):如果User模型有
Classes导航属性且查询时已Include,可从第一个用户的Classes里筛选当前班级,但这种方式逻辑脆弱——若班级无学生会直接报错,且不符合业务逻辑。
其实最规范的做法还是创建强类型ViewModel,它不仅有编译时检查,代码可读性和维护性也更高,比如:
public class ClassStudentsViewModel { public int ClassId { get; set; } public string ClassName { get; set; } public IEnumerable<User> Students { get; set; } }
控制器中填充ViewModel:
var classEntity = await _context.Classes .Where(cls => cls.Id == id) .Include(cls => cls.Users) .FirstOrDefaultAsync(); var viewModel = new ClassStudentsViewModel { ClassId = classEntity.Id, ClassName = classEntity.Name, Students = classEntity.Users.Where(user => user.RoleSettingId == 5) }; return View(viewModel);
视图用@model ClassStudentsViewModel,就能直接访问班级信息和学生列表,这也是ASP.NET MVC/Razor Pages推荐的实践方式。
内容的提问来源于stack exchange,提问作者nubcodeman

