千磨万击还坚劲,任尔东西南北风。这篇文章主要讲述一步一步创建ASP.NET MVC5程序[Repository+Autofac+Automapper+SqlSugar]相关的知识,希望能为你提供帮助。
前言Hi,
大家好,还是星期五,还是Rector,又在图享网准时和大家见面了。
今天给大家带来系列教程《一步一步创建ASP.NET MVC5程序[Repository+Autofac+Automapper+SqlSugar]》的第八期了,不知道你有没有按照教程将前七期的都实际练习一篇呢?如果是,你在练习的时候有没有遇到什么问题呢?
反正Rector是有收到部分童鞋发来他们练习过程中的问题反馈的哦。
【一步一步创建ASP.NET MVC5程序[Repository+Autofac+Automapper+SqlSugar]】如果你仔细阅读并实际练习了前面七期的教程,我相信,作为刚入门或者经验尚浅的你一定会有收获的。
加油吧,骚年!!! 人生苦短,就怕努力!!!
Rector这是要成为心理导师的节奏啊,一来就给大家灌饱心灵鸡汤。。。
**本文篇幅有点长,请作好心里准备!!!
同时,也吐个槽,本文看似内容简单的一B,但也花了笔者几个小时来准备示例以及写作,写技术文章真心伤不起
珍爱生命,远离程序!!!
**
还是回到我们的正题,开始我们今天的系列教程:《一步一步创建ASP.NET MVC5程序Repository+Autofac+Automapper+SqlSugar》
本文知识要点
- 用户注册/登录功能设计与实现
这个表呢,Rector已经为大家准备好了,mysql表结构如下:
SET FOREIGN_KEY_CHECKS = 0;
-- ----------------------------
-- Table structure for ts_user
-- ----------------------------
DROP TABLE IF EXISTS `tb_user`;
CREATE TABLE `tb_user`(
`Id` int(10) NOT NULL AUTO_INCREMENT,
`LoginName` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT ‘‘ COMMENT ‘登录名‘,
`Password` varchar(150) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT ‘‘ COMMENT ‘密码‘,
`DisplayName` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT ‘‘ COMMENT ‘显示名称‘,
`RealName` varchar(30) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT ‘‘ COMMENT ‘真实姓名‘,
`EmailAddress` varchar(120) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT ‘‘ COMMENT ‘电子邮箱‘,
`Avatar` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT ‘‘ COMMENT ‘用户头像‘,
`Status` int(2) NOT NULL DEFAULT 1 COMMENT ‘用户的状态,0:禁用,1:正常‘,
`Telephone` varchar(20) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT ‘‘ COMMENT ‘手机号码‘,
`Qq` varchar(15) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT ‘‘,
`WebsiteUrl` varchar(200) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT ‘‘,
`CreatedOn` datetime(0) NULL DEFAULT NULL COMMENT ‘用户创建时间‘,
`CreatedIp` varchar(24) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT ‘‘ COMMENT ‘创建用户时的IP地址‘,
`LoginCount` int(8) NULL DEFAULT 0 COMMENT ‘登录次数累加器‘,
`LatestLoginDate` datetime(0) NULL DEFAULT NULL COMMENT ‘最近一次登录时间‘,
`LatestLoginIp` varchar(24) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT ‘‘ COMMENT ‘最近一次登录时的IP地址‘,
`ModifiedOn` datetime(0) NULL DEFAULT NULL COMMENT ‘最近修改时间‘,
`Type` int(2) NULL DEFAULT 0 COMMENT ‘用户类型[-1:超级管理员,0:一般用户]‘,
PRIMARY KEY (`Id`) USING BTREE,
UNIQUE INDEX `IX_LoginName`(`LoginName`) USING BTREE,
UNIQUE INDEX `IX_EmailAddress`(`EmailAddress`) USING BTREE,
INDEX `IX_CreatedOn`(`CreatedOn`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 0 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;
SET FOREIGN_KEY_CHECKS = 1;
请直接复制以上MySQL脚本,然后到对应数据执行即可,当然你也可以在这个版本的源码里面找到。 这个表就不准备提前写入示例数据了,一会我们用注册功能来写入数据。
创建领域实体和视图实体
在项目 【TsBlog.Domain】中的Entities文件夹中创建 User.cs 实体类:
using SqlSugar;
using System;
namespace TsBlog.Domain.Entities
{
[SugarTable("
tb_user"
)]
public class User
{
[SugarColumn(IsIdentity = true, IsPrimaryKey = true)]
public int Id { get;
set;
}
public string LoginName { get;
set;
}
public string Password { get;
set;
}
public string RealName { get;
set;
}
public string EmailAddress { get;
set;
}
public string Avatar { get;
set;
}
public int Status { get;
set;
}
public string Telephone { get;
set;
}
public string Qq { get;
set;
}
public string WebsiteUrl { get;
set;
}
public DateTime CreatedOn { get;
set;
}
public string CreatedIp { get;
set;
}
public int LoginCount { get;
set;
}
public DateTime? LatestLoginDate { get;
set;
}
public string LatestLoginIp { get;
set;
}
public DateTime? ModifiedOn { get;
set;
}
public int Type { get;
set;
}
}
}
再在项目【TsBlog.ViewModel】中创建 User 的文件夹,并创建以下几个视图实体类
LoginViewModel.cs
using System.ComponentModel.DataAnnotations;
namespace TsBlog.ViewModel.User
{
/// <
summary>
/// 用户登录视图实体
/// <
/summary>
public class LoginViewModel
{
[Required(ErrorMessage = "
请输入用户"
)]
[Display(Name = "
用户名"
)]
public string UserName { get;
set;
}
[Required(ErrorMessage = "
请输入密码"
)]
[Display(Name = "
密码"
)]
[DataType(DataType.Password)]
public string Password { get;
set;
}
}
}
RegisterViewModel.cs
using System.ComponentModel.DataAnnotations;
namespace TsBlog.ViewModel.User
{
/// <
summary>
/// 用户注册视图实体
/// <
/summary>
public class RegisterViewModel
{
[Required(ErrorMessage = "
请输入用户名"
)]
[Display(Name = "
用户名"
)]
public string UserName { get;
set;
}
[Required(ErrorMessage = "
请输入密码"
)]
[Display(Name = "
密码"
)]
[DataType(DataType.Password), MaxLength(20, ErrorMessage = "
密码最大长度为20个字符"
), MinLength(6, ErrorMessage = "
密码最小长度为6个字符"
)]
public string Password { get;
set;
}[Required(ErrorMessage = "
请输入确认密码"
)]
[Display(Name = "
确认密码"
)]
[DataType(DataType.Password), Compare("
Password"
, ErrorMessage = "
两次密码不一致"
)]
public string ConfirmPassword { get;
set;
}
}
}
UserViewModel.cs:
using System;
namespace TsBlog.ViewModel.User
{
/// <
summary>
/// 与领域用户实体对应的用户视图实体
/// <
/summary>
public class UserViewModel
{
public int Id { get;
set;
}
public string LoginName { get;
set;
}
public string Password { get;
set;
}
public string RealName { get;
set;
}
public string EmailAddress { get;
set;
}
public string Avatar { get;
set;
}
public int Status { get;
set;
}
public string Telephone { get;
set;
}
public string Qq { get;
set;
}
public string WebsiteUrl { get;
set;
}
public DateTime CreatedOn { get;
set;
}
public string CreatedIp { get;
set;
}
public int LoginCount { get;
set;
}
public DateTime? LatestLoginDate { get;
set;
}
public string LatestLoginIp { get;
set;
}
public DateTime? ModifiedOn { get;
set;
}
public int Type { get;
set;
}
}
}
仓储层
在项目【TsBlog.Repositories】中创建 IUserRepository.cs 以及其实现类 UserRepository.cs。
IUserRepository.cs:
using TsBlog.Domain.Entities;
namespace TsBlog.Repositories
{
public interface IUserRepository : IRepository<
User>
{}
}
UserRepository.cs:
using TsBlog.Domain.Entities;
namespace TsBlog.Repositories
{
public class UserRepository : GenericRepository<
User>
, IUserRepository
{}
}
服务层
在项目【TsBlog.Services】中创建 IUserService.cs 以及其实现类 UserService.cs。
IUserService.cs:
using TsBlog.Domain.Entities;
namespace TsBlog.Services
{
public interface IUserService : IService<
User>
{
User FindByLoginName(string loginName);
}
}
UserService.cs:
using TsBlog.Domain.Entities;
using TsBlog.Repositories;
namespace TsBlog.Services
{
public class UserService : GenericService<
User>
, IUserService
{
private readonly IUserRepository _repository;
public UserService(IUserRepository repository) : base(repository)
{
_repository = repository;
}public User FindByLoginName(string loginName)
{
return _repository.FindByClause(x =>
x.LoginName == loginName);
}
}
}
创建加密类
在解决方案文件夹【1.Libraries】中创建一个新的项目,取名为【TsBlog.Core】,在此项目中先创建一个名为 Security的文件夹,再创建一个加密类 Encryptor.cs:
using System.Security.Cryptography;
using System.Text;
namespace TsBlog.Core.Security
{
/// <
summary>
/// 加密静态类
/// <
/summary>
public static class Encryptor
{
//MD5加密一个字符串
public static string Md5Hash(string text)
{
MD5 md5 = new MD5CryptoServiceProvider();
md5.ComputeHash(Encoding.ASCII.GetBytes(text));
var result = md5.Hash;
var strBuilder = new StringBuilder();
foreach (var t in result)
{
strBuilder.Append(t.ToString("
x2"
));
}return strBuilder.ToString();
}
}
}
在用户注册或者登录时,我们将使用这个MD5加密用户的密码,并将其保存到数据库中(数据库中保存明文的密码是非常危险的,特别是在重要的安全级别很高的项目中,千(不)万(信)别(你)这(试)样(一)做(下)!!!)。
创建控制器
在项目【TsBlog.Frontend】中创建控制器 AccountController.cs,并添加如下代码:
AccountController.cs
using System;
using System.Web.Mvc;
using TsBlog.Core.Security;
using TsBlog.Domain.Entities;
using TsBlog.Services;
using TsBlog.ViewModel.User;
namespace TsBlog.Frontend.Controllers
{
/// <
summary>
/// 用户中心控制器
/// <
/summary>
public class AccountController : Controller
{
/// <
summary>
/// 用户服务接口
/// <
/summary>
private readonly IUserService _userService;
public AccountController(IUserService userService)
{
_userService = userService;
}/// <
summary>
/// 登录页面
/// <
/summary>
/// <
returns>
<
/returns>
public ActionResult Login()
{
return View();
}/// <
summary>
/// 提交登录请求
/// <
/summary>
/// <
param name="
model"
>
<
/param>
/// <
returns>
<
/returns>
[HttpPost, ValidateAntiForgeryToken, AllowAnonymous]
public ActionResult Login(LoginViewModel model)
{
//如果视图模型中的属性没有验证通过,则返回到登录页面,要求用户重新填写
if (!ModelState.IsValid)
{
return View(model);
}//根据用户登录名查询指定用户实体
var user = _userService.FindByLoginName(model.UserName.Trim());
//如果用户不存在,则携带错误消息并返回登录页面
if (user == null)
{
ModelState.AddModelError("
error_message"
, "
用户不存在"
);
return View(model);
}//如果密码不匹配,则携带错误消息并返回登录页面
if (user.Password != Encryptor.Md5Hash(model.Password.Trim()))
{
ModelState.AddModelError("
error_message"
, "
密码错误,请重新登录"
);
return View(model);
}//并用户实体保存到Session中
Session["
user_account"
] = user;
//跳转到首页
return RedirectToAction("
index"
, "
home"
);
}/// <
summary>
/// 注册页面
/// <
/summary>
/// <
returns>
<
/returns>
public ActionResult Register()
{
return View();
}/// <
summary>
/// 提交注册请求
/// <
/summary>
/// <
param name="
model"
>
<
/param>
/// <
returns>
<
/returns>
[HttpPost, ValidateAntiForgeryToken, AllowAnonymous]
public ActionResult Register(RegisterViewModel model)
{
//如果视图模型中的属性没有验证通过,则返回到注册页面,要求用户重新填写
if (!ModelState.IsValid)
{
return View(model);
}//创建一个用户实体
var user = new User
{
LoginName = model.UserName,
Password = Encryptor.Md5Hash(model.Password.Trim()),
CreatedOn = DateTime.Now
//由于是示例教程,所以其他字段不作填充了
};
//将用户实体对象写入数据库中
var ret = _userService.Insert(user);
if (ret <
= 0)
{
//如果注册失败,则携带错误消息并返回注册页面
ModelState.AddModelError("
error_message"
, "
注册失败"
);
return View(model);
}
//如果注册成功,则跳转到登录页面
return RedirectToAction("
login"
);
}
}
}
添加必要JS库
由于之前我们将项目中的多余的JS库全部移除掉了,所以现在我们重新安装一下我们项目中将要到的一些JS库,包括:jQuery,Bootstrap等,都使用Nuget来安装,方便统一管理和升级。
安装jQuery:
文章图片
安装Bootstrap:
文章图片
安装jquery.validate.bootstrap:
文章图片
安装完成后的JS库文件夹:
文章图片
完成注册页面
在 [Views/Account]文件夹中创建注册页面视图 register.cshtml:
@model TsBlog.ViewModel.User.RegisterViewModel
@{
Layout = null;
}
<
!DOCTYPE html>
<
html>
<
head>
<
meta name="
viewport"
content="
width=device-width"
/>
<
title>
用户注册<
/title>
<
style type="
text/css"
>
* { box-sizing: border-box;
}
body { box-sizing: border-box;
margin: 0;
padding: 0;
color: #333;
}
.account-container { position: absolute;
margin: auto;
top: 0;
right: 0;
bottom: 0;
left: 0;
width: 400px;
height: 480px;
background-color: #fff;
/*border-radius: 3px;
*/ box-shadow: 0 2px 2px 0 rgba(0,0,0,0.14), 0 3px 1px -2px rgba(0,0,0,0.12), 0 1px 5px 0 rgba(0,0,0,0.2);
}.account-signin-container { margin-top: 15px;
}
.account-signin-container h1 { font-size: 20px;
border-bottom: 2px solid #f7f7f7;
margin: 0 0 15px;
padding-bottom: 10px;
padding-left: 15px;
letter-spacing: 0.1em;
}
.account-form { padding: 15px;
}
.account-form .form-group { width: 100%;
margin-bottom: 15px;
}
.account-form .form-group label { width: 100%;
display: block;
}
.account-form .form-group input { border: 1px solid #ccc;
line-height: 32px;
font-size: 16px;
padding: 2px 0px;
padding-left: 5px;
display: block;
width: 100%;
margin-top: 5px;
}
.account-form #btn_register { border: 0;
background: #3b78e7;
color: #fff;
font-size: 18px;
font-weight: bold;
padding: 8px 25px;
cursor: pointer;
margin-top: 15px;
display: inline-block;
box-shadow: 0 2px 2px 0 rgba(0,0,0,0.14), 0 3px 1px -2px rgba(0,0,0,0.12), 0 1px 5px 0 rgba(0,0,0,0.2);
border-radius: 3px;
min-width: 100px;
text-align: center;
}
.account-form #btn_register:hover { background: #4885F3;
}
span.error { color: #f00;
}
.btn-login { float: right;
display: block;
margin-top: 25px;
color: #4885f3;
}@@media(max-width:500px) {
.account-container { width: 100%;
height: 100vh;
}
}
<
/style>
<
/head>
<
body>
<
div class="
account-container"
>
<
div class="
account-modal-container"
>
<
div class="
modal"
>
<
/div>
<
div class="
load-bar-container"
>
<
div class="
load-bar"
>
<
div class="
bar"
>
<
/div>
<
div class="
bar"
>
<
/div>
<
div class="
bar"
>
<
/div>
<
/div>
<
/div>
<
div class="
account-signin-container"
>
<
h1>
用户注册<
/h1>
@using (Html.BeginForm("
register"
, "
account"
, FormMethod.Post, new { @class = "
account-form"
, role = "
form"
}))
{
@Html.ValidationMessage("
error_message"
, new { @class = "
error"
})
@Html.AntiForgeryToken()
<
div class="
form-group"
>
<
label>
<
span>
登录名:<
/span>
@Html.TextBoxFor(m =>
m.UserName, new { placeholder = "
请输入登录名"
})
@Html.ValidationMessageFor(m =>
m.UserName, "
"
, new { @class = "
error"
})
<
/label>
<
/div>
<
div class="
form-group"
>
<
label>
<
span>
密码:<
/span>
@Html.PasswordFor(m =>
m.Password, new { placeholder = "
请输入密码"
})
@Html.ValidationMessageFor(m =>
m.Password, "
"
, new { @class = "
error"
})
<
/label>
<
/div>
<
div class="
form-group"
>
<
label>
<
span>
确认密码:<
/span>
@Html.PasswordFor(m =>
m.ConfirmPassword, new { placeholder = "
请输入确认密码"
})
@Html.ValidationMessageFor(m =>
m.ConfirmPassword, "
"
, new { @class = "
error"
})
<
/label>
<
/div>
<
div class="
form-group"
>
<
button id="
btn_register"
type="
submit"
>
注 册<
/button>
<
a class="
btn-login"
href=https://www.songbingjia.com/android/"
~/account/login"
>
登录<
/a>
<
/div>
}<
/div>
<
/div>
<
/div>
<
script src="
~/Scripts/jquery-3.2.1.min.js"
>
<
/script>
<
script src="
~/Scripts/jquery.validate.min.js"
>
<
/script>
<
script src="
~/Scripts/jquery.validate.unobtrusive.min.js"
>
<
/script>
<
/body>
<
/html>
再在当前文件夹下创建 login.cshtml 视图文件用作登录页面:
@model TsBlog.ViewModel.User.LoginViewModel
@{
Layout = null;
}
<
!DOCTYPE html>
<
html>
<
head>
<
meta name="
viewport"
content="
width=device-width"
/>
<
title>
用户登录<
/title>
<
style type="
text/css"
>
* { box-sizing: border-box;
}
body { box-sizing: border-box;
margin: 0;
padding: 0;
color: #333;
}.account-container { position: absolute;
margin: auto;
top: 0;
right: 0;
bottom: 0;
left: 0;
width: 400px;
height: 450px;
background-color: #fff;
/*border-radius: 3px;
*/ box-shadow: 0 2px 2px 0 rgba(0,0,0,0.14), 0 3px 1px -2px rgba(0,0,0,0.12), 0 1px 5px 0 rgba(0,0,0,0.2);
}.account-signin-container { margin-top: 15px;
}
.account-signin-container h1 { font-size: 20px;
border-bottom: 2px solid #f7f7f7;
margin: 0 0 15px;
padding-bottom: 10px;
padding-left: 15px;
letter-spacing: 0.1em;
}
.account-form { padding: 15px;
}
.account-form .form-group { width: 100%;
margin-bottom: 15px;
}
.account-form .form-group label { width: 100%;
display: block;
}
.account-form .form-group input { border: 1px solid #ccc;
line-height: 32px;
font-size: 16px;
padding: 2px 0px;
padding-left: 5px;
display: block;
width: 100%;
margin-top: 5px;
}
.account-form #btn_login { border: 0;
background: #3b78e7;
color: #fff;
font-size: 18px;
font-weight: bold;
padding: 8px 25px;
cursor: pointer;
margin-top: 15px;
display: inline-block;
box-shadow: 0 2px 2px 0 rgba(0,0,0,0.14), 0 3px 1px -2px rgba(0,0,0,0.12), 0 1px 5px 0 rgba(0,0,0,0.2);
border-radius: 3px;
min-width: 100px;
text-align: center;
}
.account-form #btn_login:hover { background: #4885F3;
}
span.error { color: #f00;
}
.btn-register { float: right;
display: block;
margin-top: 25px;
color: #4885f3;
}@@media(max-width:500px) {
.account-container { width: 100%;
height: 100vh;
}
}
<
/style>
<
/head>
<
body>
<
div class="
account-container"
>
<
div class="
account-modal-container"
>
<
div class="
modal"
>
<
/div>
<
div class="
load-bar-container"
>
<
div class="
load-bar"
>
<
div class="
bar"
>
<
/div>
<
div class="
bar"
>
<
/div>
<
div class="
bar"
>
<
/div>
<
/div>
<
/div>
<
div class="
account-signin-container"
>
<
h1>
用户登录<
/h1>
@using (Html.BeginForm("
login"
, "
account"
, FormMethod.Post, new { @class = "
account-form"
, role = "
form"
}))
{
@Html.ValidationMessage("
error_message"
, new { @class = "
error"
})
@Html.AntiForgeryToken()
<
div class="
form-group"
>
<
label>
<
span>
登录名:<
/span>
@Html.TextBoxFor(m =>
m.UserName, new { placeholder = "
请输入登录名"
})
@Html.ValidationMessageFor(m =>
m.UserName, "
"
, new { @class = "
error"
})
<
/label>
<
/div>
<
div class="
form-group"
>
<
label>
<
span>
密码:<
/span>
@Html.PasswordFor(m =>
m.Password, new { placeholder = "
请输入密码"
})
@Html.ValidationMessageFor(m =>
m.Password, "
"
, new { @class = "
error"
})
<
/label>
<
/div>
<
div class="
form-group"
>
<
button id="
btn_login"
type="
submit"
>
登 录<
/button>
<
a class="
btn-register"
href=https://www.songbingjia.com/android/"
~/account/register"
>
注册账号<
/a>
<
/div>
}<
/div>
<
/div>
<
/div>
<
script src="
~/Scripts/jquery-3.2.1.min.js"
>
<
/script>
<
script src="
~/Scripts/jquery.validate.min.js"
>
<
/script>
<
script src="
~/Scripts/jquery.validate.unobtrusive.min.js"
>
<
/script>
<
script src="
~/res/assets/notie/notie.min.js"
>
<
/script>
<
/body>
<
/html>
这两个页面均是响应式的布局,可适应不同设备。
好了,关于注册和登录的逻辑以及页面都完成了,那么运行项目,打开注册页面:http://localhost:54739/account/register,具体的注册请自行体验:
文章图片
注册成功后,系统将带你到登录页面:
文章图片
具体功能也请自行体验。
以上,我们只完成了注册和登录的基本功能,接下来我们来体验一下简单的权限访问,在本期教程之前,我们的: http://localhost:54739/home/index 以及 http://localhost:54739/home/post 是可以直接访问的,现在我们给这两个页面添加访问权限,即只有登录后才能访问,修改 HomeController.cs 如下:
using System.Web.Mvc;
using TsBlog.AutoMapperConfig;
using TsBlog.Services;
namespace TsBlog.Frontend.Controllers
{
public class HomeController : Controller
{
private readonly IPostService _postService;
public HomeController(IPostService postService)
{
_postService = postService;
}
public ActionResult Index()
{
//如果未登录,则跳转到登录页面
if (Session["
user_account"
] == null)
{
return RedirectToAction("
login"
, "
account"
);
}
return View();
}public ActionResult Post()
{
//如果未登录,则跳转到登录页面
if (Session["
user_account"
] == null)
{
return RedirectToAction("
login"
, "
account"
);
}var post = _postService.FindById(1).ToModel();
return View(post);
}
}
}
重新编译项目,按F5运行,再打开地址:http://localhost:54739/home/index ,发生了什么情况? 是不是被重定向到了登录页面,要求你登录? 这就对了,输入你刚才注册的用户名和密码,登录后,系统会重新带你到:http://localhost:54739/home/index 页面。
OK,今天这期的关于用户注册和登录功能就介绍到这里,本期只实现了简单的功能,在后续的教程中将重构和封装相应的功能代码,敬请期待。。。
如果你喜欢Rector的本系列文章,请为我点个大大的赞。
看完教程如果觉得还不过瘾的,遇到问题的,想“勾对”的,欢迎加入图享网官方QQ群:483350228。有什么,你懂的。。。
谢谢你的耐心阅读,未完待续,我们下期再见……
本期源码托管,请至首发地址获取-- 《一步一步创建ASP.NET MVC5程序[Repository+Autofac+Automapper+SqlSugar](八)》
数据库脚本文件请到目录下获取:TsBlog\document\scripts\mysql\v1.8\
本文来源自 图享网 《一步一步创建ASP.NET MVC5程序[Repository+Autofac+Automapper+SqlSugar](八)》
推荐阅读
- Android-25种开源炫酷动画框架
- Android webView包装WebAPP
- mac下Appium环境配置
- 一张图片让你了解android的事件分发机制
- Delphi XE6 Android拨号函数
- 如何处理App的Application的事件
- 如何在Magento 2中设置货到付款(COD)付款方式()
- Android Studio更改工程名异常解决方案 (can't rename root module)
- Data Binding Android - Type parameter T has incompatible upper bounds : ViewDataBinding and MainAct(