优点 | 缺点 | |
大白菜 | 可还原 U 盘空间 支持 UEFI | 众多山寨产品,分不清哪个是正宗老牌,说不定还是新秀更优秀 Window 10 自带防病毒软件 Window Defender 提示发现威胁 一键制作的大白菜U盘,将 iso 拷入U盘后安装的纯净系统会被捆绑安装垃圾软件 |
老毛桃 | 可还原 U 盘空间 支持 UEFI PE 还不错 | “ISO 模式”没什么问题,但“一键制作”的是 FAT32 格式,不支持将大于 4GB 的 ISO 镜像拷贝到 U 盘 “模拟启动”可启动老毛桃界面,但直接写入硬盘映像的 U 盘无法模拟启动 卸载时,Window 10 自带防病毒软件 Window Defender 提示发现特洛伊木马 |
软碟通 UltraISO | 中规中矩 写入硬盘不会自动还原 U 盘空间 | 网上找序列号激活 |
推荐! | 开源 可同时拷入多个系统镜像文件,并支持启动时选择 可支持格式化为多种文件格式 一键制作,可升级 | 暂无 |
软碟通制作 Windows 系统安装盘过程:
下载微软官方提供的 iso 系统镜像文件
如果 U 盘容量缩水,或根本无法正常使用 U 盘,使用老毛桃还原 U 盘空间
打开 UltraISO,打开要写入 U 盘的 iso 文件
在菜单上选择:启动 - 写入硬盘映像...
选择硬盘驱动器,格式化,写入(注意:点击“写入”是按 FAT32 重新格式化的,若要制作 NTFS 文件系统的 U 盘,请先按 NTFS 格式化,然后在此界面上点击“便捷启动”-“便捷写入”)
如果 U 盘足够大,可以在 U 盘里多放几个系统镜像,双击打开另一个 iso,将所有文件和文件夹复制到 U 盘上新建的目录中。这样下次需要安装什么系统时,只要把对应的系统的文件放到 U 盘根目录,其它系统移到二级目录中即可。当然还可以往里面存放一些常用软件。
另外最好常备一个带有 PE 系统的启动盘,在旧电脑上安装旧系统时经常会用到。
如果在分区那步无法删除或创建分区(提示动态卷什么的),在确认放弃磁盘中的文件的前提下,可以使用 Shift + F10 调出命令行,键入 diskpart,列出磁盘(list disk),选择磁盘(select disk 0),清除(clean),注意 U 盘启动盘也会在列表中,勿删!

jquery.barrager.js 是一款优雅的网页弹幕插件,支持显示图片,文字以及超链接。支持速度、高度、颜色、数量等自定义。
使用方法参官方网站:http://yaseng.org/jquery.barrager.js/
jquery.barrager.min.js 会报错,改用 jquery.barrager.js(版本 1.1)
实战示例:https://xoyozo.net/Demo/DanMu
该示例重写了部分 CSS 样式。
注:info 属性支持 HTML,因此要自己过滤危险标签,建议仅保留 <img /> 标签
如果要显示在指定区域,可以使用 bottom 属性,例:
bottom: (Math.random() * (0.95 - 0.5) + 0.5) * $(window).height()
建议加 loading 锁,以适应网络不通畅的环境(ajax 时间超过定时间隔会导致请求到重复的数据)
跨域环境可以使用 JSONP 来实现,服务器端需设置 Access-Control-Allow-Origin 为 *。

