来个目录吧:

第一章-入门

第二章- Entity Framework Core Nuget包管理

第三章-创建、修改、删除、查询

第四章-排序、过滤、分页、分组

第五章-迁移,EF Core 的codefirst使用

暂时就这么多。后面陆续更新吧

Entity Framework Core Nuget包管理

如果你创建项目的时候启用了个人身份验证的话,项目中就已经包含了EFCore的支持。

如果你是单纯的空项目想将EFCore添加到你的项目中话,你需要安装一下的Nuget程序包:

vs2017可以直接进行编辑项目的.csproj文件,安装所需软件包。

···











···

(您可以编辑的.csproj文件右击解决方案资源管理器中的项目名称并选择编辑 ContosoUniversity.csproj)。

我又来了,我亲测了下.NETCORE1.1目前不支持Microsoft.EntityFrameworkCore.Tools.DotNet这样玩,这里先略过

创建数据模型

创建Contoso大学实体前,说下他们的关联关系吧。

Student 和Enrollment 的实体关系为一对多。

Course和Enrollment的实体关系同样为一对多。

简单来说就是一个学生可以参加任意一门课程,而一门课程可以有很多个学生。(当然同一个课程该学生只能参加一次,反之亦然)

然后我们开始创建实体吧。

Student 实体

我们在根目录新建一个“Models”文件夹,创建一个“Student”的类文件,复制以下代码替换掉内容。

using System;
using System.Collections.Generic;
namespace ContosoUniversity.Models
{
public class Student
{
public int ID { get; set; }
public string LastName { get; set; }
public string FirstMidName { get; set; }
public DateTime EnrollmentDate { get; set; }
public ICollection<Enrollment> Enrollments { get; set; }
}
}

Id 属性将作为Student类对应的数据库表的主键,默认情况下EF框架都会将ID或者classnameID作为主键。

Enrollments属性是一个导航属性。

老外翻译太绕了,导航属性就是方便你从一个对象导航到关联对象,也可以用于设置对象之间的关联。

这里Enrollments就是Student实体的导航属性,可以通过Enrollments属性将实体Enrollment和Student中的关联信息获取出来。换句话说:一个学生在数据库中有2行登记信息,(每行包含了Student实体的ID),那么该Student可以从导航属性Enrollments中获取到2行登记信息。

