使用Razor做Web,我web几乎忘差不多了,本demo练习 EF Core
新建core mvc
====================www.ayjs.net 杨洋 wpfui.com ayui ay aaronyang=======请不要转载谢谢了。=========
====================www.ayjs.net 杨洋 wpfui.com ayui ay aaronyang=======请不要转载谢谢了。=========
F5运行项目
关于MVC,这里AY假设你 曾经 已经掌握 ASP.NET MVC了,所以不介绍了,不会的自己百度。
打开模板页 Views/Shared/_Layout.cshtml
<!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <title>@ViewData["Title"] - AY信息管理系统</title> <environment include="Development"> <link rel="stylesheet" href="~/lib/bootstrap/dist/css/bootstrap.css" /> <link rel="stylesheet" href="~/css/site.css" /> </environment> <environment exclude="Development"> <link rel="stylesheet" href="https://ajax.aspnetcdn.com/ajax/bootstrap/3.3.7/css/bootstrap.min.css" asp-fallback-href="~/lib/bootstrap/dist/css/bootstrap.min.css" asp-fallback-test-class="sr-only" asp-fallback-test-property="position" asp-fallback-test-value="absolute" /> <link rel="stylesheet" href="~/css/site.min.css" asp-append-version="true" /> </environment> </head> <body> <nav class="navbar navbar-inverse navbar-fixed-top"> <div class="container"> <div class="navbar-header"> <button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".navbar-collapse"> <span class="sr-only">Toggle navigation</span> <span class="icon-bar"></span> <span class="icon-bar"></span> <span class="icon-bar"></span> </button> <a asp-area="" asp-controller="Home" asp-action="Index" class="navbar-brand">AY信息管理系统</a> </div> <div class="navbar-collapse collapse"> <ul class="nav navbar-nav"> <li><a asp-area="" asp-controller="Home" asp-action="Index">首页</a></li> <li><a asp-area="" asp-controller="Home" asp-action="About">关于</a></li> <li><a asp-area="" asp-controller="Students" asp-action="Index">学生</a></li> <li><a asp-area="" asp-controller="Courses" asp-action="Index">课程</a></li> <li><a asp-area="" asp-controller="Instructors" asp-action="Index">老师</a></li> <li><a asp-area="" asp-controller="Departments" asp-action="Index">部门</a></li> </ul> </div> </div> </nav> <div class="container body-content"> @RenderBody() <hr /> <footer> <p>© 2017 - AY Ltd.</p> </footer> </div> <environment include="Development"> <script src="~/lib/jquery/dist/jquery.js"></script> <script src="~/lib/bootstrap/dist/js/bootstrap.js"></script> <script src="~/js/site.js" asp-append-version="true"></script> </environment> <environment exclude="Development"> <script src="https://ajax.aspnetcdn.com/ajax/jquery/jquery-2.2.0.min.js" asp-fallback-src="~/lib/jquery/dist/jquery.min.js" asp-fallback-test="window.jQuery" crossorigin="anonymous" integrity="sha384-K+ctZQ+LL8q6tP7I94W+qzQsfRV2a+AfHIi9k8z8l9ggpc8X+Ytst4yBo/hH+8Fk"> </script> <script src="https://ajax.aspnetcdn.com/ajax/bootstrap/3.3.7/bootstrap.min.js" asp-fallback-src="~/lib/bootstrap/dist/js/bootstrap.min.js" asp-fallback-test="window.jQuery && window.jQuery.fn && window.jQuery.fn.modal" crossorigin="anonymous" integrity="sha384-Tc5IQib027qvyjSMfHjOMaLkfuWVxZxUPnCJA7l2mCWNIpG9mGCD8wGNIcPD7Txa"> </script> <script src="~/js/site.min.js" asp-append-version="true"></script> </environment> @RenderSection("Scripts", required: false) </body> </html>
AY:我看了下这个代码 <li><a asp-area="" asp-controller="Home" asp-action="Index">首页</a></li> 大致懂了,core的前缀特性 是 asp-
然后controller就是控制器名字,action是前面的控制器下的 操作,area就是 控制器的上层,域那块知识。a是超链接,单击就是路由到 指定的action上去操作。
修改首页 Views/Home/Index.cshtml
@{ ViewData["Title"] = "Home Page"; } <div class="jumbotron"> <h1>AY信息管理系统</h1> </div> <div class="row"> <div class="col-md-4"> <h2>Welcome to AY core MVC教程</h2> <p> AY信息管理是个简单的demo,使用ASP.NET Core MVC web application技术,并且也使用了 Entity Framework Core做数据交互 </p> </div> <div class="col-md-4"> <h2>Build it from scratch</h2> <p>You can build the application by following the steps in a series of tutorials.</p> <p><a class="btn btn-default" href="https://docs.asp.net/en/latest/data/ef-mvc/intro.html">See the tutorial »</a></p> </div> <div class="col-md-4"> <h2>Download it</h2> <p>You can download the completed project from GitHub.</p> <p><a class="btn btn-default" href="https://github.com/aspnet/Docs/tree/master/aspnetcore/data/ef-mvc/intro/samples/cu-final">See project source code »</a></p> </div> </div>
使用EF core,通过Nuget可以获得,但是新建的core项目 自带了ef相关的了
Microsoft.EntityFrameworkCore.SqlServer
ID属性将成为对应于此类数据库表的主键列
using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations.Schema; using System.Linq; using System.Threading.Tasks; namespace AyWebCoreMvc.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; } } 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; } } 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; } } }
表相关属性定义完了,建立库,加上这些表,新建data文件夹
你可以指定ICollection<T>或类型,如List<T>或HashSet<T>。 如果指定ICollection<T>,EF 创建HashSet<T>默认情况下的集合
StudentID和CourseID打开Startup.cs属性是一个外键
using AyWebCoreMvc.Models; using Microsoft.EntityFrameworkCore; namespace AyWebCoreMvc.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; } } }
默认根据属性名来建立表名,如果你想换名字,可以这样做
protected override void OnModelCreating(ModelBuilder modelBuilder) { modelBuilder.Entity<Course>().ToTable("Course"); modelBuilder.Entity<Enrollment>().ToTable("Enrollment"); modelBuilder.Entity<Student>().ToTable("Student"); }
给数据库通电,打开Startup.cs,注入 数据库的 对象
using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using AyWebCoreMvc.Data; using Microsoft.EntityFrameworkCore; namespace AyWebCoreMvc { public class Startup { public Startup(IConfiguration configuration) { Configuration = configuration; } public IConfiguration Configuration { get; } // This method gets called by the runtime. Use this method to add services to the container. public void ConfigureServices(IServiceCollection services) { services.AddDbContext<SchoolContext>(options => options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection"))); services.AddMvc(); } // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. public void Configure(IApplicationBuilder app, IHostingEnvironment env) { if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); app.UseBrowserLink(); } else { app.UseExceptionHandler("/Home/Error"); } app.UseStaticFiles(); app.UseMvc(routes => { routes.MapRoute( name: "default", template: "{controller=Home}/{action=Index}/{id?}"); }); } } }
再打开配置文件appsettings.json
{ "ConnectionStrings": { "DefaultConnection": "Server=(localdb)\\mssqllocaldb;Database=School;Trusted_Connection=True;MultipleActiveResultSets=true" }, "Logging": { "IncludeScopes": false, "LogLevel": { "Default": "Warning" } } }
连接字符串指定 SQL Server LocalDB 数据库。 LocalDB 是 SQL Server Express 数据库引擎的轻量级版本,用于应用程序开发,不生产环境中使用。 LocalDB 按需启动并在用户模式下运行,因此没有复杂的配置。 默认情况下,创建 LocalDB .mdf数据库中的文件C:/Users/<user>目录。
然后,我们创建假数据,并且在项目运行时候,执行这个数据。
添加数据初始化DbInitializer类
using AyWebCoreMvc.Models; using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; namespace AyWebCoreMvc.Data { public static class DbInitializer { public static void Initialize(SchoolContext context) { context.Database.EnsureCreated(); if (context.Students.Any()) { return; // 数据库已经创建了。 } var students = new Student[] { new Student{FirstMidName="张",LastName="三",EnrollmentDate=DateTime.Parse("2011-09-01")}, new Student{FirstMidName="李",LastName="四",EnrollmentDate=DateTime.Parse("2012-09-01")}, new Student{FirstMidName="王",LastName="五",EnrollmentDate=DateTime.Parse("2013-09-01")}, new Student{FirstMidName="冯",LastName="六",EnrollmentDate=DateTime.Parse("2012-09-01")}, new Student{FirstMidName="田",LastName="七",EnrollmentDate=DateTime.Parse("2015-09-01")}, new Student{FirstMidName="王",LastName="八",EnrollmentDate=DateTime.Parse("2017-09-01")}, new Student{FirstMidName="阿",LastName="九",EnrollmentDate=DateTime.Parse("2017-09-01")}, new Student{FirstMidName="句",LastName="十",EnrollmentDate=DateTime.Parse("2017-09-01")} }; foreach (Student s in students) { context.Students.Add(s); } context.SaveChanges(); var courses = new Course[] { new Course{CourseID=1050,Title="化学",Credits=3}, new Course{CourseID=4022,Title="微观经济学",Credits=3}, new Course{CourseID=4041,Title="宏观经济学",Credits=3}, new Course{CourseID=1045,Title="微积分",Credits=4}, new Course{CourseID=3141,Title="三角学",Credits=4}, new Course{CourseID=2021,Title="作曲",Credits=3}, new Course{CourseID=2042,Title="文学",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(); } } }
在program.cs初始化
using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Threading.Tasks; using Microsoft.AspNetCore; using Microsoft.AspNetCore.Hosting; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Logging; using Microsoft.Extensions.DependencyInjection; using AyWebCoreMvc.Data; namespace AyWebCoreMvc { public class Program { public static void Main(string[] args) { //BuildWebHost(args).Run(); var host = BuildWebHost(args); using (var scope = host.Services.CreateScope()) { var services = scope.ServiceProvider; try { var context = services.GetRequiredService<SchoolContext>(); DbInitializer.Initialize(context); } catch (Exception ex) { var logger = services.GetRequiredService<ILogger<Program>>(); logger.LogError(ex, "An error occurred while seeding the database."); } } host.Run(); } public static IWebHost BuildWebHost(string[] args) => WebHost.CreateDefaultBuilder(args) .UseStartup<Startup>() .Build(); } }
F5运行项目后,你的C:/user/<user>下会有个数据库了。
CRUD 操作方法和视图的自动创建被称为基架,右键控制器,新建 基架
点击添加
完成后,StudentsController.cs
using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.Rendering; using Microsoft.EntityFrameworkCore; using AyWebCoreMvc.Data; using AyWebCoreMvc.Models; namespace AyWebCoreMvc.Controllers { public class StudentsController : Controller { private readonly SchoolContext _context; public StudentsController(SchoolContext context) { _context = context; } // GET: Students public async Task<IActionResult> Index() { return View(await _context.Students.ToListAsync()); } // GET: Students/Details/5 public async Task<IActionResult> Details(int? id) { if (id == null) { return NotFound(); } var student = await _context.Students .SingleOrDefaultAsync(m => m.ID == id); if (student == null) { return NotFound(); } return View(student); } // GET: Students/Create public IActionResult Create() { return View(); } // POST: Students/Create // To protect from overposting attacks, please enable the specific properties you want to bind to, for // more details see http://go.microsoft.com/fwlink/?LinkId=317598. [HttpPost] [ValidateAntiForgeryToken] public async Task<IActionResult> Create([Bind("ID,LastName,FirstMidName,EnrollmentDate")] Student student) { if (ModelState.IsValid) { _context.Add(student); await _context.SaveChangesAsync(); return RedirectToAction(nameof(Index)); } return View(student); } // GET: Students/Edit/5 public async Task<IActionResult> Edit(int? id) { if (id == null) { return NotFound(); } var student = await _context.Students.SingleOrDefaultAsync(m => m.ID == id); if (student == null) { return NotFound(); } return View(student); } // POST: Students/Edit/5 // To protect from overposting attacks, please enable the specific properties you want to bind to, for // more details see http://go.microsoft.com/fwlink/?LinkId=317598. [HttpPost] [ValidateAntiForgeryToken] public async Task<IActionResult> Edit(int id, [Bind("ID,LastName,FirstMidName,EnrollmentDate")] Student student) { if (id != student.ID) { return NotFound(); } if (ModelState.IsValid) { try { _context.Update(student); await _context.SaveChangesAsync(); } catch (DbUpdateConcurrencyException) { if (!StudentExists(student.ID)) { return NotFound(); } else { throw; } } return RedirectToAction(nameof(Index)); } return View(student); } // GET: Students/Delete/5 public async Task<IActionResult> Delete(int? id) { if (id == null) { return NotFound(); } var student = await _context.Students .SingleOrDefaultAsync(m => m.ID == id); if (student == null) { return NotFound(); } return View(student); } // POST: Students/Delete/5 [HttpPost, ActionName("Delete")] [ValidateAntiForgeryToken] public async Task<IActionResult> DeleteConfirmed(int id) { var student = await _context.Students.SingleOrDefaultAsync(m => m.ID == id); _context.Students.Remove(student); await _context.SaveChangesAsync(); return RedirectToAction(nameof(Index)); } private bool StudentExists(int id) { return _context.Students.Any(e => e.ID == id); } } }
代码都生成好了,这里你可以学到很多知识,你将在本教程后面了解此代码中的异步编程元素
这块代码,是已经 依赖注入了,所以context是有数据库对象的,这里没有new,这就ioc的构造函数方式 注入。
下面有个代码,异步方式 async 标记这个方法是异步的,await调用一个异步方法。
接着看看下面代码,发现写法 比以前高大上多了,但是还是C#那些东西,我能看懂,你呢?
打开 Views/Students文件夹 ,他下面默认生成了 页面,我们通过这个demo,可以学到很多写法。运行项目,单击学生
讲解:DbInitializer.Initialize方法调用EnsureCreated,ef没发现数据库就会去创建。
用EF core 写 实体类时候,有约定
名称DbSet属性用作表名。 未被引用的实体DbSet属性,实体类名称用作表名称。
实体属性名称用于列名称。
ID 或 classnameID 命名的实体属性被识别为主键属性。
如果它名为属性将被解释为外键属性* * (例如,StudentID为Student以来的导航属性Student实体的主键是ID). 此外可以只需命名外键属性* * (例如,EnrollmentID由于Enrollment实体的主键是EnrollmentID
EF 上下文不是线程安全: 请勿尝试执行并行的多个操作。 当调用的任何异步 EF 方法时,始终使用await关键字
这个页面增删改查方法都有,下面顺便讲解下 controller下的某些代码:
我们点击查看详情 http://localhost:6553/Students/Details/2
我们把李四修的科目信息拿出来。
var student = await _context.Students .Include(s => s.Enrollments) .ThenInclude(e => e.Course) .AsNoTracking() .SingleOrDefaultAsync(m => m.ID == id);
Include和ThenInclude方法导致要加载的上下文Student.Enrollments导航属性,
并在每个注册Enrollment.Course导航属性。
AsNoTracking方法可以提高其中返回的实体将不会更新在当前上下文的生存期中的方案中的性能
打开detail页面
<dt>
@Html.DisplayNameFor(model => model.LastName)
</dt>
<dd>
@Html.DisplayFor(model => model.LastName)
</dd>
这里使用razor提供的DisplayName显示信息。
<dt> @Html.DisplayNameFor(model => model.Enrollments)</dt><dd> <table class="table"> <tr> <th>Course Title</th> <th>Grade</th> </tr> @foreach (var item in Model.Enrollments) { <tr> <td> @Html.DisplayFor(modelItem => item.Course.Title) </td> <td> @Html.DisplayFor(modelItem => item.Grade) </td> </tr> } </table></dd>
运行项目 http://localhost:6553/Students/Details/2
修改新增代码,我们在Bind 中去掉 ID那个 字段,然后加上try catch异常捕获
[HttpPost] [ValidateAntiForgeryToken] public async Task<IActionResult> Create( [Bind("EnrollmentDate,FirstMidName,LastName")] Student student) { try { if (ModelState.IsValid) { _context.Add(student); await _context.SaveChangesAsync(); return RedirectToAction(nameof(Index)); } } catch (DbUpdateException /* ex */) { //Log the error (uncomment ex variable name and write a log. ModelState.AddModelError("", "Unable to save changes. " + "Try again, and if the problem persists " + "see your system administrator."); } return View(student); }
ValidateAntiForgeryToken属性有助于防止跨站点请求伪造 (CSRF) 攻击
关于over-posting
over-posting简单的说就是指用户通过猜测等手段得知了后端数据Model的属性名称,在数据更新或添加的时候提交了本不应该允许用户更改的数据库字段,并且在服务器端因为没有进行防御而将恶意提交的数据写入了数据库。
所以你看到了在Create参数有个Bind 参数,里面指定了本次 插入的字段的值,其他的值都是默认,或者原来的。这样 恶意用户无法修改其他 其他的值了
关于页面上的 验证,后台使用 来判断 前台表单数据是否验证通过了,通过了,我们就可以继续 执行其他操作,不然无效数据是不合法的。
if (ModelState.IsValid)
关于更新操作,我们修改代码
修改
这些更改实现安全的最佳做法,以防止 overposting
[HttpPost, ActionName("Edit")] [ValidateAntiForgeryToken] public async Task<IActionResult> EditPost(int? id) { if (id == null) { return NotFound(); } var studentToUpdate = await _context.Students.SingleOrDefaultAsync(s => s.ID == id); if (await TryUpdateModelAsync<Student>( studentToUpdate, "", s => s.FirstMidName, s => s.LastName, s => s.EnrollmentDate)) { try { await _context.SaveChangesAsync(); return RedirectToAction(nameof(Index)); } catch (DbUpdateException /* ex */) { //Log the error (uncomment ex variable name and write a log.) ModelState.AddModelError("", "Unable to save changes. " + "Try again, and if the problem persists, " + "see your system administrator."); } } return View(studentToUpdate); }
接下来修改带Bind的edit方法
public async Task<IActionResult> Edit(int id, [Bind("ID,EnrollmentDate,FirstMidName,LastName")] Student student){ if (id != student.ID) { return NotFound(); } if (ModelState.IsValid) { try { _context.Update(student); await _context.SaveChangesAsync(); return RedirectToAction(nameof(Index)); } catch (DbUpdateException /* ex */) { //Log the error (uncomment ex variable name and write a log.) ModelState.AddModelError("", "Unable to save changes. " + "Try again, and if the problem persists, " + "see your system administrator."); } } return View(student); }
实体可能处于以下状态之一:
Added。 实体在数据库中尚不存在。 SaveChanges方法发出 INSERT 语句。
Unchanged。 无需使用通过此实体完成SaveChanges方法。 当从数据库读取实体时,该实体开始时具有此状态。
Modified。 某些或所有实体的属性值已都更改。 SaveChanges方法发出 UPDATE 语句。
Deleted。 实体已标记为删除。 SaveChanges方法发出的 DELETE 语句。
Detached。 实体不跟踪的数据库上下文。
public async Task<IActionResult> Delete(int? id, bool? saveChangesError = false) { if (id == null) { return NotFound(); } var student = await _context.Students .AsNoTracking() .SingleOrDefaultAsync(m => m.ID == id); if (student == null) { return NotFound(); } if (saveChangesError.GetValueOrDefault()) { ViewData["ErrorMessage"] = "Delete failed. Try again, and if the problem persists " + "see your system administrator."; } return View(student); }
此代码检索所选的实体,然后调用Remove方法将实体的状态设置为Deleted。 当SaveChanges称为 SQL DELETE 命令生成的。
前台错误消息显示:
<h2>Delete</h2> <p class="text-danger">@ViewData["ErrorMessage"]</p> <h3>Are you sure you want to delete this?</h3>
下面这个代码不要放入项目,只是参考学习,手动设置状态的。
[HttpPost] [ValidateAntiForgeryToken] public async Task<IActionResult> DeleteConfirmed(int id) { try { Student studentToDelete = new Student() { ID = id }; _context.Entry(studentToDelete).State = EntityState.Deleted; await _context.SaveChangesAsync(); return RedirectToAction(nameof(Index)); } catch (DbUpdateException /* ex */) { //Log the error (uncomment ex variable name and write a log.) return RedirectToAction(nameof(Delete), new { id = id, saveChangesError = true }); } }
====================www.ayjs.net 杨洋 wpfui.com ayui ay aaronyang=======请不要转载谢谢了。=========
到此先学习到这里了。
推荐您阅读更多有关于“”的文章
注:本文内容来自互联网,旨在为开发者提供分享、交流的平台。如有涉及文章版权等事宜,请你联系站长进行处理。