打开:\WeiXinMPSDK-master\src\Senparc.Weixin.MP.Sample\Senparc.Weixin.MP.Sample.sln
工具 - NuGet 包管理器 - 管理解决方案的 NuGet 程序包 - 更新
选择所有的包(Microsoft.Net.Http 无法更新,暂不勾选),更新
Microsoft.Net.Http 是个老顽固,无法更新也无法卸载,原因是它的语言包 Microsoft.Net.Http.zh-Hans 无法安装。我们切换到“已安装”选项卡,搜索“Microsoft.Net.Http”,选中 Microsoft.Net.Http.zh-Hans 并卸载它,现在我们可以更新 Microsoft.Net.Http 了。
【可能】运行报错:
“/”应用程序中的服务器错误。
配置错误
说明: 在处理向该请求提供服务所需的配置文件时出错。请检查下面的特定错误详细信息并适当地修改配置文件。
分析器错误消息: 创建 system.web.webPages.razor/host 的配置节处理程序时出错: 未能加载文件或程序集“System.Web.WebPages.Razor, Version=2.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35”或它的某一个依赖项。找到的程序集清单定义与程序集引用不匹配。 (异常来自 HRESULT:0x80131040)
源错误:行 4: <configSections>
行 5: <sectionGroup name="system.web.webPages.razor" type="System.Web.WebPages.Razor.Configuration.RazorWebSectionGroup, System.Web.WebPages.Razor, Version=2.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35">
行 6: <section name="host" type="System.Web.WebPages.Razor.Configuration.HostSection, System.Web.WebPages.Razor, Version=2.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" requirePermission="false" />
行 7: <section name="pages" type="System.Web.WebPages.Razor.Configuration.RazorPagesSection, System.Web.WebPages.Razor, Version=2.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" requirePermission="false" />
行 8: </sectionGroup>
源文件: E:\WeiXinMPSDK-master\src\Senparc.Weixin.MP.Sample\Senparc.Weixin.MP.Sample\views\web.config 行: 6展开 Senparc.Weixin.MP.Sample 引用,查看 System.Web.Razor 的属性,复制版本号。打开 \views\web.config,修改 System.Web.WebPages.Razor 的 Version(有 3 处)。
对于小型开发项目来说,我习惯将所有代码放在同一个项目(Project)中来,所以做了以下处理:(盛派官方分离这些代码是为了同时供 MVC 和 WebForms 重用)
在 Senparc.Weixin.MP.Sample 中添加文件夹 CommonService,将 Senparc.Weixin.MP.Sample.CommonService 项目中的文件夹和文件(.config 除外)拖到 CommonService 文件夹中。
卸载 Senparc.Weixin.MP.Sample.CommonService 项目并删除引用。
在解决方案的 Libraries 文件夹中,我们看到 Sample 项目了以下功能模块,这些就是 Senparc.Weixin 的源代码,如果不需要修改,可以卸载它们改为使用从 NuGet 获取,以便随时更新到最新版本:
Senparc.WebSocket
Senparc.Weixin
Senparc.Weixin.MP
Senparc.Weixin.MP.MvcExtension NuGet 中对应的包名为:Senparc.Weixin.MP.MVC
Senparc.Weixin.Open
Senparc.Weixin.Work
Senparc.Weixin.WxOpen
Senparc.Weixin.Cache.Memcached
Senparc.Weixin.Cache.Redis
卸载这些项目,并在 NuGet 中安装这些模块(搜索“Senparc.Weixin”第一页都在了,认准作者 Jeffrey Su)
单元测试项目中的引用也按需更换,当然也可以卸载这些单元测试项目(视情况保留 Senparc.Weixin.MP.Sample.Tests) 。
【可能】运行报错
HTTP Error 500.19 - Internal Server Error
无法访问请求的页面,因为该页的相关配置数据无效。
配置源:
134: </sessionState>
135: <sessionState customProvider="Memcached" mode="Custom">
136: <providers>
可以在 Global.asax 的 RegisterWeixinCache() 中修改 Memcached 配置。
或者打开 Web.config,找到 Memcached 所在的 sessionState 标签,注释掉,这样程序自动使用上方的 InProc(应用进程内)。
如果开启了 ASP.NET State Service 服务,那么建议改用 StateServer,与 Custom 一样需要序列化,方便日后改用 Memcached 或 Redis。
再次更新 NuGet,否则会提示无法加载 Enyim.Caching 的错误(报错行:MemcachedObjectCacheStrategy.RegisterServerList(memcachedConfig);)。
接下来,进入微信公众平台,在左侧 开发 - 基本配置 中设置相关参数(填写“服务器地址(URL)”如“https://weixin.xoyozo.net/weixin/”),打开 Sample 项目中的 Web.config,配置我们的公众号参数。
将项目发布到服务器上,尝试向公众号发送消息吧。
单元测试
设置单元测试中的公众号配置项:打开 Senparc.Weixin.MP.Test/CommonAPIs/CommonApiTest.cs,在 AppConfig 方法中可以看到支持两种配置方式,在 Senparc.Weixin.MP.Test/Config/test.config 配置文件中配置,或直接在 CommonApiTest.cs 文件中配置,前者格式如下,后者建议只在个人测试项目中使用。
<Config> <AppId>YourAppId</AppId> <Secret>YourSecret</Secret> <MchId>YourMchId</MchId> <TenPayKey>YourTenPayKey</TenPayKey> <TenPayCertPath>YourTenPayCertPath</TenPayCertPath> <!-- 小程序 --> <WxOpenAppId>YourOpenAppId</WxOpenAppId> <WxOpenSecret>YourWxOpenSecret</WxOpenSecret> </Config>
另外,将 _testOpenId 值改为自己的微信号在本公众号上的 openid。
微信网页授权(OAuth2)
打开 OAuth2Controller 控制器,将“Index”Action 中的盛派的网址(有 2 处)改成:
Request.Url.Scheme + "://" + Request.Url.Authority + "/Oauth2/……
未完待续……

官方 Demo
WeUI:https://weui.io/
weui.js:https://weui.io/weui.js/
开发文档
WeUI:https://github.com/Tencent/weui/wiki
weui.js:https://github.com/Tencent/weui.js/blob/master/docs/README.md
WeUI for 小程序:https://github.com/Tencent/weui-wxss
引入
① <head /> 中加入:
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width,initial-scale=1,user-scalable=0" />
② 引用样式:(请更改地址中的版本号,参开发文档)
<link href="https://res.wx.qq.com/t/wx_fed/weui-source/res/2.5.16/weui.min.css" rel="stylesheet" />
③ (可选)引用脚本:(请将下方链接中的版本号替换为最新)
<script src="https://res.wx.qq.com/t/wx_fed/weui.js/res/1.2.17/weui.min.js"></script>
经验
搜索框的 <form /> 中不加 action 则键盘中显示灰色“换行”键,加入 action 则键盘中显示蓝色“搜索”键

官方教程:Getting started with ASP.NET Core MVC and Entity Framework Core using Visual Studio
第一课:入门
Entity Framework Core NuGet 软件包
安装数据库提供程序(以 SQL Server 为例):其它数据库提供程序
Install-Package Microsoft.EntityFrameworkCore.SqlServer
该软件包依赖 Microsoft.EntityFrameworkCore 和 Microsoft.EntityFrameworkCore.Relational
新建数据模型
文件夹 Models 存放实体类
默认以 ID 或 类名ID 为主键列,并且是标识(即自动递增),以下代码可以取消标识,允许自定义 ID 值
[DatabaseGenerated(DatabaseGeneratedOption.None)]
导航属性:对于数据库来说就是外键,对一定义为实体类,对多定义为 ICollection<T>
字段类型可以是枚举 enum
创建数据库上下文
在 Data 文件夹中创建一个名为 SchoolContext.cs 的类,继承于 DbContext
使用依赖注入注册上下文
修改 Startup.cs 的 ConfigureServices 方法
在 appsettings.json 中配置数据库连接字符串
添加代码以使用测试数据初始化数据库
在 Data 文件夹中创建一个名为 DbInitializer.cs 的类
修改 Startup.cs 的 Configure 方法
新建一个控制器和视图
在 Controllers 文件夹上右键,添加
MVC 依赖项选择 Minimal Dependencies 即可
会自动安装 NuGet 包:Microsoft.EntityFrameworkCore.Design 和 Microsoft.EntityFrameworkCore.SqlServer.Design
公约
表名优先来自 DbSet 属性名,其次是类名
实体属性名将映射为列名
实体属性名 ID 或 类名ID 将默认被设为主键
如果属性被命名,它将当作外键(例如,导航属性 Student 对应的实体 Student 的主键是 ID,所以 StudentID 是外键);
公约可以被覆盖,例如,可以指定表名,设置列名,并将任何属性设置为主键或外键。
异步代码
第二课:新建、读取、更新和删除操作(CRUD)
自定义详细信息页面
Include() // 加载导航属性
ThenInclude() // 加载导航属性的导航属性
AsNoTracking() // 使该实体在当前上下文生命周期中不更新
SingleOrDefaultAsync() // 异步检索单个实体
路由数据
<a asp-action="Edit" asp-route-id="@item.ID">Edit</a>
<a asp-action="Edit" asp-route-studentID="@item.ID">Edit</a>
生成为
<a href="/Students/Edit/6">Edit</a>
<a href="/Students/Edit?studentID=6">Edit</a>
改进“新建页面”
在 HttpPost 的 Create 方法中,因为 ID 是自动递增的,所以可以从 Bind 中删除
[ValidateAntiForgeryToken] 搭配 if (ModelState.IsValid),用于实现验证,防止 CSRF 攻击
改进“删除页面”
在 HttpGet 的 Delete 方法中增加参数用于传递报错内容
在 HttpPost 的 Delete 方法中删除失败则 RedirectToAction 到 Delete 页面并带上报错参数
用”初始化和附加“的方法实现删除
Student studentToDelete = new Student() { ID = id };
_context.Entry(studentToDelete).State = EntityState.Deleted;
实例化要删除的实体(仅指定主键值),并将状态设置为 Deleted,即可在 SaveChanges 时实现删除,从而避免了删除必须先读取的情况。
处理交易
当 SaveChanges 时,EF 自动确保所有更改都成功或都失败,如果首先完成一些更改,然后发生错误,那么这些更改将自动回滚。
无追踪查询
什么情况下适用 AsNoTracking 方法:
在上下文中不需要更新任何实体,并且不需要加载导航属性(若需要导航属性,可以预先 Include 或稍后单独查询)
检索大量数据时,仅一小部分将被更新(稍后单独查询)
当前检索的实体需要在稍后以不同的目的附加(_context.Entry(*).State)
第三课:排序,过滤,分布和分组
添加列排序链接到学生索引页
将“搜索框”添加到“学生索引”页面
将分页功能添加到“学生索引”页面
PaginatedList 类的使用
创建一个显示学生统计信息的关于页面
创建视图模型:在 Models 文件夹中创建一个 SchoolViewModels 文件夹,添加一个类文件 EnrollmentDateGroup.cs
修改 HomeController 添加上下文
使用 group by 进行分组查询
第四课:迁移
用于迁移的 NuGet 软件包
要使用迁移,可以使用包管理器控制台(PMC)或命令行界面(CLI)。教程
CLI 的另一种打开方式:在项目上右键 - 在文件资源管理器中打开文件夹,在地址栏上键入 cmd 回车
删除数据库的 CLI 命令
dotnet ef database drop
创建初始迁移
dotnet ef migrations add InitialCreate
创建数据库的代码位于 Migrations 文件夹中,Up 方法用于创建,Down 方法用于回滚
这时只生成了创建数据库的代码,并没有真正的创建数据库
多次创建的迁移会按时间戳的次序展示在 Migrations 文件夹中,数据库中记录了最后一次迁移的版本
检查数据模型快照
迁移会在 Migrations/SchoolContextModelSnapshot.cs 中创建当前数据库模式的快照,因此 EF Core 不必与数据库进行交互以创建迁移。
要删除最后一次迁移,执行:
dotnet ef migrations remove
将迁移应用于数据库
dotnet ef database update
执行完成会创建数据库结构,但表中的初始数据将在第一次运行时被添加。
上次应用迁移之后创建的一个或多个新迁移,将在当前应用迁移时按顺序全部执行。
第五课:创建复杂的数据模型
通过使用属性自定义数据模型
[DataType(DataType.Date)]
[DisplayFormat(DataFormatString = "{0:yyyy-MM-dd}", ApplyFormatInEditMode = true)]
DataType 用于指定比数据库固有类型更具体的数据类型,上例指定为日期,而不是日期和时间
DateType 支持:Date, Time, PhoneNumber, Currency, EmailAddress 等等
DateType 支持传达数据的语义到 HTML5
DisplayFormat 用于指定格式
ApplyFormatInEditMode 指定是否当值显示在文本框中进行编辑时也应用此格式
[StringLength(50)]
[StringLength(50, ErrorMessage = "First name cannot be longer than 50 characters.")]
[RegularExpression(@"^[A-Z]+[a-zA-Z''-'\s]*$")]
StringLength 设置数据库中最大长度,并为客户端和服务端提供验证
StringLength 可以指定最小长度,但该值对数据库没有影响
StringLength 不会阻止用户输入空格,可以使用 RegularExpression 来限制
缩短最大长度会导致迁移时发出警告,为了保证顺利迁移,请先更改数据或重新选择合适的最大长度
[Column("FirstName")]
public string FirstMidName { get; set; }
上例将属性 FirstMidName 映射到数据库列名 FirstName
[Required]
必需属性,不能为空的类型将自动处理必填字段,无需重复指定。
[Display(Name = "Last Name")]
显示属性,指定用于显示的名称
计算属性,只有一个 get 访问器,不会在数据库中生成列,例如:
public string FullName
{
get
{
return LastName + ", " + FirstMidName;
}
}
可以将多个属性放在一行中:
[DataType(DataType.Date),Display(Name = "Hire Date"),DisplayFormat(DataFormatString = "{0:yyyy-MM-dd}", ApplyFormatInEditMode = true)]
在一对一的关系中,副表的主键名称不以副表实体 classnameID 命名,而以主表实体 classnameID 命名时,用 Key 属性来指定主键:
[Key]
或者需要以除 classnameID 或 ID 外的其它属性为主键,那么可以用 Key
[Range(0, 5)]
指定数值范围
当有相关实体的导航属性时,EF 不要求在数据模型中添加外键属性。详情见教程
[DatabaseGenerated(DatabaseGeneratedOption.None)]
主键值由用户提供,而不是由数据库生成。
[Column(TypeName = "money")]
public decimal Budget { get; set; }
通常不需要列映射,此例将 decimal 类型的属性 Budget 映射为 money 类型的字段 Budget。
部门可能有负责人也可能没有,负责人必定是教师。因此 InstructorID 属性为教师实体的外键,并且可空,导航属性是被命名为 Administrator 的 Instructor 实体。
public int? InstructorID { get; set; }
public Instructor Administrator { get; set; }
通常,EF 允许对不可空的外键进行级联删除以久多对多关系。
例如,如果将 Department.InstructorID 属性定义为不可空,则 EF 将配置级联删除规则,以在删除部门时删除教师。如果我们想保留教师,但业务逻辑要求该 InstructorID 属性确实不可为空,则必须使用以下语句来禁用关系中的级联删除:
modelBuilder.Entity<Department>()
.HasOne(d => d.Administrator)
.WithMany()
.OnDelete(DeleteBehavior.Restrict)
补充,现在设计业务逻辑时会尽量避免删除操作,而使用禁用来代替,以在数据库中永久保留记录。
[DisplayFormat(NullDisplayText = "No grade")]
NullDisplayText 指定当值为 null 时的显示内容
联合主键
在多对多关系(教师到课程)的连接表(CourseAssignment)中,两个外键都不可为空,并且一同唯一标识表的每一行,因此不需要单独的主键,将 InstructorID 和 CourseID 作为一个联合主键。
定义联合主键不能通过使用属性来完成,唯一方法是使用 Fluent API,修改 Data/SchoolContext.cs 的 OnModelCreating 方法:
modelBuilder.Entity<CourseAssignment>().ToTable("CourseAssignment");
modelBuilder.Entity<CourseAssignment>()
.HasKey(c => new { c.CourseID, c.InstructorID });
Fluent API 替代属性
protected override void OnModelCreating(ModelBuilder modelBuilder){
modelBuilder.Entity<Blog>()
.Property(b => b.Url)
.IsRequired();
}
在本教程中,您只使用 Fluent API 进行数据库映射,而不能使用属性。您还可以使用 Fluent API 来指定可以使用属性执行的大多数格式化、验证和映射规则。一些属性(如 MinimumLength)不能应用 Fluent API,因为它不会更改数据库模式,它只适用于客户端和服务端验证。
一些开发人员喜欢专门使用 Fluent API,以保持实体类“干净”。当然可以混合使用属性和 Fluent API,但推荐只选择一种方式。如果混合使用,那么 Fluent API 会覆盖属性。
添加迁移
dotnet ef migrations add ComplexDataModel
如果在此时尝试运行命令 database update(不要这样做),您将收到以下错误:
ALTER TABLE语句与FOREIGN KEY约束“FK_dbo.Course_dbo.Department_DepartmentID”冲突。冲突发生在数据库“ContosoUniversity”,表“dbo.Department”,“DepartmentID”列。
这是因为在课程 Course 表增加了一个 DepartmentID 外键,而且是不可空的,又由于课程表中已经存在数据,所以无法 AddColumn。
我们可以创建一个临时部门,然后将它的 DepartmentID 值赋给 Course.DepartmentID 作为默认值。
打开 _ComplexDataModel.cs 文件,注释将 DepartmentID 添加到课程表的代码行,注释之,并在创建 Department 表的代码之后添加创建默认部门和添加 DepartmentID 的代码,详见教程。
第六课:读取相关数据
预先加载、显式加载和延时加载
预先加载:当实体被读取时,相关数据随之被检索。即在单个连接查询中读取所有所需数据。通过 Include 和 ThenInclude 方法实现。
显式加载:当实体首次读取时,不检索相关数据。如果需要再检索相关数据。显式加载会导致多次数据库查询。
延时加载:当实体首次读取时,不检索相关数据。在首次尝试访问导航属性时,将自动检索该导航属性所需的数据。
* 从定义上看“显示加载”和“延时加载”比较类似,详细区别会在学完本课后有具体的了解。
创建一个显示部门名称的课程页面
在课程列表中,每个课程都显示部门名称,因此使用预先加载。
在控制器中使用 Include 和 AsNoTracking
在视图中使用 @Html.DisplayFor(modelItem => item.Department.Name)
创建一个显示课程和报名的教师页面
为教师页面创建一个视图模型 InstructorIndexData.cs
在控制器中多次使用 Include 和 ThenInclude 来预先加载相关数据
第一个 Include 加载办公室(一对零或一)
第二个 Include 加载课程(多对多)和报名课程的学生(多对多)
第三个 Include 加载课程(多对多)和课程所在部门(多对一)
本例中“课程的学生”和“课程的部门”是各自级联 ThenInclude 加载的,不能合并到同一个 Include 的课程中,因为 ThenInclude 不能读取多个分支的导航属性,每个分支都必须使用 Include 从教师实体调用。
在视图中给选择的教师添加 class 样式类
显式加载
去掉 Include 和 AsNoTracking,然后在需要的时候附加相关的实体(上下文.Entry())来实现显式加载,以减少首次读取实体时的数据量。
await 上下文.Entry(单个实体).Collection(x=>x.导航属性).LoadAsync(); // 一对多
await 上下文.Entry(单个实体).Reference(x=>x.导航属性).LoadAsync(); // 一对一
第七课:更新相关数据
课程必须与部门关联,在新建和编辑课程时提供部门的下拉列表。
ViewBag.DepartmentID = new SelectList(departmentsQuery.AsNoTracking(), "DepartmentID", "Name", selectedDepartment);
<select asp-for="DepartmentID" class="form-control" asp-items="ViewBag.DepartmentID">
<option value="">-- Select Department --</option>
</select>
SelectList 为下拉列表创建一个集合,允许指定默认选中项,并传递到 ViewBag。
将 .AsNoTracking() 添加到详细信息和删除方法
“一对零或一”关系:(本例是教师和办公室分配,抑或是文章和正文、学生和档案)
删除教师的办公室分配时,如果原先具有值,则删除办公室分配;
输入教师的办公室分配时,如果原先为空,则创建一个办公室分配;
更改教师的办公室分配时,更改现在有办公室分配值。
如何处理:
在 HttpGet 的 Edit 方法中,Include 办公室实体,并 AsNoTracking
在 HttpPost 的 Edit 方法中,移除除 id 以外的参数(为了不与 HttpGet 的 Edit 冲突,改名为 EditPost,并 ActionName("Edit"))
if (await TryUpdateModelAsync<Instructor>(
instructorToUpdate,
"",
i => i.FirstMidName, i => i.LastName, i => i.HireDate, i => i.OfficeAssignment))
使用模型绑定器的值更新检索到的教师实体。TryUpdateModel 支持更新实体的导航属性。
我们可以直接设置实体的导航属性为 null,以便相关的行将被删除。
第八课:处理并发冲突
悲观并发,即锁定,性能差。
乐观并发:
[Timestamp]
public byte[] RowVersion { get; set; }
添加一个 timestamp 类型的 RowVersion 字段
详见教程
第九课:继承
第十课:进阶专题
调用“返回实体”的查询
为了防止 SQL 注入,使用参数化查询:
public async Task<IActionResult> Details(int? id)
{
if (id == null)
{
return NotFound();
}
string query = "SELECT * FROM Department WHERE DepartmentID = {0}";
var department = await _context.Departments
.FromSql(query, id)
.Include(d => d.Administrator)
.AsNoTracking()
.SingleOrDefaultAsync();
if (department == null)
{
return NotFound();
}
return View(department);
}
调用“返回其他类型”的查询
编写 SQL,而非 LINQ:
public async Task<ActionResult> About()
{
List<EnrollmentDateGroup> groups = new List<EnrollmentDateGroup>();
var conn = _context.Database.GetDbConnection();
try
{
await conn.OpenAsync();
using (var command = conn.CreateCommand())
{
string query = "SELECT EnrollmentDate, COUNT(*) AS StudentCount "
+ "FROM Person "
+ "WHERE Discriminator = 'Student' "
+ "GROUP BY EnrollmentDate";
command.CommandText = query;
DbDataReader reader = await command.ExecuteReaderAsync();
if (reader.HasRows)
{
while (await reader.ReadAsync())
{
var row = new EnrollmentDateGroup { EnrollmentDate = reader.GetDateTime(0), StudentCount = reader.GetInt32(1) };
groups.Add(row);
}
}
reader.Dispose();
}
}
finally
{
conn.Close();
}
return View(groups);
}
调用“更新”查询(批量更新)
使用参数化查询批量更新数据,返回受影响行数:
ViewData["RowsAffected"] =
await _context.Database.ExecuteSqlCommandAsync(
"UPDATE Course SET Credits = Credits * {0}",
parameters: multiplier);
检查发送到数据库的 SQL
有时,可以看到发送到数据库的实际 SQL 查询很有帮助。
第一步:设置一个断点
第二步:调试模式运行程序
第三步:在“输出”窗口可以看到 SQL 查询语句
自动变化检测
EF 通过将实体的当前值与原始值进行比较来确定实体如何更改。
一些方法会导致自动更改检测:
DbContext.SaveChages
DbContext.Entry
ChangeTracker.Entries
如果您正在跟踪大量实体,并且您在循环中多次调用上述方法之一,则可以通过临时关闭 AutoDetectChangesEnabled 属性,能显著提升性能。
_context.ChangeTracker.AutoDetectChangesEnabled = false;
从现有数据库反向工程
常见错误
问:ContosoUniversity.dll 由另一个进程使用
答:在 IIS Express 中停止站点。
问:迁移在 Up 和 Down 方法中没有代码
原因:在运行migrations add
命令时有未保存的更改
答:运行migrations remove
命令,保存代码更改并重新运行migrations add
命令。

本文不定时更新!
A: MySQL 执行 SHOW FULL PROCESSLIST
Q: 查看连接数和慢查询,适用于 MySQL 数据库无法连接 1040
A: iftop -i eth0
Q: 查看占用带宽的IP(命令:iftop -i eth0 -F ip/24
),添加到安全组、防火墙、宝塔的黑名单中。
命令 grep -l "x.x.x.x" /www/wwwlogs/*.log
可以在 wwwlogs 目录下的所有 .log 文件中查找指定的恶意 IP。
A: goaccess -f xxx.log
Q: 实时分析网站日志,查看请求最多的IP
A: net.xoyozo.weblog 日志分析工具
Q: 自制的 Web 日志分析工具,可按多种方式排序,纠出可疑访问
A: 重启 web 服务器
Q: 有时候能解决 CPU 和内存消耗的问题,如果一会儿又升高,则需要找另外的原因
Q: 500 服务器内部错误
502 Bad Gateway
504 Gateway Time-out
A: 查看 php 日志,可能的路径:
/usr/local/php/var/log/php-fpm.log
/www/server/php/[版本]/var/log/php-fpm.log
Q: RDS MySQL IOPS 使用率高的原因和处理
A: 根据时间点查看慢查询
Q: Discuz! 论坛界面错乱、表情不显示、模块缺失、登录失败、发帖失败等等
A: 进入管理中心 - 工具 - 更新缓存,能解决大部分问题
Q: Discuz! 浏览帖子提示“没有找到帖子”
A: 进入数据库,修复表 pre_forum_post 或分表
Q: CPU 100% 或内存 100%,负载100+
A: 原因有很多,以下是一些建议:
Windows 在任务管理器中查看进程
当前是否有正常的大流量访问(譬如民生类论坛的某个帖子突然火了)特别是重启无效的情况
对比网站日志大小可大致确定哪个网站被大量恶意请求。
观察:命令 top
排查:通过关闭网站来确定是某网站的问题,通过关闭功能确定是某功能的问题,如果 nginx 崩溃请参下条
案例:通过修改 mobcent 文件夹名确定是安米的文件被疯狂请求导致的,更新插件和 mobcent 包解决问题。
如果都是正常访问,top 看到很多 php-fpm,而且个个占用 CPU 还不小,那么根据服务器硬件配置来修改 php 的并发量,如宝塔面板在 php 设置 - 性能调整 页,300 并发方案的推荐配置是:
max_children:300
start_servers:30
min_spare_servers:30
max_spare_servers:180
另外,memcached 或 redis 的配置也可以进行相应的修改。
另一个案例是 kswapd0 进程占满 CPU,原因是内存不足导致 swap 分区与内存频繁交换数据。同样调整 php 的设置即可。
也可以通过 iftop 来查询占用带宽较多的 IP 并封禁(出方向),如果 CPU 能降下来,那这个 IP 就是罪魁祸首。
Q: 阿里云 ECS 的 CPU 突然达到 100%,并持续到次日 0:00 左右
A: 可能 ECS 是 t5 规格,受 CPU 积分制度限制,积分耗尽时 CPU 不工作。解决方法是更换其它规格产品或升配。
Q: ASP.NET 所在服务器 CPU 突然达到 50% 或 100%,并持续
A: 首先确定哪个网站,再依次排查网站各功能。可能是 HttpWebRequest 请求远程数据时长时间未返回结果导致的程序阻塞。
Q: nginx 服务停止
A: 查看 nginx 日志
WDCP 路径:/www/wdlinux/nginx-1.0.15/logs/error.log
Q: 公网出带宽 100%,其它指标正常
A: Windows 在任务管理器-性能-资源监视器-网络 查看占用带宽的进程PID,然后在任务管理器-详细信息中的找到对应的用户(如果为每个网站分别创建了用户,就能知道是哪个网站占用了带宽);如果是被 PID 为 4 的 System 占用大部分带宽,也可以尝试重启 IIS 来解决。
CentOS 使用 nethogs 查看占用带宽的进程PID和USER,如果为每个网站分别创建了用户,就能知道是哪个网站占用了带宽,否则只能一个个关闭网站来判断,不知道大家有没有好的方法?当然还可以直接用 iftop 命令查看占用带宽的 IP。另外,查看每个网站在那个时间段的日志文件的大小也能大概看出是哪个网站被采集了。
A: Linux 显示每个用户会话的登入和登出信息
utmpdump /var/log/wtmp
参考:http://www.tulaoshi.com/n/20160331/2050641.html
Q: RDS 的 CPU 100%
A: 如果是突然持续占满(同时伴随 ECS 资源使用率下降,页面出现 502),很大可能是受攻击(或社交网站推送突发事件等),查看“慢查询”,添加相关索引;如果是 Discuz! 论坛,可尝试修复优化表 pre_common_session。
如果是数日缓步上升,或新项目上线,考虑 SQL 慢查询,思路:MySQL / SQL Server。
MySQL:SHOW FULL PROCESSLIST
SQL Server:sp_who
Q: php 网站的服务器,内存在数天内缓慢上升
A: 大概是 php-fpm 占用过多,或进程数太多
更改 php 的配置(如 max_spare_servers),执行:service php-fpm reload
Q: 进程 cloudfs 占用内存过多
A: 参:https://xoyozo.net/Blog/Details/cloudfs-cache
Q: RDS 磁盘占用过大
A: 参:https://xoyozo.net/Blog/Details/how-to-use-rds
Q: ECS 受到 DDoS 攻击怎么办?
A: 参:https://xoyozo.net/Blog/Details/aliyun-ddos-without-bgp
Q: 如果 ECS 和 RDS 各项指标都没有异常,但网页打开慢或打不开502,TTFB 时间很长,是什么原因?(ECS 的 CPU 100%,RDS 的连接数上升,也可参考此条)
A: 数据库有坏表,尝试优化/修复表(慢 SQL 日志中锁等待时间较长的表?),或主备切换。show full processlist 时看到许多
DELETE FROM pre_common_session WHERE sid='******' OR lastactivity<****** OR (uid='0' AND ip1='*' AND ip2='*' AND ip3='*' AND ip4='*' AND lastactivity>******)
Q: Discuz! 创始人(站长)密码被改
A: 数据库找到 pre_ucenter_members 表,复制其它的已知登录密码的账号,复制其 password 和 salt 两个字段的值到创始人账号中,创始人账号即可用该密码登录了。
Q: 通过 iftop 观察到,Discuz! 网站从 RDS 数据库到 ECS 网站服务器私网流量非常大,远大于公网流量
A: 可能是缓存出问题了,尝试卸载重装 Redis 来解决。
Q: 宝塔面板中安装的 Redis 经常自动停止
A: 尝试卸载重装 Redis 来解决。
Q: 马甲客户端出现“您的网络有些问题”
A: 原因有许多,其中一个就是新建了一个数据表,然后 /source/class/table/ 下面丢失了对应的文件,具体可以找官方排查原因。
Q: 排查服务器安全需要检查哪些日志?
A: Web日志、登录日志(/var/log/secure)等。
Q: 带宽波形以几分钟为周期呈锯齿状波动是什么原因?
A: 该现象主要由防火墙流量管控机制与检测周期设置共同作用所致。防火墙基于预设的带宽阈值执行安全防护策略,当检测到流量峰值超过设定阈值时,将自动触发限流策略拒绝后续请求。待流量回落至安全阈值后,系统自动恢复服务访问权限。若防火墙的带宽采样检测周期设置过长(如以分钟为单位的检测间隔),将导致系统对实时流量变化的响应出现迟滞。这种周期性的检测机制会使带宽监控数据在阈值临界点附近呈现规律性的锯齿状波动特征。
优化建议:可通过调整防火墙的流量检测周期至更小时间粒度(如10-15秒),或采用动态流量整形策略,以实现更平滑的带宽控制效果。
补充说明:对于阿里云监控数据的调用,建议注意接口调用频率管控。高频调用云监控API接口将触发阿里云的API计费策略,可能产生额外的资源消耗成本。
其它案例
某台 ECS 上的多个网站出现 502,查询到 CPU / 内存 / 带宽 都比平时高,但都未满。用 iftop 看到连接的一个远程 Redis 占用内网带宽非常高。尝试重启远程 Redis 未果。尝试重启 ECS 上的 nginx 未果。尝试重启 ECS 未果。进入 ECS 的宝塔面板,重启 PHP 有效。

最近要做个简单的类似 CNZZ 和百度统计的统计器,不可避免地遇到 JS 文件异步加载 和 给 JS 文件传参 的问题。
参考了 CNZZ 的代码以后,在 Chrome 的控制台发现以下警告:
A Parser-blocking, cross-origin script, http://s4.cnzz.com/stat.php?***, is invoked via document.write. This may be blocked by the browser if the device has poor network connectivity. See https://www.chromestatus.com/feature/5718547946799104 for more details.
Paul Kinlan 给出了解释,是因为使用了 document.write() 的方式输出了 <script src="***" /> HTML DOM,建议改成 document.appendChild() 或 parentNode.insertBefore(),最好的例子就是 Google Analytics。
<!-- Google Analytics -->
<script>(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
})(window,document,'script','https://www.google-analytics.com/analytics.js','ga');
ga('create', 'UA-XXXXX-Y', 'auto');
ga('send', 'pageview');
</script>
<!-- End Google Analytics -->
上述 JavaScript 跟踪代码段可以确保该脚本在所有浏览器中加载和异步执行。
加了一些注释,便于理解,官方英文版。
(function (i, s, o, g, r, a, m) {
i['GoogleAnalyticsObject'] = r;
// console.log(window['GoogleAnalyticsObject']) // 'ga'
// console.log(i[r]) // undefined
i[r] = i[r] || function () { // i[r] 就是 window['ga'],定义了一个函数
(i[r].q = i[r].q || []).push(arguments) // 往 ga.q 这个数组中增加一项
},
i[r].l = 1 * new Date(); // 时间戳,写法等同于 new Date().getTime()
// console.log(i[r]) // window['ga'] 就是上面那个 function
a = s.createElement(o), // 创建一个 script 元素
m = s.getElementsByTagName(o)[0]; // 文档中的第一个脚本(文档中肯定至少已有一个脚本了)
a.async = 1; // 异步加载
a.defer = 1; // 兼容旧浏览器(我自己加的)
a.src = g;
m.parentNode.insertBefore(a, m) // 将 a 脚本插入到 m 脚本之前
})(window, document, 'script', 'http://***/***.js', 'ga');
// i s o g r
ga('create', 'UA-XXXXX-Y', 'auto');
ga('send', 'pageview');
过程是:
创建了一个 <script> 元素,并异步加载 http://***/***.js;初始化了一个全局函数 ga;在 ga() 命令队列中添加了两条命令。
现在我们可以在这个外部 js 中使用 ga.q 这个对象中的数据了,示例:
;(function () {
console.log(ga.q);
})(window);
简单补充下,async 是 HTML5 属性,使支持异步加载 JS 文件;defer 只支持 IE,作用类似。
测试异步只需要将 js 文件换成服务端页面,并人为设置 sleep 时间即可,阻塞式调用的话会在加载 js 时暂停后续页面的渲染。

收录了一些个人觉得不错的网页开发插件。
由于插件更新频繁,本页如有错误请指正,也欢迎告知更多功能强大、使用方便的插件。
插件 | 简介 | 备注 |
框架 | ||
jQuery | 最流行的 JS 框架 | 下载、中文文档、英文整合文档、中文整合文档,浏览器支持、来自 css88 的文档 官方建议 IE 6-8 使用 1.12.4 |
Angular、中文版 AngularJS (version 1.x) | 一套框架,多种平台同时适用手机与桌面 | MVC 架构,使得开发现代的单一页面应用程序(SPAs:Single Page Applications)变得更加容易 |
Vue.js | 是一套用于构建用户界面的渐进式框架。 | |
Bootstrap、中文版 | 简洁、直观、强悍的前端开发框架 | 英文文档、v3中文文档、v2中文文档、视频教程,主题和模板 |
jQuery UI | 为 jQuery 提供更丰富的功能 | 示例:Datepicker、Color Animation、Shake Effect |
功能 | ||
jQuery File Upload | jQuery 文件上传 | 英文文档 |
jQuery Cookie | 读取、写入和删除 cookie | 浏览器支持,文档 |
json2.js | json 操作库 | 已弃用,旧 IE 用 jQuery 的 parseJSON,HTML 5 用 JSON.parse |
Lightbox | 老牌图片浏览插件 推荐使用更强大的 Viewer.js | |
Swiper、中文版 | 最现代的移动触摸滑块(Most Modern Mobile Touch Slider) | 英文文档,中文文档,旧浏览器支持版本:2.x.x,Swiper 2 英文文档,中文文档 |
jquery-cropper | 图片裁剪 | |
FastClick | 用于消除手机浏览器上触摸事件触发之间的 300 毫秒延迟 | 用法,不应用的场景 |
PACE | 页面加载进度条 | 文档,IE8+ |
toastr | jQuery 通知 | 文档 |
Autosize | 一款小巧的,可自动调整 textarea 高度的独立脚本 | IE9+ |
X-editable | 允许您在页面上创建可编辑元素 | 文档,Demo |
select2 | 一款提供搜索过滤、自定义样式的下拉框插件 | |
jQuery Tags Input | 标签输入框 | 用法 |
Viewer.js | 图片浏览插件 | GitHub(viewerjs)、GitHub(jquery-viewer) jquery-viewer 是 viewerjs 的 jQuery 插件,即在 jQuery 环境中要同时引用这两个脚本。 |
PDF.js | A general-purpose, web standards-based platform for parsing and rendering PDFs. | |
编辑器 | ||
UEditor | 百度在线编辑器 | GitHub 下载、文档、ASP.NET 部署教程 |
日期时间 | ||
bootstrap-datepicker | Bootstrap 日期选择器 | Online Demo |
DateTimePicker | 日期时间选择 | |
MultiDatesPicker | 多日期选择 | |
FullCalendar | 日历日程事件工作表 | IE 9+, jQuery 2.0.0+ |
TimeTo | 计时、倒计时 | |
图表 | ||
D3.js | D3.js 是基于数据驱动文档工作方式的一款 JavaScript 函数库,主要用于网页作图、生成互动图形,是最流行的可视化库之一。 | |
Highcharts、中文版 | 兼容 IE6+、完美支持移动端、图表类型丰富、方便快捷的 HTML5 交互性图表库 | 文档 |
ECharts | 百度图表控件 | |
AntV | 来自蚂蚁金服的专业、简单、无限可能的可视化解决方案 G2 - 专业易用的可视化类库 G2-mobile - 移动端高性能可视化类库 G6 - 关系图可视化类库 | 流程图, 关系图, 可视化规范, 地图, 河流图, 力导图, 网络图, UML图, 业务流程图, 时序图 |
SyntaxHighlighter | 功能齐全的代码语法高亮插件(JS) | |
动态排名数据可视化 | 将历史数据排名转化为动态柱状图图表 开源代码,非插件,修改使用 | GitHub、视频教程、EV录屏、网页示例、视频效果 |
图标 | ||
Font Awesome | 完美的图标字体 | IE 8+,v3.2.1 支持 IE 7,进阶用法(定宽/边框/动画/旋转/叠加) |
Glyphicons | 图标字体 | 作为 Bootstrap 组件 |
Iconfont | 阿里巴巴矢量图标库 | 用户可以自定义下载多种格式的 icon,也可将图标转换为字体,便于前端工程师自由调整与调用 |
UI 框架 | ||
WeUI | 同微信原生视觉体验一致的基础样式库 | Demo、Wiki |
Apple UI Design Resources | 苹果用户界面设计资源 |

程序设计规范:
【推荐】上传的文件直接保存到文件存储服务(如阿里云 OSS),这样即使被上传了后门 shell,对网站服务器也不会有影响。
否则必须通过文件头来确定文件类型,检查文件十六进制的文件头和文件尾是否合法,并检查文件流中是否包含 php、evel 等字符。
不可直接使用客户端文件名来保存文件,特别是后缀名/扩展名。应生成随机文件名,并通过检验文件头来确定文件类型。必须由程序指定保存目录。
使用 OSS 的应直接上传,不要在 ECS 上临时存放或备份。如必须存放的,应按上述规范操作。
服务器安全设置
CentOS + nginx + PHP:
全站文件取消属性中的“执行”权限(chmod),因为这个“执行”与运行 PHP 无关。而需要上传文件的“目录”需要“执行”权限,原因是需要往该目录创建文件。
仅需要写入的目录或文件设置“写入”权限。如上传图片目录、ThinkPHP 的 Runtime 目录。
凡可写目录或文件均不允许运行 PHP / PY 等 除需要被直接访问的 PHP / PY 文件,其它动态文件均不允许被访问到,在 nginx 的配置文件中添加项,参:https://xoyozo.net/Blog/Details/nginx-location-if,若全站使用统一的入口文件访问,那么设置仅该文件允许运行 PHP 即可。通过 IO 方式被其它文件包含的文件,无需运行 PHP 权限。(“deny all”对 include 起作用,但对 IO 不起作用,因此 Runtime 目录可以继续为 ThinkPHP 提供缓存服务。)这一步非常有用。
使用与 nginx 网站用户不同的用户来部署网站文件,如宝塔面板 PHP 使用 www 用户,那么就使用 root 或其它新用户来上传文件,否则将导致全站目录和文件可写。有条件的建议不同网站使用不同的用户,可防止一个网站被入侵后导致其它网站文件或磁盘上的其它文件被泄露的风险(2022年10月2日从宝塔官方社区获悉,宝塔面板暂不支持使用非 www 用户创建并运行网站)。
Windows Server + IIS + ASP.NET:
配置每个磁盘的安全属性,拒绝“IIS_IUSRS”这个用户组的所有权限。只要设置驱动器即可,子文件夹和文件会自动继承。若运行 .NET Framework 项目,需要设置 C:\Windows\Microsoft.NET\Framework\v*.*.*****\Temporary ASP.NET Files\ 目录可修改写入权限,.NET Core 项目不需要此设置。
为每个网站创建一个新用户,仅隶属于“IIS_IUSRS”。网站根目录安全属性添加该用户,权限选择“读取”。(已测取消“读取与执行”不影响 PHP,“列出文件夹内容”视业务需求开启,建议关闭)。仅需要上传文件的目录或文件设置“修改”、“写入”权限。(修改对应修改文件,写入对应上传文件)
IIS 网站中设置“物理路径凭据”以及应用程序池的“标识”。
IIS 中设置写入目录的“处理程序映射”无脚本。

各位站长好:
根据苹果相关通知,从2017年1月1日起,所有上架AppStore的应用必须支持https协议。
仍然采用HTTP传输的站点APP,将无法在AppStore被用户下载使用,也无法进行升级更新等工作。
官方视频
https://developer.apple.com/videos/play/wwdc2016/706/
相关新闻
http://www.chinaz.com/news/2016/1028/602635.shtml
根据AppStore审核要求,站长需要在2017年1月1日之前,在APP服务器全面部署https。
尽管不升级支持https后果严重,但诸位也不必太过担心,马甲根据要求为各位提供一份服务器升级https的技术文档,供您参考。
https的优点:
1. 相对于http,https可以确保数据在网络传输过程中不被篡改(如禁止运营商插入广告),同时可以提升网站的安全性。
2. iOS可以实现微信内部浏览器直接启动并打开App对应页面。
APP网站升级https步骤:
1. 为APP域名申请购买SSL证书(可选免费型);
2. 将SSL证书部署到APP站点。
https证书申请方法:
1. 进入阿里云(独立服务器客户可注册一个阿里云账号,无须购买阿里云主机)https证书申请页面 https://www.aliyun.com/product/security/markets/aliyun/product/cas
2. 购买https证书,选择免费类型,点击购买(无需实际付款)
3. 购买完成后,进入控制台-CA证书服务页面,找到证书订单,点击“补全”
4. 填写证书对应的域名信息,免费证书只能用于一个域名(请填写完整域名例如:test.magapp.cc 而不是 magapp.cc)
5. 继续补全证书信息,注意域名验证类型选择DNS,邮箱填写自己常用邮箱即可,稍后系统会往邮箱发送域名解析信息。
6. 下一步生成证书请求文件(CSR),这里建议选择“系统生成CSR”,点击右侧创建,创建完成后,点击提交审核,开始申请https证书。
7. 稍后系统会向邮箱发送一份该邮件,邮件包含需要在域名管理后台解析的信息。
8. 到域名管理后台添加新的域名解析,注意解析类型为CNAME
9. 添加新域名解析完成后,系统会对您的申请进行审核(审核时间在几分钟到几个小时不确定),如果审核成功系统会为你签发https证书,失败也会有相关原因说明,如果失败可根据提示重新申请。
10. https证书申请成功后,下一步就可以下载并部署到APP服务器。
11. 点击下载后,可以根据阿里云提供的部署文档,进行相关的服务器部署工作。
12. 部署完成后,验证是否部署成功
第一步:使用https协议访问域名是否能正常访问,例如 https://test.magapp.cc
第二步:在线检测域名是否满足苹果审核要求,网址为:https://www.qcloud.com/product/ssl.html#userDefined10
填写自己申请https的域名
如果域名检测各项满足会显示如下图所示
部署注意事项:
1. https使用的是443端口而不是默认的80端口,需要在wdcp安全管理-iptables开启443端口。
2. https免费证书有效期为一年,请各位站长作好记录,提前重新申请。
3. 站点从http转到https完全过渡需要一段时间,建议在部署时站点同时支持http和https。
4. 部署过程遇到任何问题,可联系您的专属运维人员。
MAGAPP技术部