前一阵子临时有事,这篇文章发布间隔比较长,咱们先回顾下以前的内容,每篇文章用一句话总结重点。 html
文章一 MVC核心概念简介,一个基本MVC项目结构 数据库
文章二 经过开发一个最基本的登陆界面,介绍了如何从Controller中获取表单数据 服务器
文章三 EF的整个开发过程 app
文章四 EF基本的CRUD和经常使用的HtmlHelper 函数
文章五 使用布局页(模板页)改造UI 布局
文章六 分部视图(Partial View) 学习
文章七 排序过滤分页 测试
文章八 不丢失数据进行数据库结构升级 ui
以上若是有不清楚的能够再回去看一下。 this
基于前面的文章,本次咱们更近一步,进行更加深刻的讲解, 首先介绍下Attribute配置Data Model.
使用Attribute配置Data Model, 能够指定formatting, validation, database mapping rules
约定:下图中三种状况通常资料都翻译成"属性",为了区分,咱们用下图中的表述方式。
接下来,咱们先对经常使用的attribute进行举例说明。
首先打开ModelsàSysUser.cs
添加 public DateTime CreateDate { get; set; }
增长完以后及时使用Code First Migrations 方式更新数据库。(否则运行时会报contex和database不一致的错误)
Note
注意把Migrations\ Configuration.cs中Seed方法中内容注释掉,由于
模型变了,插入示例数据时会报一个错误。
运行 add-migration AddCreateDateToSysUser,update-database便可更新
更新后随便在数据库中插入两个日期值。
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属性设置了数据库中存储字段的最大长度,为程序提供客户端和服务器端的验证。一样用这个属性也能够指定最小长度,不过不影响数据库的结构。
一样更新下数据库
add-migration MaxLengthOnNames
update-database
先去数据库看下,能够看到已经有长度限制了。
咱们再修改下Create方法,测试下验证。
以前咱们的模型太简陋了,为了看到效果,再作两处修改。
Views\Account\Create.cshtml增长一个Helper:ValidationMessageFor用来显示验证信息
Controllers\AccountController.cs增长一个判断条件ModelState.IsValid,否则会出错。
运行能够看到以下效果:
这个属性也很是实用。
有时会有这么一种状况,咱们Model中的字段和数据库中表的字段要用不一样的命名。例如咱们Model中命名为UserName,数据库表中命名为LoginName.
这个时候就用到Column了。
一样运行更新指令。
add-migration ColumnLoginName
update-database
打开数据库能够看到UserName已经变成LoginName了。
下面再列出其余经常使用的attribute, 就不举展开讲了,很容易能够看懂,你们能够本身尝试。
[Display(Name="用户名")]
[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.
详细对应表:https://msdn.microsoft.com/en-us/library/bb896344.aspx
前面文章中咱们介绍过显示关联表数据的方法。
第四篇文章介绍过经过navigation 属性显示关联表数据。
本篇文章就系统的讲解下多表关联数据显示的问题。
有三种方式EF能够加载关联数据到一个实体的navigation属性中,下面我就直接用MSDN上的截图来讲明。
第一次读取entity的时候不会加载。
当须要读取navigation property的时候,相关的数据将会被自动读取。
这种状况会致使屡次查询数据库。
当读取entity的时候,相关数据会被一块儿读取。
通常来讲这种方式会产生一个join query来获取全部须要的数据。
经过Include方法来指定eager 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;
新建一个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 *)
为了演示这个场景,咱们新建一个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>
}
最终展现结果:
例子:[DataType(DataType.Date)]
例子:
[DisplayFormat(DataFormatString="{0:yyyy-MM-dd}",ApplyFormatInEditMode=true)]
[DisplayFormat(NullDisplayText = "No grade")]
例子:
[StringLength(10,MinimumLength=1,ErrorMessage="名字在1和10个字之间")]
例子:
[Column("FirstName")]
[Column(TypeName="money")]
例子:
[Display(Name="用户名")]
好了,到目前为止,使用MVC+EF开发的基本知识差很少都齐全了。
在下一课,也就是咱们第一阶段的最后一课:MVC5+EF6入门完整教程十,咱们将再讲解下数据更新的部分,以及如何使用原生SQL,如何调用存储过程等。
祝你们新年快乐,学习进步!