从实体框架核心开始:构建一个ASP。NET Core应用程序与Web API和代码优先开发
介绍 在上一篇文章中,我们了解了实体框架的基本概念,并了解了如何使用实体框架的数据库优先开发快速构建web应用程序。 这一次,我们将构建一个简单而现实的ASP。NET Core应用程序,展示了实体框架核心的特性。但是在我们进一步深入之前,让我们快速回顾一下代码优先和数据库优先设计工作流的高级差异和关键好处,然后我们将决定在我们的项目中使用哪一个。 设计工作流 请记住,Entity Framework Core支持两种主要的设计工作流:代码优先方法,即创建类(POCO实体)并从中生成一个新的数据库。数据库优先方法允许您使用现有数据库并根据数据库模式生成类。下表显示了每个设计工作流如何工作的高级流程。 代码先数据库先 图1:EF核心设计工作流 代码优先(新数据库) 如果你不了解你的数据库的全貌,这是一个不错的选择,因为你可以更新你的普通旧的类对象(POCO)实体,并让EF同步你的更改到你的数据库。换句话说,您可以轻松地添加或删除类中定义的特性,而不必担心使用迁移同步数据库。您不必担心您的数据库,因为EF将为您处理创建。从本质上讲,数据库只是一种没有逻辑的存储介质。您将完全控制代码。您只需定义和创建POCO实体,并让EF为您生成相应的数据库。缺点是,如果您手动更改数据库中的某些内容,可能会丢失它们,因为您的代码定义了数据库。它很容易修改和维护,因为没有自动生成的代码。 数据库优先(现有数据库) 如果您擅长数据库,或者您有一个由dba设计的数据库,或者您已经开发了一个现有的数据库,那么这是一个不错的选择。您不必担心创建POCO对象,因为EF将根据您现有的数据库为您生成它们。如果希望在POCO实体中添加其他特性,可以创建部分类。您可以手动修改数据库,因为数据库定义了您的POCO实体(领域模型)。为数据库配置提供了更多的灵活性和控制。 结束 如果您有一个单独开发的现有数据库,或者时间对您来说很重要,那么可以选择数据库优先。数据库首先使我们能够更快地构建应用程序,因为你可以在仅仅几次点击中生成你的领域模型。对于较大的应用程序或长期的客户端项目,代码首先为我们提供了创建最高效程序所需的控制,还为我们提供了版本控制数据库的保护和一致性,同时减少了膨胀。 请记住,两种设计工作流都有其优点。当您开始一个新项目时,选择使用哪种方法是一个非常重要的决策,所以一定要知道您的项目需要什么和期望什么。 下表列出了我个人在构建应用程序时应该考虑的设计工作流的建议。 ,数据库第一个代码第一个代码生成使用您喜欢的工具创建数据库模式。g SQL Server Management Studio),并使用脚本导入该数据库和模式,为您搭建所有框架。用您想要跟踪的属性编写类,然后定义其他类之间的关系。同步更改将创建一个表示更改的迁移,并针对数据库运行脚本。您可以在需要时创建迁移,代码将使用这些文件(以及目标数据库中对应的表)来保持模式更改与代码所期望的同步。换句话说,您可以在不编写任何SQL的情况下管理数据库模式。最适合它的项目类型对现有项目使用database-first更有意义,因为您不需要进行大的更改来扩展数据库。当数据库由DBA单独开发,并归其他团队所有时,您也可以将此方法用于新项目。如果从零开始开发一个新的应用程序,那么代码优先更有意义,因为您可以在开发过程中增长和发展模型。 在一个选择 通过判断两种方法之间的差异,我们可以很容易地看到,不存在总体上更好或最好的方法:相反,我们可以说每个项目场景都可能有一个最合适的方法。 现在,关于我们将要构建的应用程序,考虑到我们还没有数据库,而我们的目标是一个灵活的、可变的小规模数据结构,采用代码优先的方法将会可能是个不错的选择。这就是我们要做的,构建一个ASP。NET Core应用程序从头开始使用实体框架核心的代码优先方法。 ASP。网络核心革命 为了总结在ASP中发生的事情。在过去的一年里,NET世界并不是一项简单的任务:简而言之,我们可以说,我们无疑正面临着。NET框架自诞生之年以来最重要的一系列变化。ASP。NET Core是asp.net的一个完整的重新实现。它将以前所有的web应用程序技术如MVC、web API和Razor Pages统一为一个单一的编程模块,即以前的MVC6。新框架引入了一个全功能的跨平台组件,也称为。net Core,并附带了一个全新的开源。net编译平台(目前称为Roslyn)和一个跨平台运行时CoreCLR。 ASP。NET Core是一个新的开源跨平台框架,用于构建现代的基于云的web应用。它从头开始构建,为部署到云或本地服务器上的web应用提供了一个优化的开发框架。此外,它还被重新设计,使ASP。NET leaner,模块化(这样你就可以添加你的应用程序需要的特性),跨平台(这样你就可以在Windows, Mac或Linux上轻松地开发和运行你的应用程序,这非常棒)和云优化(这样你就可以在云上部署和调试应用程序)。 三个队员,一个进球 我们的主要目标是使用三种尖端技术构建一个数据驱动的web应用程序:NET Core MVC, Web API和实体框架核心。 ASP。NET Web API是一个用于构建HTTP服务的框架,也是在。NET框架上构建RESTful应用程序的理想平台。 ASP。NET Core MVC是一种基于模式的构建动态网站的方法,它能够清晰地分离关注点,并让你完全控制标记或HTML。 我们将使用ASP。NET Core来构建REST API (Web API),并作为前端框架(MVC)来生成页面或视图(又名MVC)。一个用户界面)。另一方面,我们将使用实体框架核心作为数据访问机制来处理来自数据库的数据。下图说明了每种技术如何相互作用。 图2:高级流程流 上图说明了当用户从数据库中访问信息时流程是如何工作的。当用户请求一个页面时,它将打开视图/UI (MVC)。当用户不想查看某些数据时,MVC应用程序将与Web API服务对话以处理数据。然后,Web API将与EF Core通信来处理实际的请求(CRUD操作),然后在必要时更新数据库。 设置一个ASP。NET Core Web API项目 现在我们已经知道了ASP的高级概述。NET Core和EF Core,是时候让我们动手用ASP构建一个简单的、数据驱动的web应用程序了。带有实体框架核心的NET Core。让我们首先创建Web API项目。 启动Visual Studio 2017,让我们创建一个新的ASP。NET Core Web应用程序项目。为此,选择File >新比;项目。在新建项目对话框中选择visualc# >网络比;ASP。NET Core Web应用程序(。NET Core)如下图所示: 图3:创建新的ASP。网络核心Project 在对话框中,输入项目名称,然后单击OK按钮继续下一步。注意,对于这个特定的演示,我们将项目命名为“StudentApplication.API”。现在单击Web API模板,如下图所示。 图4:ASP。NET Core Web API模板 与新的ASP。NET Core项目模板对话框,现在可以选择使用ASP。NET Core 1.0或1.1使用屏幕顶部的组合框。在撰写本文时,默认的模板是ASP。1.1网络核心。此外,您可以通过屏幕左下角的复选框选择将容器支持添加到项目中。这个选项补充道: 一个Docker合成项目来定义你如何实例化你的容器多项目/容器调试支持容器 为了简单起见,我们省略容器的使用。 现在再次单击OK按钮,让Visual Studio为您生成默认文件。下图显示了生成的文件: 图5:Visual Studio默认生成的文件 让我们快速概览一下生成的每个文件。 如果你已经知道了ASP的核心重大变化。然后你可以跳过这部分,但如果你是新的ASP。然后我想强调一下其中的一些变化。如果您使用过以前版本的ASP。在此之前,你会注意到新的项目结构是完全不同的。该项目现在包括这些文件: 连接服务:允许服务提供商创建Visual Studio扩展,可以在不离开IDE的情况下添加到项目中。它还允许让你连接你的ASP。NET Core应用程序或移动服务到Azure存储服务。Connected Services负责处理所有引用和连接代码,并相应地修改配置文件控制器:这是放置所有Web API或MVC控制器类的地方。注意,Web API项目将在默认情况下生成ValuesController.cs文件。是一个文件夹,你所有的静态文件将被放置在其中。这些是web应用程序将直接服务于客户端的资产,包括HTML、CSS、图像和JavaScript文件。appsettings。json:包含应用程序设置。启动。cs:这是你放置启动和配置代码的地方。程序。cs:这是初始化应用程序所需的所有服务的地方。 第一次运行该项目 为了确保我们得到Web API项目所需的一切,让我们试着构建并运行这个项目。我们可以通过按CTRL + F5键来做到这一点。这将编译、构建并自动启动浏览器窗口。如果您得到下面的结果,那么我们可以放心地假设我们的项目是好的。 图6:初始运行 建立实体框架核心 让我们在项目中安装实体框架核心包。去工具>NuGet包管理器->包管理器控制台,并单独运行以下命令: 隐藏,复制Code
Install-Package Microsoft.EntityFrameworkCore.SqlServer
Install-Package Microsoft.EntityFrameworkCore.Tools
在成功安装上述包之后,请确保检查您的项目引用,以确保我们在项目中获得了它们。见下图。 图7:实体框架核心包引用 这些包用于提供实体框架核心迁移。迁移使我们能够基于我们的实体生成数据库、同步和更新表。 创建模型 我们将首先在应用程序的根目录中创建“Models”文件夹。在Models 文件夹中添加一个新的类文件,并将其命名为“Student”。,然后将默认生成的代码替换为以下代码: 隐藏,复制Code
using System;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema; namespace StudentApplication.API.Models
{
public class Student
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public long StudentId { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string Gender { get; set; }
public DateTime? DateOfBirth { get; set; }
public DateTime? DateOfRegistration { get; set; }
public string PhoneNumber { get; set; }
public string Email { get; set; }
public string Address1 { get; set; }
public string Address2 { get; set; }
public string City { get; set; }
public string State { get; set; }
public string Zip { get; set; }
}
}
上面的代码只是一个包含一些属性的简单类。您可能已经注意到,我们使用Key和DatabaseGenerated属性装饰了StudentId属性。这是因为我们将把这个类转换为一个数据库表,而StundentId将作为我们的主键,并使用自动递增的标识。这些属性驻留在System.ComponentModel.DataAnnotations中。有关数据注释的更多细节,请参见:使用数据注释进行模型验证 创建空数据库 现在是创建数据库的时候了。打开SQL Express Management Studio,执行以下命令: 隐藏,复制Code
CREATE DATABASE StundentApplication
上面的命令应该在您的本地创建一个空白数据库。 定义一个DBContext 实体框架核心代码优先的开发方法要求我们创建一个从DbContext类继承的数据访问上下文类。现在让我们在Models文件夹下添加一个新类。将类命名为“ApplicationContext”,然后复制下面的代码: 隐藏,复制Code
using Microsoft.EntityFrameworkCore; namespace StudentApplication.API.Models
{
public class ApplicationContext: DbContext
{
public ApplicationContext(DbContextOptions opts) : base(opts)
{
} public DbSet<Student> Students { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
}
}
}
上面的代码定义了组成我们模型的上下文和实体类。anbsp;DbContext必须有一个DbContextOptions的实例才能执行。这可以通过覆盖configuration进行配置,或者通过构造函数参数从外部提供。在上面的示例中,我们选择选择构造函数参数来配置DbContext。 通过依赖注入注册DbContext 我们的ApplicationContext类将充当一个服务。该服务将在应用程序启动期间通过依赖注入(DI)注册。这将使我们的API控制器或其他服务能够通过构造函数参数或属性访问ApplicationContext。 为了让其他服务使用ApplicationContext,我们将其注册为服务。要启用该服务,请执行以下步骤: 1. 在appsettings下定义以下数据库连接字符串。json文件: 隐藏,复制Code
,
"ConnectionString": {
"StudentApplicationDB": "Server=SSAI-L0028-HP\\SQLEXPRESS;Database=StundentApplication;Trusted_Connection=True;"
}
2. 打开Startup.cs,声明以下名称空间引用: 隐藏,复制Code
using Microsoft.EntityFrameworkCore;
using StudentApplication.API.Models;
using StudentApplication.API.Models.DataManager;
using StudentApplication.API.Models.Repository
在ConfigureServices方法中添加以下代码来将ApplicationContext注册为服务。 隐藏,复制Code
public void ConfigureServices(IServiceCollection services)
{
// Add framework services.
services.AddDbContext<ApplicationContext>(opts => opts.UseSqlServer(Configuration["ConnectionString:StudentApplicationDB"]));
services.AddSingleton(typeof(IDataRepository<Student, long>), typeof(StudentManager));
services.AddMvc();
}
添加迁移 我们的下一步是添加代码优先迁移。迁移可以自动根据我们的模型创建数据库。在PM控制台中运行以下命令: 隐藏,复制Code
PM> Add-Migration StudentApplication.API.Models.ApplicationContext
当命令执行成功后,应该会生成如下图所示的迁移文件: 图8:生成的EF迁移文件 现在,切换回包管理器控制台并运行以下命令: 隐藏,复制Code
PM> update-database
上面的命令应该会基于我们的实体生成数据库表。下面是生成的数据库表的示例截图: 图9:生成的表 实现一个简单的数据存储库 我们不希望我们的API控制器直接访问我们的ApplicationContext服务,相反,我们将让其他服务处理我们的ApplicationContext和API控制器之间的通信。话虽如此,我们将实现一个基本的通用存储库来处理应用程序中的数据访问。在Models下添加一个新文件夹,并将其命名为“Repository”。创建一个新接口,并将其命名为“IDataRepository”。更新该文件中的代码,使其看起来类似于下面的代码: 隐藏,复制Code
using System.Collections.Generic; namespace StudentApplication.API.Models.Repository
{
public interface IDataRepository<TEntity, U> where TEntity : class
{
IEnumerable<TEntity> GetAll();
TEntity Get(U id);
long Add(TEntity b);
long Update(U id, TEntity b);
long Delete(U id);
} }
上面的代码定义了IDataRepository接口。接口只是方法的骨架,没有实际的实现。这个接口将被注入到我们的API控制器中,因此我们只需要与接口对话,而不是与存储库的实际实现对话。使用接口的主要好处之一是使我们的代码易于重用和维护。 创建DataManager 接下来,我们将创建一个实现IDataRepository接口的具体类。在Models下添加一个新文件夹,并将其命名为“DataManager”。创建一个新类,并将其命名为“StudentManager”。更新该文件中的代码,使其看起来类似以下代码: 隐藏,收缩,复制Code
using StudentApplication.API.Models.Repository;
using System.Collections.Generic;
using System.Linq; namespace StudentApplication.API.Models.DataManager
{
public class StudentManager : IDataRepository<Student, long>
{
ApplicationContext ctx;
public StudentManager(ApplicationContext c)
{
ctx = c;
} public Student Get(long id)
{
var student = ctx.Students.FirstOrDefault(b => b.StudentId == id);
return student;
} public IEnumerable<Student> GetAll()
{
var students = ctx.Students.ToList();
return students;
} public long Add(Student stundent)
{
ctx.Students.Add(stundent);
long studentID = ctx.SaveChanges();
return studentID;
} public long Delete(long id)
{
int studentID = 0;
var student = ctx.Students.FirstOrDefault(b => b.StudentId == id);
if (student != null)
{
ctx.Students.Remove(student);
studentID = ctx.SaveChanges();
}
return studentID;
} public long Update(long id, Student item)
{
long studentID = 0;
var student = ctx.Students.Find(id);
if (student != null)
{
student.FirstName = item.FirstName;
student.LastName = item.LastName;
student.Gender = item.Gender;
student.PhoneNumber = item.PhoneNumber;
student.Email = item.Email;
student.DateOfBirth = item.DateOfBirth;
student.DateOfRegistration = item.DateOfRegistration;
student.Address1 = item.Address1;
student.Address2 = item.Address2;
student.City = item.City;
student.State = item.State;
student.Zip = item.Zip; studentID = ctx.SaveChanges();
}
return studentID;
}
}
}
DataManager类处理应用程序的所有数据库操作。这个类的目的是将实际的数据操作逻辑从API控制器中分离出来,并拥有一个中央类来处理创建、更新、获取和删除(CRUD)操作。 目前,DataManager类包含五个方法:Get()方法通过传递Id从数据库中获取一个特定的学生记录。它使用LINQ语法查询数据并返回一个学生对象。GetAll()方法从数据库中获取所有学生记录,您可以根据方法名猜到这一点。Add()方法在数据库中创建一个新的学生记录。Delete()方法根据Id从数据库中删除特定的学生记录。最后,Update()方法更新数据库中的特定学生记录。以上方法都使用LINQ来查询数据库中的数据。 创建API控制器 现在我们的DataManager已经全部设置好了,现在是时候创建API控制器并公开一些端点来处理CRUD操作了。 右键点击Controllers文件夹然后选择Add >新项目在Web API控制器类。将类命名为“StudentController”,如下图所示,然后单击Add。 图10:新的Web API控制器类 现在替换默认生成的代码,使其看起来类似下面的代码: 隐藏,收缩,复制Code
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using StudentApplication.API.Models;
using StudentApplication.API.Models.Repository;
using StudentApplication.API.Models.DataManager; namespace StudentApplication.API.Controllers
{
[Route("api/[controller]")]
public class StudentController : Controller
{
private IDataRepository<Student,long> _iRepo;
public StudentController(IDataRepository<Student, long> repo)
{
_iRepo = repo;
} // GET: api/values
[HttpGet]
public IEnumerable<Student> Get()
{
return _iRepo.GetAll();
} // GET api/values/5
[HttpGet("{id}")]
public Student Get(int id)
{
return _iRepo.Get(id);
} // POST api/values
[HttpPost]
public void Post([FromBody]Student student)
{
_iRepo.Add(student);
} // POST api/values
[HttpPut]
public void Put([FromBody]Student student)
{
_iRepo.Update(student.StudentId,student);
} // DELETE api/values/5
[HttpDelete("{id}")]
public long Delete(int id)
{
return _iRepo.Delete(id);
}
}
}
StudentController从控制器基派生,并通过使用Route属性修饰它,使该类成为一个Web API控制器。 如果您注意到,我们已经使用控制器注入订阅了IDataRepository接口。因此,我们没有直接使用StudentManager类,而是直接与接口对话并使用可用的方法。 StudentController有5个主要方法/端点。第一个Get()方法类来自IDataRepository接口的GetAll()方法,基本上从数据库中返回所有的student列表。第二个Get()方法根据Id返回一个特定的学生对象。注意,第二个Get()方法用[HttpGet("{Id}")]装饰,这意味着将“Id”附加到路由模板。“{id}”是一个占位符变量,用于表示Student对象的id。当第二个Get()方法被调用时,它将URL中的值“{id}”赋给该方法的id参数。Post()方法向数据库添加一条新记录。Put()方法更新数据库中的一条特定记录。注意,Post()和Put()都使用了[FromBody]标记。当参数具有[FromBody]时,Web API使用内容类型头来选择格式化程序。最后,Delete()方法从数据库中删除特定的记录。 创建一个ASP。NET Core MVC应用程序 至此,我们已经具备了构建这个演示的UI部分所需的所有需求。首先,我们创建数据库,使用实体框架核心代码设置数据访问——首先,最后,我们已经准备好了Web API。现在是时候创建一个使用web API端点的web应用程序了。 现在继续并创建一个新的ASP。网的核心项目。右键单击解决方案,选择Add >新项目在ASP。NET Core Web应用程序(。网络核心)。你应该能看到这样的东西: , 图11:新ASP。NET Core Web应用程序项目 为了更加一致,将这个项目命名为“StudentApplication”。,然后点击确定。在下一个对话框中,在ASP下选择“Web Application”。NET Core 1.1模板,然后单击OK,让Visual Studio生成默认文件对我们来说。 整合Newtonsoft.Json 让我们继续安装Newtonsoft。项目中的Json包。右键单击项目,然后选择Manage Nuget Packages。在浏览选项卡中,输入“NewtonSoft”,结果应该是这样的: 图12:添加Newtonsoft。Json包 单击“install”将最新的软件包版本添加到项目中。 我们将使用牛顿软件。在我们的代码中序列化和反序列化一个API请求中的对象。 辅助类 在应用程序的根目录创建一个新类,并将其命名为“Helper”。然后复制下面的代码: 隐藏,收缩,复制Code
using System;
using System.Net.Http;
using System.Net.Http.Headers; namespace StudentApplication.Web.Helper
{ public class StudentAPI
{
private string _apiBaseURI = "http://localhost:60883";
public HttpClient InitializeClient()
{
var client = new HttpClient();
//Passing service base url
client.BaseAddress = new Uri(_apiBaseURI); client.DefaultRequestHeaders.Clear();
//Define request data format
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json")); return client; }
} public class StudentDTO
{
public long StudentId { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string Gender { get; set; }
public DateTime? DateOfBirth { get; set; }
public DateTime? DateOfRegistration { get; set; }
public string PhoneNumber { get; set; }
public string Email { get; set; }
public string Address1 { get; set; }
public string Address2 { get; set; }
public string City { get; set; }
public string State { get; set; }
public string Zip { get; set; }
}
}
cs文件包含两个主要类:StudentAPI和StudentDTO。StudentAPI类有一个方法InitializeClient()来初始化HttpClient对象。稍后我们将需要此方法来访问API端点。StudentDTO只是一个普通类,它模仿了API控制器中的Student类。我们将使用这个类作为API请求的对象参数。 引用: 注意:在本地测试时,您必须将_apiBaseURI变量中的值更改为Web API项目的托管位置,或者如果您使用IISExpress在本地运行,则更改由Visual Studio生成的端口号的值。 控制器 在Controllers文件夹下添加一个新控制器,并将其命名为“StudentsController”。用下面的代码替换默认生成的代码: 隐藏,收缩,复制Code
using Microsoft.AspNetCore.Mvc;
using Newtonsoft.Json;
using StudentApplication.Web.Helper;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Text;
using System.Threading.Tasks; namespace StudentApplication.Web.Controllers
{
public class StudentsController : Controller
{
StudentAPI _studentAPI = new StudentAPI(); public async Task<IActionResult> Index()
{
List<StudentDTO> dto = new List<StudentDTO>(); HttpClient client = _studentAPI.InitializeClient(); HttpResponseMessage res = await client.GetAsync("api/student"); //Checking the response is successful or not which is sent using HttpClient
if (res.IsSuccessStatusCode)
{
//Storing the response details recieved from web api
var result = res.Content.ReadAsStringAsync().Result; //Deserializing the response recieved from web api and storing into the Employee list
dto = JsonConvert.DeserializeObject<List<StudentDTO>>(result); }
//returning the employee list to view
return View(dto);
} // GET: Students/Create
public IActionResult Create()
{
return View();
} // POST: Students/Create
[HttpPost]
[ValidateAntiForgeryToken]
public IActionResult Create([Bind("StudentId,FirstName,LastName,Gender,DateOfBirth,DateOfRegistration,PhoneNumber,Email,Address1,Address2,City,State,Zip")] StudentDTO student)
{
if (ModelState.IsValid)
{
HttpClient client = _studentAPI.InitializeClient(); var content = new StringContent(JsonConvert.SerializeObject(student), Encoding.UTF8,"application/json");
HttpResponseMessage res = client.PostAsync("api/student", content).Result;
if (res.IsSuccessStatusCode)
{
return RedirectToAction("Index");
}
}
return View(student);
} // GET: Students/Edit/1
public async Task<IActionResult> Edit(long? id)
{
if (id == null)
{
return NotFound();
} List<StudentDTO> dto = new List<StudentDTO>();
HttpClient client = _studentAPI.InitializeClient();
HttpResponseMessage res = await client.GetAsync("api/student");
if (res.IsSuccessStatusCode)
{
var result = res.Content.ReadAsStringAsync().Result;
dto = JsonConvert.DeserializeObject<List<StudentDTO>>(result);
} var student = dto.SingleOrDefault(m => m.StudentId == id);
if (student == null)
{
return NotFound();
} return View(student);
} // POST: Students/Edit/1
[HttpPost]
[ValidateAntiForgeryToken]
public IActionResult Edit(long id, [Bind("StudentId,FirstName,LastName,Gender,DateOfBirth,DateOfRegistration,PhoneNumber,Email,Address1,Address2,City,State,Zip")] StudentDTO student)
{
if (id != student.StudentId)
{
return NotFound();
} if (ModelState.IsValid)
{
HttpClient client = _studentAPI.InitializeClient(); var content = new StringContent(JsonConvert.SerializeObject(student), Encoding.UTF8, "application/json");
HttpResponseMessage res = client.PutAsync("api/student", content).Result;
if (res.IsSuccessStatusCode)
{
return RedirectToAction("Index");
}
}
return View(student);
} // GET: Students/Delete/1
public async Task<IActionResult> Delete(long? id)
{
if (id == null)
{
return NotFound();
} List<StudentDTO> dto = new List<StudentDTO>();
HttpClient client = _studentAPI.InitializeClient();
HttpResponseMessage res = await client.GetAsync("api/student"); if (res.IsSuccessStatusCode)
{
var result = res.Content.ReadAsStringAsync().Result;
dto = JsonConvert.DeserializeObject<List<StudentDTO>>(result);
} var student = dto.SingleOrDefault(m => m.StudentId == id);
if (student == null)
{
return NotFound();
} return View(student);
} // POST: Students/Delete/5
[HttpPost, ActionName("Delete")]
[ValidateAntiForgeryToken]
public IActionResult DeleteConfirmed(long id)
{
HttpClient client = _studentAPI.InitializeClient();
HttpResponseMessage res = client.DeleteAsync($"api/student/{id}").Result;
if (res.IsSuccessStatusCode)
{
return RedirectToAction("Index");
} return NotFound();
} }
}
看看我们上面做的。 请注意,我们的MVC控制器使用HttpClient对象来访问Web API端点。我们还使用Newtonsoft的JsonConvert.SerializeObject()和JsonConvert.DeserializeObject()来回序列化和反序列化数据。 Index()动作方法从数据库中获取所有学生记录。这个方法被定义为异步的,它返回相应的index&view以及Students 实体的列表。 Create()动作方法简单地返回它对应的视图。overloadcreate()动作方法用[HttpPost]属性装饰,该属性表示该方法只能被post请求调用。如果模型状态在post上是有效的,我们将在此方法中实际处理向数据库添加新数据。 The Edit()动作方法以id作为参数。如果id为null,则返回NotFound(),否则返回相应的数据到视图。overload&edit()动作方法根据id将相应的记录更新到数据库中。 就像其他操作一样,the Delete()操作方法采用id作为参数。如果id为null,则返回NotFound(),否则返回相应的数据到视图。overload&delete()操作方法根据id删除数据库中相应的记录。 视图 下面是索引、创建、编辑和删除视图的对应标记。 指数 隐藏,收缩,复制Code
@model IEnumerable<StudentApplication.Web.Helper.StudentDTO> @{
ViewData["Title"] = "Index";
Layout = "~/Views/Shared/_Layout.cshtml";
} <h2>Index</h2> <p>
<aasp-action="Create">Create New</a>
</p>
<tableclass="table">
<thead>
<tr>
<th>
@Html.DisplayNameFor(model => model.FirstName)
</th>
<th>
@Html.DisplayNameFor(model => model.LastName)
</th>
<th>
@Html.DisplayNameFor(model => model.Gender)
</th>
<th>
@Html.DisplayNameFor(model => model.DateOfBirth)
</th>
<th>
@Html.DisplayNameFor(model => model.DateOfRegistration)
</th>
<th>
@Html.DisplayNameFor(model => model.PhoneNumber)
</th>
<th>
@Html.DisplayNameFor(model => model.Email)
</th>
<th>
@Html.DisplayNameFor(model => model.Address1)
</th>
<th>
@Html.DisplayNameFor(model => model.Address2)
</th>
<th>
@Html.DisplayNameFor(model => model.City)
</th>
<th>
@Html.DisplayNameFor(model => model.State)
</th>
<th>
@Html.DisplayNameFor(model => model.Zip)
</th>
<th></th>
</tr>
</thead>
<tbody>
@foreach (var item in Model)
{
<tr>
<td>
@Html.DisplayFor(modelItem => item.FirstName)
</td>
<td>
@Html.DisplayFor(modelItem => item.LastName)
</td>
<td>
@Html.DisplayFor(modelItem => item.Gender)
</td>
<td>
@Html.DisplayFor(modelItem => item.DateOfBirth)
</td>
<td>
@Html.DisplayFor(modelItem => item.DateOfRegistration)
</td>
<td>
@Html.DisplayFor(modelItem => item.PhoneNumber)
</td>
<td>
@Html.DisplayFor(modelItem => item.Email)
</td>
<td>
@Html.DisplayFor(modelItem => item.Address1)
</td>
<td>
@Html.DisplayFor(modelItem => item.Address2)
</td>
<td>
@Html.DisplayFor(modelItem => item.City)
</td>
<td>
@Html.DisplayFor(modelItem => item.State)
</td>
<td>
@Html.DisplayFor(modelItem => item.Zip)
</td>
<td>
<aasp-action="Edit"asp-route-id="@item.StudentId">Edit</a> |
<aasp-action="Details"asp-route-id="@item.StudentId">Details</a> |
<aasp-action="Delete"asp-route-id="@item.StudentId">Delete</a>
</td>
</tr>
}
</tbody>
</table>
创建 隐藏,收缩,复制Code
@model StudentApplication.Web.Helper.StudentDTO @{
ViewData["Title"] = "Create";
Layout = "~/Views/Shared/_Layout.cshtml";
} <h2>Create</h2> <formasp-action="Create">
<divclass="form-horizontal">
<h4>Student</h4>
<hr/>
<divasp-validation-summary="ModelOnly"class="text-danger"></div>
<divclass="form-group">
<labelasp-for="FirstName"class="col-md-2 control-label"></label>
<divclass="col-md-10">
<inputasp-for="FirstName"class="form-control"/>
<spanasp-validation-for="FirstName"class="text-danger"></span>
</div>
</div>
<divclass="form-group">
<labelasp-for="LastName"class="col-md-2 control-label"></label>
<divclass="col-md-10">
<inputasp-for="LastName"class="form-control"/>
<spanasp-validation-for="LastName"class="text-danger"></span>
</div>
</div>
<divclass="form-group">
<labelasp-for="Gender"class="col-md-2 control-label"></label>
<divclass="col-md-10">
<inputasp-for="Gender"class="form-control"/>
<spanasp-validation-for="Gender"class="text-danger"></span>
</div>
</div>
<divclass="form-group">
<labelasp-for="DateOfBirth"class="col-md-2 control-label"></label>
<divclass="col-md-10">
<inputasp-for="DateOfBirth"class="form-control"/>
<spanasp-validation-for="DateOfBirth"class="text-danger"></span>
</div>
</div>
<divclass="form-group">
<labelasp-for="DateOfRegistration"class="col-md-2 control-label"></label>
<divclass="col-md-10">
<inputasp-for="DateOfRegistration"class="form-control"/>
<spanasp-validation-for="DateOfRegistration"class="text-danger"></span>
</div>
</div>
<divclass="form-group">
<labelasp-for="PhoneNumber"class="col-md-2 control-label"></label>
<divclass="col-md-10">
<inputasp-for="PhoneNumber"class="form-control"/>
<spanasp-validation-for="PhoneNumber"class="text-danger"></span>
</div>
</div>
<divclass="form-group">
<labelasp-for="Email"class="col-md-2 control-label"></label>
<divclass="col-md-10">
<inputasp-for="Email"class="form-control"/>
<spanasp-validation-for="Email"class="text-danger"></span>
</div>
</div>
<divclass="form-group">
<labelasp-for="Address1"class="col-md-2 control-label"></label>
<divclass="col-md-10">
<inputasp-for="Address1"class="form-control"/>
<spanasp-validation-for="Address1"class="text-danger"></span>
</div>
</div>
<divclass="form-group">
<labelasp-for="Address2"class="col-md-2 control-label"></label>
<divclass="col-md-10">
<inputasp-for="Address2"class="form-control"/>
<spanasp-validation-for="Address2"class="text-danger"></span>
</div>
</div>
<divclass="form-group">
<labelasp-for="City"class="col-md-2 control-label"></label>
<divclass="col-md-10">
<inputasp-for="City"class="form-control"/>
<spanasp-validation-for="City"class="text-danger"></span>
</div>
</div>
<divclass="form-group">
<labelasp-for="State"class="col-md-2 control-label"></label>
<divclass="col-md-10">
<inputasp-for="State"class="form-control"/>
<spanasp-validation-for="State"class="text-danger"></span>
</div>
</div>
<divclass="form-group">
<labelasp-for="Zip"class="col-md-2 control-label"></label>
<divclass="col-md-10">
<inputasp-for="Zip"class="form-control"/>
<spanasp-validation-for="Zip"class="text-danger"></span>
</div>
</div>
<divclass="form-group">
<divclass="col-md-offset-2 col-md-10">
<inputtype="submit"value="Create"class="btn btn-default"/>
</div>
</div>
</div>
</form> <div>
<aasp-action="Index">Back to List</a>
</div> @section Scripts {
@{await Html.RenderPartialAsync("_ValidationScriptsPartial");}
}
编辑 隐藏,收缩,复制Code
@model StudentApplication.Web.Helper.StudentDTO @{
ViewData["Title"] = "Edit";
Layout = "~/Views/Shared/_Layout.cshtml";
} <h2>Edit</h2> <formasp-action="Edit">
<divclass="form-horizontal">
<h4>Student</h4>
<hr/>
<divasp-validation-summary="ModelOnly"class="text-danger"></div>
<inputtype="hidden"asp-for="StudentId"/>
<divclass="form-group">
<labelasp-for="FirstName"class="col-md-2 control-label"></label>
<divclass="col-md-10">
<inputasp-for="FirstName"class="form-control"/>
<spanasp-validation-for="FirstName"class="text-danger"></span>
</div>
</div>
<divclass="form-group">
<labelasp-for="LastName"class="col-md-2 control-label"></label>
<divclass="col-md-10">
<inputasp-for="LastName"class="form-control"/>
<spanasp-validation-for="LastName"class="text-danger"></span>
</div>
</div>
<divclass="form-group">
<labelasp-for="Gender"class="col-md-2 control-label"></label>
<divclass="col-md-10">
<inputasp-for="Gender"class="form-control"/>
<spanasp-validation-for="Gender"class="text-danger"></span>
</div>
</div>
<divclass="form-group">
<labelasp-for="DateOfBirth"class="col-md-2 control-label"></label>
<divclass="col-md-10">
<inputasp-for="DateOfBirth"class="form-control"/>
<spanasp-validation-for="DateOfBirth"class="text-danger"></span>
</div>
</div>
<divclass="form-group">
<labelasp-for="DateOfRegistration"class="col-md-2 control-label"></label>
<divclass="col-md-10">
<inputasp-for="DateOfRegistration"class="form-control"/>
<spanasp-validation-for="DateOfRegistration"class="text-danger"></span>
</div>
</div>
<divclass="form-group">
<labelasp-for="PhoneNumber"class="col-md-2 control-label"></label>
<divclass="col-md-10">
<inputasp-for="PhoneNumber"class="form-control"/>
<spanasp-validation-for="PhoneNumber"class="text-danger"></span>
</div>
</div>
<divclass="form-group">
<labelasp-for="Email"class="col-md-2 control-label"></label>
<divclass="col-md-10">
<inputasp-for="Email"class="form-control"/>
<spanasp-validation-for="Email"class="text-danger"></span>
</div>
</div>
<divclass="form-group">
<labelasp-for="Address1"class="col-md-2 control-label"></label>
<divclass="col-md-10">
<inputasp-for="Address1"class="form-control"/>
<spanasp-validation-for="Address1"class="text-danger"></span>
</div>
</div>
<divclass="form-group">
<labelasp-for="Address2"class="col-md-2 control-label"></label>
<divclass="col-md-10">
<inputasp-for="Address2"class="form-control"/>
<spanasp-validation-for="Address2"class="text-danger"></span>
</div>
</div>
<divclass="form-group">
<labelasp-for="City"class="col-md-2 control-label"></label>
<divclass="col-md-10">
<inputasp-for="City"class="form-control"/>
<spanasp-validation-for="City"class="text-danger"></span>
</div>
</div>
<divclass="form-group">
<labelasp-for="State"class="col-md-2 control-label"></label>
<divclass="col-md-10">
<inputasp-for="State"class="form-control"/>
<spanasp-validation-for="State"class="text-danger"></span>
</div>
</div>
<divclass="form-group">
<labelasp-for="Zip"class="col-md-2 control-label"></label>
<divclass="col-md-10">
<inputasp-for="Zip"class="form-control"/>
<spanasp-validation-for="Zip"class="text-danger"></span>
</div>
</div>
<divclass="form-group">
<divclass="col-md-offset-2 col-md-10">
<inputtype="submit"value="Save"class="btn btn-default"/>
</div>
</div>
</div>
</form> <div>
<aasp-action="Index">Back to List</a>
</div> @section Scripts {
@{await Html.RenderPartialAsync("_ValidationScriptsPartial");}
}
删除 隐藏,收缩,复制Code
@model StudentApplication.Web.Helper.StudentDTO @{
ViewData["Title"] = "Delete";
Layout = "~/Views/Shared/_Layout.cshtml";
} <h2>Delete</h2> <h3>Are you sure you want to delete this?</h3>
<div>
<h4>Student</h4>
<hr/>
<dlclass="dl-horizontal">
<inputtype="hidden"asp-for="StudentId"/>
<dt>
@Html.DisplayNameFor(model => model.FirstName)
</dt>
<dd>
@Html.DisplayFor(model => model.FirstName)
</dd>
<dt>
@Html.DisplayNameFor(model => model.LastName)
</dt>
<dd>
@Html.DisplayFor(model => model.LastName)
</dd>
<dt>
@Html.DisplayNameFor(model => model.Gender)
</dt>
<dd>
@Html.DisplayFor(model => model.Gender)
</dd>
<dt>
@Html.DisplayNameFor(model => model.DateOfBirth)
</dt>
<dd>
@Html.DisplayFor(model => model.DateOfBirth)
</dd>
<dt>
@Html.DisplayNameFor(model => model.DateOfRegistration)
</dt>
<dd>
@Html.DisplayFor(model => model.DateOfRegistration)
</dd>
<dt>
@Html.DisplayNameFor(model => model.PhoneNumber)
</dt>
<dd>
@Html.DisplayFor(model => model.PhoneNumber)
</dd>
<dt>
@Html.DisplayNameFor(model => model.Email)
</dt>
<dd>
@Html.DisplayFor(model => model.Email)
</dd>
<dt>
@Html.DisplayNameFor(model => model.Address1)
</dt>
<dd>
@Html.DisplayFor(model => model.Address1)
</dd>
<dt>
@Html.DisplayNameFor(model => model.Address2)
</dt>
<dd>
@Html.DisplayFor(model => model.Address2)
</dd>
<dt>
@Html.DisplayNameFor(model => model.City)
</dt>
<dd>
@Html.DisplayFor(model => model.City)
</dd>
<dt>
@Html.DisplayNameFor(model => model.State)
</dt>
<dd>
@Html.DisplayFor(model => model.State)
</dd>
<dt>
@Html.DisplayNameFor(model => model.Zip)
</dt>
<dd>
@Html.DisplayFor(model => model.Zip)
</dd>
</dl> <formasp-action="Delete">
<divclass="form-actions no-color">
<inputtype="submit"value="Delete"class="btn btn-default"/> |
<aasp-action="Index">Back to List</a>
</div>
</form>
</div>
上面所有的标记都是强类型的视图,因为它们都引用了相同的对象模型,即StudentApplication.Web.Helper.StudentDTO。他们还使用标记助手来定义HTML和数据绑定。 测试应用程序 由于这个演示由两个项目组成,并且我们的MVC应用程序依赖于我们的Web API应用程序,所以我们需要使Web API在测试页面时可访问。通常,我们将在IIS web服务器中托管或部署这两个项目,以便能够在项目之间进行连接。幸运的是,Visual Studio 2017的一个很酷的特性是支持多个启动项目。这意味着我们可以在Visual Studio中运行我们的Web API和MVC应用程序,并且能够立即测试它们,而无需将它们部署到IIS中。你所需要做的就是: 右键单击解决方案,选择设置启动项目,选择多个启动项目单选按钮,选择“开始”作为Web API和MVC项目的操作,单击应用,然后确定 现在使用CTRL + 5构建并运行应用程序。在浏览器中,导航到/students/Create。它应该会打开下面的页面。 图13:Create视图 提供字段并点击“创建”按钮,它将导航到添加了新学生记录的索引页面,如下图所示: 图14:索引视图 当然,编辑和删除也可以工作:)请随意下载源代码并享受乐趣! 总结 在本系列中,我们学习了如何构建ASP。NET Core MVC应用程序使用实体框架核心与新的数据库。我们还学习了如何创建一个Web API控制器并让控制器与an通信接口。我们还学习了如何使用存储库模式实现简单的数据访问来处理CRUD操作。最后,我们学习了如何创建一个基本的ASP。使用Web API端点在UI中执行CRUD的asp.net Core MVC应用程序。 参考文献 https://docs.microsoft.com/en-us/aspnet/core/ https://docs.microsoft.com/en-us/ef/core/configuring-dbcontext https://docs.microsoft.com/en-us/ef/core/get-started/aspnetcore/newdb https://asp.net/web -api , 本文转载于:http://www.diyabc.com/frontweb/news19693.html
从实体框架核心开始:构建一个ASP。NET Core应用程序与Web API和代码优先开发的更多相关文章
- NHibernate构建一个ASP.NET MVC应用程序
NHibernate构建一个ASP.NET MVC应用程序 什么是Nhibernate? NHibernate是一个面向.NET环境的对象/关系数据库映射工具.对象/关系数据库映射(object/re ...
- 使用Visual Studio Code创建第一个ASP.NET Core应用程序
全文翻译自:Your First ASP.NET Core Application on a Mac Using Visual Studio Code 这篇文章将向你展示如何在Mac上写出你的第一个A ...
- 如何一秒钟从头构建一个 ASP.NET Core 中间件
前言 其实地上本没有路,走的人多了,也便成了路. -- 鲁迅 就像上面鲁迅说的那样,其实在我们开发中间件的过程中,微软并没有制定一些策略或者文档来约束你如何编写一个中间件程序, 但是其中却存在者一些最 ...
- 【asp.net core】实现动态 Web API
序言: 远程工作已经一个月了,最近也算是比较闲,每天早上起床打个卡,快速弄完当天要做的工作之后就快乐摸鱼去了.之前在用 ABP 框架(旧版)的时候就觉得应用服务层写起来真的爽,为什么实现了个 IApp ...
- 【ASP.NET Core】从向 Web API 提交纯文本内容谈起
前些时日,老周在升级“华南闲肾回收登记平台”时,为了扩展业务,尤其是允许其他开发人员在其他平台向本系统提交有关肾的介绍资料,于是就为该系统增加了几个 Web API. 其中,有关肾的介绍采用纯文本方式 ...
- 4、ASP.NET MVC入门到精通——NHibernate构建一个ASP.NET MVC应用程序
下周就去办理离职手续了,之前没有使用过NHibernate,只知道NHibernate是一种ORM框架,但是听说新公司是使用NHibernate在做项目,所以,我就网上找资料学习一下NHibernat ...
- Kubernetes初探[1]:部署你的第一个ASP.NET Core应用到k8s集群
Kubernetes简介 Kubernetes是Google基于Borg开源的容器编排调度引擎,作为CNCF(Cloud Native Computing Foundation)最重要的组件之一,它的 ...
- 使用Azure Application Insignhts监控ASP.NET Core应用程序
Application Insignhts是微软开发的一套监控程序.他可以对线上的应用程序进行全方位的监控,比如监控每秒的请求数,失败的请求,追踪异常,对每个请求进行监控,从http的耗时,到SQL查 ...
- C# 6 与 .NET Core 1.0 高级编程 - 38 章 实体框架核心(上)
译文,个人原创,转载请注明出处(C# 6 与 .NET Core 1.0 高级编程 - 38 章 实体框架核心(上)),不对的地方欢迎指出与交流. 章节出自<Professional C# 6 ...
随机推荐
- unity 模板测试 详解
https://blog.csdn.net/u011047171/article/details/46928463#t4
- 12 props 传的是数组处理
<template> <div>InfoDetailed</div> </template> <script> export default ...
- JS中的数组,添加删除元素,判断是否存在一个值的方法总结
一.添加元素: 1:在最后添加,返回数组长度:arr.push(...); 2:在最前面添加,返回数组长度:arr.unshift(...); 3:在指定位置添加,没有返回值:arr[i] = xxx ...
- do...while 循环
语法 C 语言中 do...while 循环的语法: do { statement(s); }while( condition ); 请注意,条件表达式出现在循环的尾部,所以循环中的 statemen ...
- [剑指Offer]33-根据后序序列判断是否能组成BST
题目 如题. 题解 从序列第一个大于根节点的值往后都是右子树,判断右子树是否都大于根节点. 然后递归判断左右子树是否是BST 代码 class TreeNode { int val = 0; Tree ...
- python字符串和列表小案例
python 目录 python 一.字符串 1.给定一个字符串,利用切片将字符串反转 2.给定一个字符串,将空格替换为逗号 3.给定一个字符串,大写改为小写 4.str = '' ,li = ['l ...
- 解决spark streaming集成kafka时只能读topic的其中一个分区数据的问题
1. 问题描述 我创建了一个名称为myTest的topic,该topic有三个分区,在我的应用中spark streaming以direct方式连接kakfa,但是发现只能消费一个分区的数据,多次更换 ...
- Github上如何添加 LICENSE 文件?
什么是开源软件? 开源软件是所有人都可以修改和补充的软件,因为开源软件的 license 协议允许这样做. Git版本控制系统就是开源的软件. 实际上开源软件的一个重要组成部分就是添加 license ...
- JWT理论理解
什么是JWT Json web token (JWT), 是为了在网络应用环境间传递声明而执行的一种基于JSON的开放标准(RFC 7519).该token被设计为紧凑且安全的,特别适用于分布式站点的 ...
- 传值&传值引用
转自http://www.cnblogs.com/androidsuperman/p/9012320.html 首先对传值和传引用要有个基本的概念 传值:传递的是值的副本.方法中对副本的修改,不会影响 ...