MVC5+EF6 入门完整教程九

 

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

文章提纲

 

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

 

理论基础

 

基于前面的文章,本次我们更近一步,进行更加深入的讲解, 首先介绍下Attribute配置Data Model.

使用Attribute配置Data Model, 可以指定formatting, validation, database mapping rules

约定:下图中三种情况一般资料都翻译成"属性",为了区分,我们用下图中的表述方式。

MVC5+EF6 入门完整教程九

接下来,我们先对常用的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,把创建日期显示出来,如下方框处。

MVC5+EF6 入门完整教程九

MVC5+EF6 入门完整教程九

大家注意到,默认情况下会显示出时间,我们只需要显示到日就可以了。

下面我们就把CreateDate调整到我们需要的格式。

打开 Models\SysUser.cs, 做如下修改。

MVC5+EF6 入门完整教程九

MVC5+EF6 入门完整教程九

DataType 属性用来指定更加具体的数据类型,DataType枚举值提供了一些常见的类型,比如Date,Time,EmailAddress等。

但是DataType不能指定数据类型的显示格式,例如日期要什么格式显示。

默认情况下显示格式会根据电脑的设定显示。

这个时候就需要配合使用DisplayFormate属性来指定格式。

[DisplayFormat(DataFormatString="{0:yyyy-MM-dd}",ApplyFormatInEditMode=true)]

 

 

StringLength

 

你可以指定数据验证规则以及出错信息。

StringLength属性设置了数据库中存储字段的最大长度,为程序提供客户端和服务器端的验证。同样用这个属性也可以指定最小长度,不过不影响数据库的结构。

MVC5+EF6 入门完整教程九

同样更新下数据库

add-migration MaxLengthOnNames
update-database

先去数据库看下,可以看到已经有长度限制了。

MVC5+EF6 入门完整教程九

我们再修改下Create方法,测试下验证。

之前我们的模型太简陋了,为了看到效果,再做两处修改。

Views\Account\Create.cshtml增加一个Helper:ValidationMessageFor用来显示验证信息

MVC5+EF6 入门完整教程九

Controllers\AccountController.cs增加一个判断条件ModelState.IsValid,不然会出错。

MVC5+EF6 入门完整教程九

运行可以看到如下效果:

MVC5+EF6 入门完整教程九

 

 

Column

 

这个属性也非常实用。

有时会有这么一种情况,我们Model中的字段和数据库中表的字段要用不同的命名。例如我们Model中命名为UserName,数据库表中命名为LoginName.

这个时候就用到Column了。

MVC5+EF6 入门完整教程九

同样运行更新指令。

打开数据库可以看到UserName已经变成LoginName了。

MVC5+EF6 入门完整教程九

下面再列出其他常用的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的时候,相关的数据将会被自动读取。

这种情况会导致多次查询数据库。

MVC5+EF6 入门完整教程九

 

Eager loading

 

当读取entity的时候,相关数据会被一起读取。

一般来说这种方式会产生一个join query来获取所有需要的数据。

通过Include方法来指定eager loading.

MVC5+EF6 入门完整教程九

 

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

MVC5+EF6 入门完整教程九

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)

MVC5+EF6 入门完整教程九

原来的SysUser中添加一个如下两个property

MVC5+EF6 入门完整教程九

使用 code first migrations的方式更新下数据库。可以看到新的表结构已经生成了。

MVC5+EF6 入门完整教程九

去数据库中SysDepartment添加两笔资料。

去数据库中SysUser修改用户对应的department

MVC5+EF6 入门完整教程九

MVC5+EF6 入门完整教程九

先看下原来的Views\Account\Index.cshtml

MVC5+EF6 入门完整教程九

我们原来是显示SysUser主表内容,当点击Details时通过navigation property实现SysUseràSysUserRoleàSysRole多表间查询。

现在我们增加一列Department, 让这个表格能直接显示SysUser主表及相应的Department内容。

我们使用Eager Loading的方式将Department的内容也加载进去,打开Controllers\AccountController.cs, 在index修改一处地方:

MVC5+EF6 入门完整教程九

修改对应的View

MVC5+EF6 入门完整教程九

运行,可以看到Department中的内容已经被我们加载进来了。

MVC5+EF6 入门完整教程九

这个就是第一种场景,多对一的情况。

下面我们再来看多对多的情况。

 

场景二:多对多关系

 

多对多关系可以拆解成一对多的关系,例如用户和角色(* to *)可拆解成:

显示用户及相应的角色(1 to *)

显示角色及相应的用户(1 to *)

为了演示这个场景,我们新建一个ViewModel,将需要显示的表都放进去。

MVC5+EF6 入门完整教程九

创建相应的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> 
} 

 

最终展示结果:

MVC5+EF6 入门完整教程九

 

总结

 

一、掌握常用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="用户名")]

 

 

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

上一篇:c# – 查找附近的GPS导航路线


下一篇:wxPython导航面板(如Finder(Mac OS X),Nautilus(Ubuntu),Explorer(Windows 7))