首页 > 代码库 > ASP.NET Core 实战(一)CURD
ASP.NET Core 实战(一)CURD
我一哥们从公司跳槽,推荐我去华为面试。大家也知道,华为不需要.net程序员,去了还要转java,你要是放到3年前的那次面试,你放我英语四级一马,我还真就去了,现在虽然你不刻意要求英语,可以放宽限制,我也是有心无力,真的是没精力去转什么java。和有些公司就是没有缘分,没办法的事。
今天我们来看一下ASP.NET Core的增删改查Demo。首先我们来看一下项目结构。
大家发现了吧,图标变了,在创建ASP.NET Core项目前,确保你已经安装了VS2015。
OK,接下来我们大概看一下项目文件,先看Project.json
我们看到了,是一些引用的dll还有一些发布运行时的配置,这个有点类似于node.js中的package.json包管理文件。
我们再来看appsettings.json
就是一些键值对的配置,ok,当然我们也可以在web.config中配置。再看一下bundleconfig.json。
之前我们用的是bundle.cs,而现在也换成了json文件配置。配置了输入原js,css以及输出js,css的压缩文件。OK,我们看这些文件也只是为了看区别。接下来我们把框架搭起来,很熟悉,Repository被Service调用,Service被controller调用。数据访问层是EF,采用database first模式。
首先我们要创建好数据库,然后我们按照微软官方网站的示例,根据database生成model。
https://docs.efproject.net/en/latest/platforms/aspnetcore/existing-db.html
ok,model生成好之后,我们看一下。
生成的整整齐齐,和之前的T4模板一样。注意在AttendanceManageContext文件中有数据库连接字符串,以及表结构定义,包括表关联关系。AttendanceManageContext继承DbContext,这一点没有变。
public partial class AttendanceManageContext : DbContext { protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder { #warning To protect potentially sensitive information in your connection optionsBuilder.UseSqlServer(@"Server=IGFMVQVM354PFSB\BRUCE;Database=Atte..." } protected override void OnModelCreating(ModelBuilder modelBuilder) { modelBuilder.Entity<Auth>(entity => { entity.HasKey(e => e.TransactionNumber) .HasName("PK__Auth__E733A2BE21B6055D"); entity.Property(e => e.AuthName) .IsRequired() .HasMaxLength(50); entity.Property(e => e.InDate).HasColumnType("datetime"); }); modelBuilder.Entity<Department>(entity => { entity.HasKey(e => e.TransactionNumber) .HasName("PK__Departme__E733A2BE25869641"); entity.Property(e => e.DepartmentName) .IsRequired() .HasMaxLength(500); entity.Property(e => e.InDate).HasColumnType("datetime"); }); } .......... }
OK,我们再看一下Repository。
public interface IRepository<TEntity> where TEntity : class, new() { void Update(TEntity entity); void Remove(TEntity entity); void Create(TEntity entity); }
定义一个接口,然后用BaseRepository继承并实现
public class BaseRepository<TEntity> : IRepository<TEntity> where TEntity : class, new() { protected DbContext _context; DbSet<TEntity> _dbSet; public BaseRepository(DbContext context) { this._context = context; this._dbSet = context.Set<TEntity>(); } public void Update(TEntity tEntity) { this._dbSet.Attach(tEntity); this._context.Entry(tEntity).State = EntityState.Modified; } public void Remove(TEntity tEntity) { if (_context.Entry(tEntity).State == EntityState.Detached) { _dbSet.Attach(tEntity); } _dbSet.Remove(tEntity); } public void Create(TEntity tEntity) { _dbSet.Add(tEntity); }
OK,接下来我们看一下IUserRepository,定义两个接口方法。
public interface IUserRepository : IRepository<Users> { IPagedList<Users> GetUserList(string userName, int pageIndex, int pageSize); Task<Users> GetUserById(int userId); }
再看一下实现,注意这里GetUserById是个异步方法。
public class UserRepository : BaseRepository<Users>, IUserRepository { public UserRepository(DbContext dbContext) : base(dbContext) { } public IPagedList<Users> GetUserList(string userName, int pageIndex, int pageSize) { IQueryable<Users> users = this._context.Set<Users>(); if (!string.IsNullOrEmpty(userName)) { users = users.Where(t => t.RealName.Contains(userName)); } return new PagedList<Users>(users, pageIndex, pageSize); } public async Task<Users> GetUserById(int userId) { return await this._context.Set<Users>().FirstOrDefaultAsync(u=>u.UserId==userId); } }
接下来我们来看下Service层,先看IUserService。
public interface IService<TEntity> where TEntity : class, new() { void Create(TEntity entity); void Remove(TEntity entity); void Update(TEntity entity); }
再看实现,全程泛型,注意这里的工作单元。
public class BaseService<TEntity> : IService<TEntity> where TEntity : class, new() { protected AttendanceManageContext dbContext; IRepository<TEntity> repository; protected IUnitOfWork unitOfWork; public BaseService() { this.dbContext = new AttendanceManageContext(); this.unitOfWork = new UnitOfWork(dbContext); this.repository = new BaseRepository<TEntity>(dbContext); } public void Create(TEntity entity) { this.repository.Create(entity); this.unitOfWork.Commit(); } public void Remove(TEntity entity) { this.repository.Remove(entity); this.unitOfWork.Commit(); } public void Update(TEntity entity) { this.repository.Update(entity); this.unitOfWork.Commit(); } }
ok,接下来我们看一下IUserService的定义。
public interface IUserService : IService<Users> { IPagedList<Users> GetUserList(string userName, int pageIndex, int pageSize); Task<Users> GetUserById(int userId); }
再看实现,很简单的模式,不解释。
public class UserService : BaseService<Users>, IUserService { IUserRepository userRepository; public UserService() { this.userRepository = new UserRepository(this.dbContext); } public IPagedList<Users> GetUserList(string userName, int pageIndex, int pageSize) { return this.userRepository.GetUserList(userName, pageIndex, pageSize); } public async Task<Users> GetUserById(int userId) { return await this.userRepository.GetUserById(userId); } }
现在万事具备,只欠东风。看一下表现层控制器。
using Microsoft.AspNetCore.Mvc; using CURDDemo.Models; using CURDDemo.Service.UserMng; using CURDDemo.Model.ViewModel; using CURDDemo.Resources; namespace CURDDemo.Controllers { public class HomeController : Controller { IUserService _userService; public HomeController(IUserService userService) { this._userService = userService; } public IActionResult Index(int pageIndex = 0, int pageSize = 5, string userName = "") { var userList = this._userService.GetUserList(userName, pageIndex, pageSize); UsersModel userModel = new UsersModel { UserList = userList, UserName = userName }; return View(userModel); } [HttpGet] public IActionResult Edit(int userId) { ViewBag.Title = UserResource.title_edit; var user = this._userService.GetUserById(userId).Result; return View("~/Views/Home/CreateOrUpdate.cshtml", user); } public IActionResult EditUser(Users user) { var userDB = this._userService.GetUserById(user.UserId).Result; if (userDB != null) { userDB.RealName = user.RealName; userDB.TelNumber = user.TelNumber; } this._userService.Update(userDB); return RedirectToAction("Index"); } [HttpGet] public IActionResult Create() { return View("~/Views/Home/CreateOrUpdate.cshtml"); } [HttpPost] public IActionResult CreateUser(Users user) { ViewBag.Title = UserResource.title_create; this._userService.Create(user); return RedirectToAction("Index"); } [HttpDelete] public IActionResult Remove(int userId) { var user = this._userService.GetUserById(userId).Result; if (user != null) { this._userService.Remove(user); } return RedirectToAction("Index"); } } }
注意这里的构造函数依赖注入,这里我们注入工具为Autofac。首先我们要在项目中引用Autofac,
选择管理NuGet程序包,找到Autofac.Extensions.DependencyInjection。
安装好之后,我们在项目中可以看到引用的dll。
ok,完了之后我们还要配置,打开Startup.cs。首先增加一个属性
public IContainer ApplicationContainer { get; private set; }
然后我们将方法ConfigureServices改造如下
public IServiceProvider ConfigureServices(IServiceCollection services) { var connection = @"Server=IGFMVQVM354PFSB\BRUCE;Database=AttendanceManage;Trusted_Connection=True;"; services.AddDbContext<AttendanceManageContext>(options => options.UseSqlServer(connection)); // Add framework services. services.AddApplicationInsightsTelemetry(Configuration); services.AddMvc(); var builder = new ContainerBuilder(); builder.RegisterType<UserService>().As<IUserService>().InstancePerLifetimeScope(); builder.Populate(services); this.ApplicationContainer = builder.Build(); return new AutofacServiceProvider(this.ApplicationContainer); }
OK,我们在这里已经注册了类型UserService和接口的映射关系,并且指明生命周期。好的,我们回到controller,其实controller的实现代码以及路由和之前的也没啥区别。
我们直接看UI,还是熟悉的razor。
首先我们看一下_Layout.cshtml。
<!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <title>@ViewData["Title"] - CURDDemo</title> <environment names="Development"> <link rel="stylesheet" href="http://www.mamicode.com/~/lib/bootstrap/dist/css/bootstrap.css" /> <link rel="stylesheet" href="http://www.mamicode.com/~/css/site.css" /> <script src="http://www.mamicode.com/~/lib/jquery/dist/jquery.js"></script> </environment> <environment names="Staging,Production"> <link rel="stylesheet" href="https://ajax.aspnetcdn.com/ajax/bootstrap/3.3.6/css/bootstrap.min.css" asp-fallback-href="http://www.mamicode.com/~/lib/bootstrap/dist/css/bootstrap.min.css" asp-fallback-test-class="sr-only" asp-fallback-test-property="position" asp-fallback-test-value="http://www.mamicode.com/absolute" /> <link rel="stylesheet" href="http://www.mamicode.com/~/css/site.min.css" asp-append-version="true" /> </environment> @Html.ApplicationInsightsJavaScript(TelemetryConfiguration) </head> <body> <div 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">CURDDemo</a> </div> <div class="navbar-collapse collapse"> <ul class="nav navbar-nav"> <li><a asp-area="" asp-controller="Home" asp-action="Index">Home</a></li> </ul> </div> </div> </div> <div class="container body-content"> @RenderBody() <hr /> <footer> <p>© 2016 - CURDDemo</p> </footer> </div> <environment names="Development"> <script src="http://www.mamicode.com/~/lib/bootstrap/dist/js/bootstrap.js"></script> </environment> <environment names="Staging,Production"> <script src="https://ajax.aspnetcdn.com/ajax/jquery/jquery-2.2.0.min.js" asp-fallback-src="http://www.mamicode.com/~/lib/jquery/dist/jquery.min.js" asp-fallback-test="window.jQuery"> </script> <script src="https://ajax.aspnetcdn.com/ajax/bootstrap/3.3.6/bootstrap.min.js" asp-fallback-src="http://www.mamicode.com/~/lib/bootstrap/dist/js/bootstrap.min.js" asp-fallback-test="window.jQuery && window.jQuery.fn && window.jQuery.fn.modal"> </script> <script src="http://www.mamicode.com/~/js/site.min.js" asp-append-version="true"></script> </environment> @RenderSection("scripts", required: false) </body> </html>
和之前的差不多,只不过上面分了开发环境还是生产环境的一些js的引用。ok,我们再看Index.cshtml。
@model CURDDemo.Model.ViewModel.UsersModel <div class="margin-t10"> <h4><label>用户信息列表</label></h4> </div> <div id="userlist" class="margin-t10"> @{await Html.RenderPartialAsync("~/Views/Partial/Users.cshtml", Model);} </div> <div class="row"> <div class="col-md-12"> @Html.ActionLink("新增", "Create", new { }, new { @class = "btn btn-info tab-btn" }) </div> </div>
一个用户信息列表界面,里面又引用了部分页users.cshtml。
@model CURDDemo.Model.ViewModel.UsersModel <table class="table table-bordered table-hover table-striped"> <tr> <th> 姓名 </th> <th> 电话号码 </th> <th> 操作 </th> </tr> @foreach (var user in Model.UserList) { <tr> <td> @user.RealName </td> <td> @user.TelNumber </td> <td> @Html.ActionLink("编辑","Edit",new { userId = user.UserId }, new { @class="btn btn-primary"}) @Html.ActionLink("删除", "Remove", new { userId = user.UserId }, new { @class = "btn btn-danger" }) </td> </tr> } </table> <div class="well well-sm"> 共有 <span style="color: blue" id="ft">@Model.UserList.TotalCount</span>条记录 当前是第 <span style="color: blue">@(Model.UserList.PageIndex + 1)</span> 页 共<span style="color: blue">@Model.UserList.TotalPages</span>页 @if (Model.UserList.HasPreviousPage) { @Html.ActionLink("首页", "Index/0/" + Model.UserList.PageSize, new { userName = Model.UserName }, new { data_ajax = "true", data_ajax_method = "GET", data_ajax_mode = "replace", data_ajax_update = "#userlist", style = "margin-left:5px;color:blue" }) @Html.ActionLink("上一页", "Index/" + (Model.UserList.PageIndex - 1 + "/" + Model.UserList.PageSize), new { userName = Model.UserName }, new { data_ajax = "true", data_ajax_method = "GET", data_ajax_mode = "replace", data_ajax_update = "#userlist", style = "margin-left:5px;color:blue" }) } else { <text>首页 </text> <text>上一页 </text> } @if (Model.UserList.HasNextPage) { @Html.ActionLink("下一页", "Index/" + (Model.UserList.PageIndex + 1) + "/" + Model.UserList.PageSize, new { userName = Model.UserName }, new { data_ajax = "true", data_ajax_method = "GET", data_ajax_mode = "replace", data_ajax_update = "#userlist", style = "margin-left:5px;color:blue" }) @Html.ActionLink("末页", "Index/" + (Model.UserList.TotalPages - 1) + "/" + Model.UserList.PageSize, new { userName = Model.UserName }, new { data_ajax = "true", data_ajax_method = "GET", data_ajax_mode = "replace", data_ajax_update = "#userlist", style = "margin-left:5px;color:blue" }) } else { <text>下一页 </text> <text>末页 </text> } </div>
不过是一个列表带分页。注意这里没有什么所谓的Ajax.ActionLink,只有Html.ActionLink,通过匿名对象设置ajax的一些配置。之前的AjaxOptions的一些属性的设置都成了这些data_属性。这里我们设置请求方式为Get,页面内容响应模式为替换,替换的div的id为userlist。Ok,讲到这里也就差不多了,之前我也讲过类似的代码,运行走起。
分页也是没什么问题的。
ok,我们点击编辑。
新增界面,看一下。
点击保存,有验证,为空通不过,看一下修改和创建界面。
@model CURDDemo.Models.Users <link rel="stylesheet" href="http://www.mamicode.com/~/js/validator/css/bootstrapValidator.css" /> <script type="text/javascript" src="http://www.mamicode.com/~/js/validator/js/bootstrapValidator.js"></script> <div class="margin-t10"> <h4><label>@ViewBag.Title</label></h4> </div> <form action="@Url.Action("EditUser")" method="put"> @Html.HiddenFor(m=>m.UserId) <div class="row"> <div class="col-md-12"> <div class="form-group"> <label>姓名:</label> @Html.TextBoxFor(m => m.RealName, new { @class = "form-control", maxlength = 30 }) </div> <div class="form-group"> <label>电话号码:</label> @Html.TextBoxFor(m => m.TelNumber, new { @class = "form-control", maxlength = 30 }) </div> <div class="margin-top-10"> <input type="submit" class="btn btn-primary tab-btn" value="http://www.mamicode.com/保存" /> <input type="reset" class="btn btn-primary tab-btn" /> </div> <div class="margin-top-10"> <span style="color: red"> @ViewBag.ErrorMsg </span> </div> </div> </div> </form> <script type="text/javascript"> $(function () { $(‘form‘).bootstrapValidator({ message: ‘This value is not valid‘, feedbackIcons: { valid: ‘glyphicon glyphicon-ok‘, invalid: ‘glyphicon glyphicon-remove‘, validating: ‘glyphicon glyphicon-refresh‘ }, fields: { RealName: { validators: { notEmpty: { message: ‘姓名不能为空‘ } } }, TelNumber: { validators: { notEmpty: { message: ‘电话号码不能为空‘ } } } } }); }); </script>
在这里我们使用了bootstrapValidator,语法很简单,就不解释了。不早了,今天就到这里。如果想要源码,请加群205217091
本文出自 “技术创造价值” 博客,请务必保留此出处http://leelei.blog.51cto.com/856755/1865210
ASP.NET Core 实战(一)CURD