文章提纲
理论基础
应用场景
总结
理论基础
基于前面的文章, 本次我们更近一步, 进行更加深入的讲解, 首先介绍下 Attribute 配置 Data Model.
使用 Attribute 配置 Data Model, 可以指定 formatting, validation, database mapping rules
约定: 下图中三种情况一般资料都翻译成 "属性", 为了区分, 我们用下图中的表述方式.
接下来, 我们先对常用的 attribute 进行举例说明.
一, 常用 Attribute
DataType,DisplayFormate
首先打开 ModelsàSysUser.cs
添加 public DateTime CreateDate { get; set; }
增加完之后及时使用 Code First Migrations 方式更新数据库.(不然运行时会报 contex 和 database 不一致的错误)
Note
注意把 Migrations\ Configuration.cs 中 Seed 方法中内容注释掉, 因为
模型变了, 插入示例数据时会报一个错误.
运行 即可更新
更新后随便在数据库中插入两个日期值.
Code First Migrations 方式更新数据库详细做法参加上篇文章.
接着修改 Views\Account\Index.cshtml, 把创建日期显示出来, 如下方框处.
大家注意到, 默认情况下会显示出时间, 我们只需要显示到日就可以了.
下面我们就把 CreateDate 调整到我们需要的格式.
打开 Models\SysUser.cs, 做如下修改.
DataType 属性用来指定更加具体的数据类型, DataType 枚举值提供了一些常见的类型, 比如 Date,Time,EmailAddress 等.
但是 DataType 不能指定数据类型的显示格式, 例如日期要什么格式显示.
默认情况下显示格式会根据电脑的设定显示.
这个时候就需要配合使用 DisplayFormate 属性来指定格式.
- [DisplayFormat(DataFormatString="{0:yyyy-MM-dd}",ApplyFormatInEditMode=true)]
- StringLength
你可以指定数据验证规则以及出错信息.
StringLength 属性设置了数据库中存储字段的最大长度, 为程序提供客户端和服务器端的验证. 同样用这个属性也可以指定最小长度, 不过不影响数据库的结构.
同样更新下数据库
- add-migration MaxLengthOnNames
- update-database
先去数据库看下, 可以看到已经有长度限制了.
我们再修改下 Create 方法, 测试下验证.
之前我们的模型太简陋了, 为了看到效果, 再做两处修改.
Views\Account\Create.cshtml 增加一个 Helper:ValidationMessageFor 用来显示验证信息
Controllers\AccountController.cs 增加一个判断条件 ModelState.IsValid, 不然会出错.
运行可以看到如下效果:
Column
这个属性也非常实用.
有时会有这么一种情况, 我们 Model 中的字段和数据库中表的字段要用不同的命名. 例如我们 Model 中命名为 UserName, 数据库表中命名为 LoginName.
这个时候就用到 Column 了.
同样运行更新指令.
打开数据库可以看到 UserName 已经变成 LoginName 了.
下面再列出其他常用的 attribute, 就不举展开讲了, 很容易可以看懂, 大家可以自己尝试.
- [StringLength(10,MinimumLength=1,ErrorMessage="名字在 1 和 10 个字之间")]
- Note
1. 可以将多个属性写在一块用逗号隔开, 例如
[Column("FirstName"),Display(Name = "First Name"),StringLength(50, MinimumLength=1)]
2. 对某一些类型来说不需要使用 Required, 例如 DateTime, int,double,float, 因为这些值类型不能被赋予空值, 因此他们天生就具有 Required 的特性.
- [Column(TypeName="money")]
- public decimal Budget {
- get; set;
- }
之前用 Column 可以改变数据库中列名.
指定 Column 的 TypeName 可以改变 SQL data type, 这个例子中就是知道使用 SQL Server 的 money 类型.
Column mapping 一般来说不需要, 因为 EF 通常会基于你为 property 定义的 CLR 类型选择合适的 SQL Server data type.
The CLR decimal type maps to a SQL Server decimal type.
详细对应表:
二, Lazy, Eager, and Explicit Loading of Related Data
前面文章中我们介绍过显示关联表数据的方法.
第四篇文章介绍过通过 navigation 属性显示关联表数据.
本篇文章就系统的讲解下多表关联数据显示的问题.
有三种方式 EF 可以加载关联数据到一个实体的 navigation 属性中, 下面我就直接用 MSDN 上的截图来说明.
Lazy loading
第一次读取 entity 的时候不会加载.
当需要读取 navigation property 的时候, 相关的数据将会被自动读取.
这种情况会导致多次查询数据库.
Eager loading
当读取 entity 的时候, 相关数据会被一起读取.
一般来说这种方式会产生一个 join query 来获取所有需要的数据.
通过 Include 方法来指定 eager loading.
Explicit loading
和 lazy loading 类似, 除了需要在代码中明确指定需要获取的关联数据.
在读取 navigation property 时 explicit loading 不会自动发生, 你需要手动加载相关数据.
通过获取 object state manager entry for entity, 调用 Collection.Load method for collections 或者 Reference.Load method for properties that hold a single entity.
一般来说, 只有在关闭 lazying loading 的时候才会使用 explicit loading
lazy loading 和 explicit loading 都不立即获取 property values, 它们也被称作 deferred loading.
Disable lazy loading before serialization
disable lazy loading 的两种方式:
1. 对特定的 navigation properties 来说, 省略 property 的 virtual 关键字就可以了
2. 对所有 navigation properties 来说, 在 context 类中, 构造函数中设置 LazyingLoadingEnabled 为 false 即可.
this.Configuration.LazyLoadingEnabled = false;
应用场景
场景一: 多对一关系, 显示用户及相应的部门(* to 0 or 1)
新建一个 entity: SysDepartment
我们约定, 某个用户只能归属于 0 个或 1 个部门.
即用户和部门的关系为(* to 0 or 1)
原来的 SysUser 中添加一个如下两个 property
使用 code first migrations 的方式更新下数据库. 可以看到新的表结构已经生成了.
去数据库中 SysDepartment 添加两笔资料.
去数据库中 SysUser 修改用户对应的 department
先看下原来的 Views\Account\Index.cshtml
我们原来是显示 SysUser 主表内容, 当点击 Details 时通过 navigation property 实现 SysUseràSysUserRoleàSysRole 多表间查询.
现在我们增加一列 Department, 让这个表格能直接显示 SysUser 主表及相应的 Department 内容.
我们使用 Eager Loading 的方式将 Department 的内容也加载进去, 打开 Controllers\AccountController.cs, 在 index 修改一处地方:
修改对应的 View
运行, 可以看到 Department 中的内容已经被我们加载进来了.
这个就是第一种场景, 多对一的情况.
下面我们再来看多对多的情况.
场景二: 多对多关系
多对多关系可以拆解成一对多的关系, 例如用户和角色 (* to *) 可拆解成:
显示用户及相应的角色(1 to *)
显示角色及相应的用户(1 to *)
为了演示这个场景, 我们新建一个 ViewModel, 将需要显示的表都放进去.
创建相应的 Controller 和 View
因为前面的文章已经将基本的用法都讲过了, 我这里就直接贴出代码以及最终的展示结果, 如果有不理解的部分再给我留言.
- Controllers\UserRoleController.cs:
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using System.web;
- using System.Web.Mvc;
- using MVCDemo.ViewModels;
- using MVCDemo.DAL;
- using MVCDemo.Models;
- using System.Data.Entity;
- namespace MVCDemo.Controllers
- {
- public class UserRoleController : Controller
- {
- private AccountContext db = new AccountContext();
- //
- // GET: /UserRole/
- public ActionResult Index(int? id)
- {
- var viewModel = new UserRoleIndexData();
- viewModel.SysUsers = db.SysUsers
- .Include(u => u.SysDepartment)
- .Include(u => u.SysUserRoles.Select(ur => ur.SysRole))
- .OrderBy(u => u.UserName);
- if (id != null)
- {
- ViewBag.UserID = id.Value;
- viewModel.SysUserRoles = viewModel.SysUsers.Where(u => u.ID == id.Value).Single().SysUserRoles;
- viewModel.SysRoles = (viewModel.SysUserRoles.Where(
- ur => ur.SysUserID == id.Value)).Select(ur => ur.SysRole);
- }
- return View(viewModel);
- }
- }
- }
- Views\UserRole\Index.cshtml
- @model MVCDemo.ViewModels.UserRoleIndexData
- @{
- ViewBag.Title = "Index";
- Layout = "~/Views/Shared/_LayoutAdmin.cshtml";
- }
- <h2>UserRoles</h2>
- <p>
- @HTML.ActionLink("Create New", "Create")
- </p>
- <table class="table table-striped">
- <tr>
- <th>
- UserName
- </th>
- <th>
- </th>
- <th>
- CreateDate
- </th>
- <th>
- Department
- </th>
- <th>
- Roles
- </th>
- <th></th>
- </tr>
- @foreach (var item in Model.SysUsers)
- {
- string selectedRow = "";
- if (item.ID==ViewBag.UserID)
- {
- selectedRow = "success";
- }
- <tr class="@selectedRow">
- <td>
- @HTML.DisplayFor(modelItem => item.UserName)
- </td>
- <td>
- @HTML.DisplayFor(modelItem => item.Email)
- </td>
- <td>
- @HTML.DisplayFor(modelItem => item.CreateDate)
- </td>
- <td>
- @if (item.SysDepartment != null)
- {
- @item.SysDepartment.DepartmentName
- }
- </td>
- <td>
- @{
- foreach (var userRole in item.SysUserRoles)
- {
- @userRole.SysRole.RoleName <br />
- }
- }
- </td>
- <td>
- @HTML.ActionLink("Select", "Index", new { id = item.ID })
- </td>
- </tr>
- }
- </table>
- @if (Model.SysRoles != null)
- {
- <h3>Related Roles</h3>
- <table class="table table-striped">
- <tr>
- <th>RoleName</th>
- <th>RoleDesc</th>
- </tr>
- @foreach (var item in Model.SysRoles)
- {
- <tr>
- <td>
- @item.RoleName
- </td>
- <td>
- @item.RoleDesc
- </td>
- </tr>
- }
- </table>
- }
最终展示结果:
总结
一, 掌握常用 attribute
DataType
例子:[DataType(DataType.Date)]
DisplayFormat
例子:
- [DisplayFormat(DataFormatString="{0:yyyy-MM-dd}",ApplyFormatInEditMode=true)]
- [DisplayFormat(NullDisplayText = "No grade")]
- StringLength
例子:
- [StringLength(10,MinimumLength=1,ErrorMessage="名字在 1 和 10 个字之间")]
- Column
例子:
- [Column("FirstName")]
- [Column(TypeName="money")]
- Display
例子:
[Display(Name="用户名")]
二, 掌握加载多表数据两种应用场景
来源: http://www.bubuko.com/infodetail-3110705.html