如果一个导航属性包含了多个实体(如:一对多,多对多的关系),那么他们的类型必须一个list类型,可以添加、删除、修改。比如: ICollection<T> 。当然你还可以声明为List<T>或者HashSet<T>.如果你声明为 ICollection<T> .EF会默认创建类型为```HashSet``

Enrollment 实体

同样在Models文件中,创建一个类“Enrollment” 然后把代码替换为如下:

namespace ContosoUniversity.Models
{
public enum Grade
{
A, B, C, D, F
} public class Enrollment
{
public int EnrollmentID { get; set; }
public int CourseID { get; set; }
public int StudentID { get; set; }
public Grade? Grade { get; set; } public Course Course { get; set; }
public Student Student { get; set; }
}
}

我们将EnrollmentID作为Enrollment的主键,使用的是classnameID而不是像Student实体中的ID。这里暂时不解释,后面会提到这个问题。

我们声明了一个枚举Grade属性。而在Enrollment实体中Grade是个可空类型,说明他是一个默认可以为空的值,可以在后面根据具体的业务情况来进行赋值处理。

StudentID 作为属性外键,对应的导航属性为Student。ENrollment和Student的关联关系为一对一,所以Enrollment只能持有一个Student实体。(而Student拥有了Enrollment的多个导航属性)

CourseID作为Course的导航属性外键。同样的Enrollment和Course也是一对一的关系。

在这里StudentID作为Student导航属性的外键,等同于Student实体中的ID主键

Course 实体

在Models文件中,创建一个Course类,然后替换为如下代码:

using System.Collections.Generic;
using System.ComponentModel.DataAnnotations.Schema; namespace ContosoUniversity.Models
{
public class Course
{
[DatabaseGenerated(DatabaseGeneratedOption.None)]
public int CourseID { get; set; }
public string Title { get; set; }
public int Credits { get; set; } public ICollection<Enrollment> Enrollments { get; set; }
}
}

在这里Enrollments作为导航属性,一个课程会有多个不同学生的登记信息,所以是一对多的关系。

创建数据库上下文(Database Context)

我们需要创建一个作为EF框架用来连接数据库上下文的类。我们创建的类是从System.Data.Entity.DbContext中派生出来的。你可以定义哪些实体包含在EF的数据模型中。你也可以自定义特定的EF行为。在这个项目中,我们创建一个类名“SchoolContext”

  • 在根目录创建一个文件夹“Data”。
  • 在“Data”这个文件夹中,创建一个新的类“SchoolContext”,然后替换代码为下面:
using ContosoUniversity.Models;
using Microsoft.EntityFrameworkCore; namespace ContosoUniversity.Data
{
public class SchoolContext : DbContext
{
public SchoolContext(DbContextOptions<SchoolContext> options) : base(options)
{
} public DbSet<Course> Courses { get; set; }
public DbSet<Enrollment> Enrollments { get; set; }
public DbSet<Student> Students { get; set; }
}
}

我们为每个实体都创建了一个DbSet的属性。在EF框架中,实体集通常对应数据库中的表,一个实体对应表中的一行数据。

在这里你可以忽略掉**DbSet and DbSet **,它同样会生成表。因为Student会引用Enrollment实体,而Enrollment中包含了Course实体。同样被会引用。EF在生成表的时候会包含他们的引用实体。

创建数据库的时候,数据库的表名会跟 DbSet的属性名一致。属性名称通常会是复数(如 student的表名是Students),但是大多数开发者不同意将表名和实体名字区分开,这样容易混淆。下面的教程就会教会你怎么通过指定你个性化的DbContext表名

把下面的代码,复制到最后的DbSet属性下面

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Course>().ToTable("Course");
modelBuilder.Entity<Enrollment>().ToTable("Enrollment");
modelBuilder.Entity<Student>().ToTable("Student");
}

使用依赖注入的方式来注入DbContext

ASP.NET Core默认实现了依赖注入。在程序启动的时候使用依赖注入将服务(EF的数据库上下文)注入。这些服务的组件(如MVC控制器)通过构造函数的参数实现,下面我们会逐步实现。

首先我们打开“Startup.cs”类,然后将“SchoolContext”注入到ConfigureServices方法中。

services.AddDbContext<SchoolContext>(options =>
options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));

连接字符串的名称是通过DbContextOptionsBuilder对象的方法进行上下文调用的。

而在ASP.NET CORE中的连接字符串是通过“appsettings.json”文件实现的。

如下代码:

{
"ConnectionStrings": {
"DefaultConnection": "Server=(localdb)\\mssqllocaldb;Database=ContosoUniversity1;Trusted_Connection=True;MultipleActiveResultSets=true"
},
"Logging": {
"IncludeScopes": false,
"LogLevel": {
"Default": "Debug",
"System": "Information",
"Microsoft": "Information"
}
}
}

SQL Server Express LocalDB

上面的链接字符串说下吧,连接字符串指定SQL Server LocalDB数据库。LocalDB是一个轻量级版本的SQL Server Express数据库引擎,用于开发环境、而不是生产。LocalDB开始于需求和运行在用户模式下,所以没有复杂的配置。默认情况下,LocalDB创建。

mdf数据库文件在C:/用户/ wer_ltm 文件夹中

我肯定不是这样干的。我们修改下链接字符串

Data Source=.; Database=MaterialCirculation; User ID=sa; Password=123;

初始化数据库并且添加一些测试数据到数据库中

EF框架默认生成的数据库 是一个空数据库,为了我们的测试、开发方便我们添加一些测试数据到数据库中。

这里我们使用EnsureCreated方法来自动创建数据库。在后面的教程中,我们会通过Codefirst迁移的方式来修改数据库而不是传统的删除并重新创建一个数据库的方式来改变模型架构。

在"Data"文件夹中,创建一个“DbInitializer”类,然后把现有代码替换为以下代码:

using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
using ContosoUniversity.Models; namespace ContosoUniversity.Data
{
public static class DbInitializer
{
public static void Initialize(SchoolContext context)
{
context.Database.EnsureCreated(); // Look for any students.
if (context.Students.Any())
{
return; // DB has been seeded
} var students = new Student[]
{
new Student{FirstMidName="Carson",LastName="Alexander",EnrollmentDate=DateTime.Parse("2005-09-01")},
new Student{FirstMidName="Meredith",LastName="Alonso",EnrollmentDate=DateTime.Parse("2002-09-01")},
new Student{FirstMidName="Arturo",LastName="Anand",EnrollmentDate=DateTime.Parse("2003-09-01")},
new Student{FirstMidName="Gytis",LastName="Barzdukas",EnrollmentDate=DateTime.Parse("2002-09-01")},
new Student{FirstMidName="Yan",LastName="Li",EnrollmentDate=DateTime.Parse("2002-09-01")},
new Student{FirstMidName="Peggy",LastName="Justice",EnrollmentDate=DateTime.Parse("2001-09-01")},
new Student{FirstMidName="Laura",LastName="Norman",EnrollmentDate=DateTime.Parse("2003-09-01")},
new Student{FirstMidName="Nino",LastName="Olivetto",EnrollmentDate=DateTime.Parse("2005-09-01")}
};
foreach (Student s in students)
{
context.Students.Add(s);
}
context.SaveChanges(); var courses = new Course[]
{
new Course{CourseID=1050,Title="Chemistry",Credits=3,},
new Course{CourseID=4022,Title="Microeconomics",Credits=3,},
new Course{CourseID=4041,Title="Macroeconomics",Credits=3,},
new Course{CourseID=1045,Title="Calculus",Credits=4,},
new Course{CourseID=3141,Title="Trigonometry",Credits=4,},
new Course{CourseID=2021,Title="Composition",Credits=3,},
new Course{CourseID=2042,Title="Literature",Credits=4,}
};
foreach (Course c in courses)
{
context.Courses.Add(c);
}
context.SaveChanges(); var enrollments = new Enrollment[]
{
new Enrollment{StudentID=1,CourseID=1050,Grade=Grade.A},
new Enrollment{StudentID=1,CourseID=4022,Grade=Grade.C},
new Enrollment{StudentID=1,CourseID=4041,Grade=Grade.B},
new Enrollment{StudentID=2,CourseID=1045,Grade=Grade.B},
new Enrollment{StudentID=2,CourseID=3141,Grade=Grade.F},
new Enrollment{StudentID=2,CourseID=2021,Grade=Grade.F},
new Enrollment{StudentID=3,CourseID=1050},
new Enrollment{StudentID=4,CourseID=1050,},
new Enrollment{StudentID=4,CourseID=4022,Grade=Grade.F},
new Enrollment{StudentID=5,CourseID=4041,Grade=Grade.C},
new Enrollment{StudentID=6,CourseID=1045},
new Enrollment{StudentID=7,CourseID=3141,Grade=Grade.A},
};
foreach (Enrollment e in enrollments)
{
context.Enrollments.Add(e);
}
context.SaveChanges();
}
}}

上面的代码会先检查数据库是否有任何的学生信息,如果没有的话,他会假定数据库需要新的测试种子数据,这里选择了将数据添加到数组中,没有选择List集合来进行性能的优化。

在Startup.cs中,修改Configure 中的方法,以便程序在启动的时候调用Seed方法。

  • 首先修改Configure 方法,添加构造参数"SchoolContext "到方法中,这样ASP.NET 的依赖注入可以提供服务给DbInitializer类。
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory, SchoolContext context)
{
loggerFactory.AddConsole(Configuration.GetSection("Logging"));
loggerFactory.AddDebug();

添加代码** DbInitializer.Initialize(context);方法,在整个Configure**方法的最下面。

    app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}");
}); DbInitializer.Initialize(context);//记得添加这行
}

现在,当你第一次运行程序的时候会给你创建测试数据。每当你更改实体的时候,也就是数据模型的时候,可以删除数据库。更新种子数据并重新创建一个新的数据库。在以后的教程中,你会学会如何通过codefirst的方式通过迁移的方式来进行数据的修改和创建。

创建一个控制器和视图

接下来,我们使用visual studio 中脚手架功能,添加MVC的控制器和视图,将使用EF的查询和保存数据。

通过脚手架功能,我们可以自动创建一个CRUD的功能。你可以通过修改脚手架生成的代码来满足你的业务要求。当你的类发生变化的时候,你可以通过脚手架重新生成代码。

在VS2017 脚手架被叫做了基架 ,在我看来一样的难听。。。

  • 右键选择“Controllers”文件夹,然后选择添加>新搭基建项目

  • 在对话框中,

    • 选择“视图使用EntityFramework的MVC控制器”

      -- 点击添加
  • 添加控制器对话框中

    -- 模型类:选择Student

    -- 数据上下文类:选择 SchoolContext

    -- 接收默认的StudentController作为名称

    -- 点击添加

当你点击“添加的时候”,VS 基架引擎会自动生成一个“StudentController.cs”文件和一组视图文件(.cshtml)。

打开StudentController控制器,你会发现SchoolContext 作为了构造函数的参数。

namespace ContosoUniversity.Controllers
{
public class StudentsController : Controller
{
private readonly SchoolContext _context; public StudentsController(SchoolContext context)
{
_context = context;
}

我们之前在“Startup.cs”中已经配置了依赖注入。现在

ASP.NET会通过依赖注入的方式,将SchoolContext 注入到控制器中。

控制器中包含了一个Index的Action 方法,它会将数据库中的所有学生信息都显示出来。

await _context.Students.ToListAsync() 方法会从Student实体通过读取数据库上下文属性获取学生列表。

public async Task<IActionResult> Index()
{
return View(await _context.Students.ToListAsync());
}

这里是采用了异步方法。我们在后面讲解

我们先打开“Views/Students/Index.cshtml ”视图文件:

@model IEnumerable<ContosoUniversity.Models.Student>

@{
ViewData["Title"] = "Index";
} <h2>Index</h2> <p>
<a asp-action="Create">Create New</a>
</p>
<table class="table">
<thead>
<tr>
<th>
@Html.DisplayNameFor(model => model.LastName)
</th>
<th>
@Html.DisplayNameFor(model => model.FirstMidName)
</th>
<th>
@Html.DisplayNameFor(model => model.EnrollmentDate)
</th>
<th></th>
</tr>
</thead>
<tbody>
@foreach (var item in Model) {
<tr>
<td>
@Html.DisplayFor(modelItem => item.LastName)
</td>
<td>
@Html.DisplayFor(modelItem => item.FirstMidName)
</td>
<td>
@Html.DisplayFor(modelItem => item.EnrollmentDate)
</td>
<td>
<a asp-action="Edit" asp-route-id="@item.ID">Edit</a> |
<a asp-action="Details" asp-route-id="@item.ID">Details</a> |
<a asp-action="Delete" asp-route-id="@item.ID">Delete</a>
</td>
</tr>
}
</tbody>
</table>

好了 按 CTRL + F5 运行项目或选择调试 > 开始执行(不调试)。

在菜单上选择Student按钮。然后就可以看到我们的数据信息。

查看数据库

如果你是改了连接字符串的话,直接打开数据库看表吧。如果你没有更改,那就不要跳过这里了。

我们刚刚说过了,你如果采用的是免费版本的话,现在要查看数据库就需要打开

工具-连接数据库

展开表信息,然后选择Student 然后右键表,点击查看数据。

公约

由于EF框架的公约/设定,为了让EntityFramework能够为您创建一个完整的数据库,你只需要编写很少的代码。

  • DbSet 属性的名称作为表的名称。对于不是由DbSet属性引用的实体,实体类名将作为表的名称
  • 实体属性名称会作为表的列名称
  • 名为ID或者classnameID的实体属性会被识别为主键属性
  • 如果属性被命名,属性则会被EF作为外键属性。(例如:StudentID对于Student导航属性,因为Student实体的主键为ID)。外键属性也可以随意命名。(例如:EnrollmentID因为Enrollment实体的主键是EnrollmentID)

一些常规的设定是可以进行覆盖的。例如你可以显示指定表的名称,就如我们之前做的,自定义表的名称。

当然你也可以设置列名称并将任何属性设置为主键或者外键。在后面的教程中我们会涉及。

关于异步代码

异步编程是ASP.NET Core和EF Core的默认模式。

Web服务器的线程数量是有限的,在高负载的情况下,可能所有的线程都被占用了。当发生这种情况的时候,服务器不能处理新的请求,直到线程被释放出来。再以前使用同步代码请求,许多线程可能被绑定,他们实际上没有做任何工作,因为他们正在等待I/O完成。使用异步写代码,当进程等待I/O完成的时候,它的线程就会被释放,服务器用于处理其他请求。因此,异步代码使服务器资源能够更有效地使用,并且服务器能够无延迟地处理更多流量。

异步代码在运行时引入少量开销,但是对于低流量情况,性能命中是可以忽略的,而对于高流量情况,潜在的性能改进是巨大的。

在以下代码中,async关键字,Task返回值,await关键字和ToListAsync方法使代码异步执行。

public async Task<IActionResult> Index()
{
return View(await _context.Students.ToListAsync());
}
  • 该async关键字告诉编译器生成方法不会回调和自动创建Task返回的对象。
  • 返回类型Task表示正在进行的工作与类型的结果IActionResult。
  • 该await关键字使编译器将该方法拆分为两部分。第一部分以异步启动的操作结束。第二部分被放入一个在操作完成时被调用的回调方法中。
  • ToListAsync是ToList扩展方法的异步版本。

当你使用EntityFramework的异步代码的时候,你需要注意一些事情:

  • 只有查询或者发送命令到数据库的时候才能使用异步语句。如:ToListAsync,SingleOrDefaultAsync,和SaveChangesAsync。不包含 类型为IQueryable,修改命令。比如
var students = context.Students.Where(s => s.LastName == "Davolio").
  • EF的上下文不是线程安全的:不要尝试执行/并行多个操作。当您调用任何异步EF方法的时候,始终使用“await”关键字。

  • 如果你想使用异步的性能优势,请确保你使用的任何包(例如分页),他们调用的Entity Framework 方法,使用async发送到数据库中。

基于Asp.Net Core Mvc和EntityFramework Core 的实战入门教程系列-2的更多相关文章

  1. 基于Asp.Net Core Mvc和EntityFramework Core 的实战入门教程系列-1

    来个目录吧: 第一章 第二章 第三章 暂时就这么多.后面路线更新吧 本系列文章为翻译加上我个人的使用心得理解,希望帮助热爱学习的程序员. 珍重声明:本系列文章会跟原文有点出入,去掉了罗里吧嗦的文字. ...

  2. 基于Asp.Net Core Mvc和EntityFramework Core 的实战入门教程系列-4

    来个目录吧: 第一章-入门 第二章- Entity Framework Core Nuget包管理 第三章-创建.修改.删除.查询 第四章-排序.过滤.分页.分组 第五章-迁移,EF Core 的co ...

  3. 基于Asp.Net Core Mvc和EntityFramework Core 的实战入门教程系列-3

    来个目录吧: 第一章 第二章 第三章 暂时就这么多.后面路线更新吧 创建.查询.更新.删除 这章主要讲解使用EF完成 增删改查的功能. 自定义"详情信息"页面 我们通过基架生成的代 ...

  4. 基于Asp.Net Core Mvc和EntityFramework Core 的实战入门教程系列-5

    来个目录吧: 第一章-入门 第二章- Entity Framework Core Nuget包管理 第三章-创建.修改.删除.查询 第四章-排序.过滤.分页.分组 第五章-迁移,EF Core 的co ...

  5. 基于asp.net(C#)MVC+前端bootstrap+ztree+lodash+jquery技术-Angel工作室通用权限管理

    一.Angel工作室简单通用权限系统简介 AngelRM(Asp.net MVC Web api)是基于asp.net(C#)MVC+前端bootstrap+ztree+lodash+jquery技术 ...

  6. 基于ASP.NET的MVC框架下的MvcPaper分页控件的使用技术

    using System; using System.Collections.Generic; using System.Linq; using System.Web; using Webdiyer. ...

  7. 基于Asp.Net Core 2.1的简单问答社区系统源代码分享

    看见园子里很多人都在分享源代码,我也来凑个热闹. 该项目基于.NET CORE 2.1(其实是从1.1开始开发的),经历过不停的调整终于有个能拿出手的版本了,第一次在博客园发文章. 使用到的技术以及框 ...

  8. ASP.NET Core 入门教程 6、ASP.NET Core MVC 视图布局入门

    一.前言 1.本教程主要内容 ASP.NET Core MVC (Razor)视图母版页教程 ASP.NET Core MVC (Razor)带有Section的视图母版页教程 ASP.NET Cor ...

  9. ASP.NET Core 入门笔记7,ASP.NET Core MVC 视图布局入门

    一.前言 1.本教程主要内容 ASP.NET Core MVC (Razor)视图母版页教程 ASP.NET Core MVC (Razor)带有Section的视图母版页教程 ASP.NET Cor ...

随机推荐

  1. html 5 本地数据库(Web Sql Database)核心方法openDatabase、transaction、executeSql 详解

    Web SQL数据库API实际上不是HTML5规范的组成部分,而是单独的规范.它通过一套API来操纵客户端的数据库.Safari.Chrome. Firefox.Opera等主流浏览器都已经支持Web ...

  2. Windows上的音频采集技术

    在制作发布端的时候,需要采集到声卡的输出信号,以便与麦克风的输入信号进行混音,对于音频处理的技术,主要有如下几种: 采集麦克风输入 采集声卡输出 将音频数据送入声卡进行播放 对多路音频输入进行混音处理 ...

  3. Spring 集成 Dubbo

    Duboo是什么 DUBBO是一个分布式服务框架,致力于提供高性能和透明化的RPC远程服务调用方案,是阿里巴巴SOA服务化治理方案的核心框架,每天为2,000+个服务提供3,000,000,000+次 ...

  4. 机器学习实战笔记(1)——k-近邻算法

    机器学习实战笔记(1) 1. 写在前面 近来感觉机器学习,深度学习神马的是越来越火了,从AlphaGo到Master,所谓的人工智能越来越NB,而我又是一个热爱新潮事物的人,于是也来凑个热闹学习学习. ...

  5. 常用的JQ函数

    /// <reference path="jquery-1.8.0.min.js"> /* * DIV或元素居中 * @return */ jQuery.fn.mCen ...

  6. WPF开发进阶 - Fody/PropertyChanged(一)

    INotifyPropertyChanged 在WPF MVVM模式开发中,实现INotifyPropertyChanged的ViewModel是非常重要且常见的类: public class Mai ...

  7. 5种IE hasLayoutt的属性及其值

    hasLayout 是Windows  Internet  Explore 渲染引擎的一个内部组成部分.在 Internet Explore 中,一个元素要么自己对自身内容进行计算大小和组织,要么依赖 ...

  8. JS闭包深入理解(理解篇)

    看书的时候很是不明白为啥变量老是五,经过认真思考的出一下理解: function box() {   var arr = [];   for (var i = 0; i < 5; i++) {  ...

  9. context:annotation-config, mvc:annotation-driven, context:compont-scan 区别

    当我们需要使用BeanPostProcessor时,直接在Spring配置文件中定义这些Bean显得比较笨拙,例如: 使用@Autowired注解,必须事先在Spring容器中声明AutowiredA ...

  10. Hbase数据库安装

    一.环境准备 1.Ubuntu14.04-server 2.ssh 3.jdk1.6 4.hbase-0.98.19-hadoop2-bin.tar.gz(下载地址http://www.apache. ...