MVC5+EF6 入门完整教程九

Posted zhang1f

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了MVC5+EF6 入门完整教程九相关的知识,希望对你有一定的参考价值。

 

原文:https://www.cnblogs.com/miro/p/4288184.html

文章提纲

 

  • 理论基础
  • 应用场景
  • 总结

 

理论基础

 

基于前面的文章,本次我们更近一步,进行更加深入的讲解, 首先介绍下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.

详细对应表:https://msdn.microsoft.com/en-us/library/bb896344.aspx

 

 

二、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> 
            Email 
        </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="用户名")]

 

 

二、掌握加载多表数据两种应用场景

以上是关于MVC5+EF6 入门完整教程九的主要内容,如果未能解决你的问题,请参考以下文章

MVC5+EF6 入门完整教程八

MVC5+EF6 入门完整教程13 -- 动态生成多级菜单

MVC5+EF6 完整教程17--升级到EFCore2.0(转)

MVC 5 + EF6 完整教程16 -- 控制器详解

基于 jQuery 的专业 ASP.NET WebForms/MVC 控件库!

ASP.NET + MVC5 入门完整教程八 -—-- 一个完整的应用程序(下)