前言:
本文适合 6.x 环境,8.x 请结合参考:如何升级 ASP.NET 项目 MySql.Data 和 Connector/NET 至 8.0.x
使用 ASP.NET Core 的同学请移步:ASP.NET Core 使用 DBFirst 模式连接 MySQL 数据库
安装
开发环境
软件安装:
MySQL for Visual Studio - 用于在 Visual Studio 中连接 MySQL 数据库
Connector/NET - ADO.NET 托管提供程序
Nuget 中搜索安装 MySql.Data 和 MySql.Data.Entity
版本选择:
MySQL for Visual Studio 最新版
Connector/NET 版本必须与 MySql.Data 和 MySql.Data.Entity 版本相同,否则会出现 MySql.Data.MySqlClient.MySqlConnection 无法强制转换的错误
生产环境
软件安装:
MySQL Server - 数据库
Connector/NET - ADO.NET 托管提供程序
版本选择:
MySQL Server 最新版
Connector/NET 应与项目中使用的 MySql.Data 和 MySql.Data.Entity 版本一致。(某些项目在确保 Web.config 的 MySql.Data 版本号与 MySql.Data 和 MySql.Data.Entity 版本一致的前提下,允许服务端的 Connector/NET 小版本略低于 MySql.Data 和 MySql.Data.Entity 版本,某些项目允许安装比 MySql.Data 和 MySql.Data.Entity 版本略高的 Connector/NET 版本,为确保不出问题,严格要求版本全部一致)
升级
MySQL for Visual Studio:直接更新至最新版
MySQL Server:直接更新至最新版
MySql.Data 和 MySql.Data.Entity:升级至两者共有的新版本,更改 Web.config 中 MySql.Data 的版本号,并更新开发环境和生产环境的 Connector/NET 至相同版本(详细步骤见下方说明)
Connector/NET:视是否有相同版本的 MySql.Data 和 MySql.Data.Entity,若有,同时升级。(详细步骤见下方说明)
关于 Connector/NET 和 MySql.Data 及 MySql.Data.Entity 版本一致的说明:
某些项目在确保 Web.config 的 MySql.Data 版本号与 MySql.Data 和 MySql.Data.Entity 版本一致的前提下,允许服务端的 Connector/NET 小版本略低于 MySql.Data 和 MySql.Data.Entity 版本;
某些项目允许安装比 MySql.Data 和 MySql.Data.Entity 版本略高的 Connector/NET 版本;
为确保不出问题,严格要求版本全部一致。
但服务器上同时运行多个项目,一旦出现问题涉及面就很广,为了尽量减少影响,列出下面的升级步骤:
升级开发环境的 Connector/NET
升级各项目的 MySql.Data 和 MySql.Data.Entity
更改 Web.config 中关于 MySql.Data 的版本号(若未自动更新,项目中搜索原版本号即可)
测试运行各项目
同时发布各项目,同时升级服务器上的 Connector/NET(升级 Connector/NET 仅需几十秒)
部分项目需要删除并重新上传 bin 目录才能生效
尽管如此,发布成功以后还是会有各种各样的问题导致无法正常打开,须要有针对性地去解决,所以升级还是要趁夜深人静的时候进行
附加消息:暂无该开发者或未上传过应用
原因: 没有上架应用(隐藏的不算上架),应用设置显示后记得在版本管理中将版本也设为显示。
附加消息:抱歉,开发者验证无效
原因:可能是没有达到认证要求,不允许加群(譬如:需应用最低总安装量 1000 次)
DZ 水很深,慎入:http://open.discuz.net/?ac=document&page=xootan
2018.8.14 踩坑:
还有,加群要开发者中填写的 QQ 才能加,因为验证是全自动程序判断的。
如果要跳过安装量 1000 次的要求,就直接花钱购买会员,不过购买会员也是一件非常艰难的事情,在与“Q群管家”这个机器人 QQ 的对话中发送“@购买vip”,绝对能让你体验到一种有钱也花不出去的感受。
做这么多花哨的东西,不如踏踏实实把产品做好。
2018.8.15 踩坑:
跟Q群管家对话,@login 时,由于密码过于复杂,消息没反应,特殊符号别提了,连纯大小写字母数字也不行,后改成纯数字密码登录成功。
加群完全看基本信息中填写的 QQ 客服 号,群主是机器人,只要应用是上架显示的,版本是显示的,就可以用这个QQ客服申请入群,(可以改)。
制作类似下图中的拖拽排序功能:

1. 首先数据库该表中添加字段 sort,类型为 double(MySQL 中为 double(0, 0))。
2. 页面输出绑定数据(以 ASP.NET MVC 控制器为例):
public ActionResult EditSort()
{
if (!zConsole.Logined) { return RedirectToAction("", "SignIn", new { redirect = Request.Url.OriginalString }); }
db_auto2018Entities db = new db_auto2018Entities();
return View(db.dt_dealer.Where(c => c.enabled).OrderBy(c => c.sort));
}这里可以加条件列出,即示例中 enabled == true 的数据。
3. 前台页面引用 jQuery 和 jQuery UI。
4. 使用 <ul /> 列出数据:
<ul id="sortable" class="list-group gutter list-group-lg list-group-sp">
@foreach (var d in Model)
{
<li class="list-group-item" draggable="true" data-id="@d.id">
<span class="pull-left"><i class="fa fa-sort text-muted fa m-r-sm"></i> </span>
<div class="clear">
【id=@d.id】@d.name_full
</div>
</li>
}
</ul>5. 初始化 sortable,当拖拽结束时保存次序:
<script>
var url_SaveSort = '@Url.Action("SaveSort")';
</script>
<script>
$("#sortable").sortable({
stop: function (event, ui) {
// console.log('index: ' + $(ui.item).index())
// console.log('id: ' + $(ui.item).data('id'))
// console.log('prev_id: ' + $(ui.item).prev().data('id'))
$.post(url_SaveSort, {
id: $(ui.item).data('id'),
prev_id: $(ui.item).prev().data('id')
}, function (json) {
if (json.result.success) {
// window.location.reload();
} else {
toastr["error"](json.result.info);
}
}, 'json');
}
});
$("#sortable").disableSelection();
</script>这里回传到服务端的参数为:当前项的 id 值、拖拽后其前面一项的 prev_id 值(若移至首项则 prev_id 为 undefined)。
不使用 $(ui.item).index() 是因为,在有筛选条件的结果集中排序时,使用该索引值配合 LINQ 的 .Skip 会引起取值错误。
6. 控制器接收并保存至数据库:
[HttpPost]
public ActionResult SaveSort(int id, int? prev_id)
{
if (!zConsole.Logined)
{
return Json(new { result = new { success = false, msg = "请登录后重试!" } }, JsonRequestBehavior.AllowGet);
}
db_auto2018Entities db = new db_auto2018Entities();
dt_dealer d = db.dt_dealer.Find(id);
// 拖拽后其前项 sort 值(若无则 null)(此处不需要加 enabled 等筛选条件)
double? prev_sort = prev_id.HasValue
? db.dt_dealer.Where(c => c.id == prev_id).Select(c => c.sort).Single()
: null as double?;
// 拖拽后其后项 sort 值(若无前项则取首项作为后项)(必须强制转化为 double?,否则无后项时会返回 0,导致逻辑错误)
double? next_sort = prev_id.HasValue
? db.dt_dealer.Where(c => c.sort > prev_sort && c.id != id).OrderBy(c => c.sort).Select(c => (double?)c.sort).FirstOrDefault()
: db.dt_dealer.Where(c => c.id != id).OrderBy(c => c.sort).Select(c => (double?)c.sort).FirstOrDefault();
if (prev_sort.HasValue && next_sort.HasValue)
{
d.sort = (prev_sort.Value + next_sort.Value) / 2;
}
if (prev_sort == null && next_sort.HasValue)
{
d.sort = next_sort.Value - 1;
}
if (prev_sort.HasValue && next_sort == null)
{
d.sort = prev_sort.Value + 1;
}
db.SaveChanges();
return Json(new { item = new { id = d.id }, result = new { success = true } });
}需要注意的是,当往数据库添加新项时,必须将 sort 值设置为已存在的最大 sort 值 +1 或最小 sort 值 -1。
var d = new dt_dealer
{
name_full = "新建项",
sort = (db.dt_dealer.Max(c => (double?)c.sort) ?? 0) + 1,
};Controller:
public ActionResult EvalAge(byte age)
{
return Content($"{age} 岁");
}View:
@Html.Action("EvalAge", new { age = item.age })ASP.NET MVC 如果从控制器输出的是匿名类的集合,那么绑定到视图时会报错:
“object”未包含“xxx”的定义
简单的解决方法是 JSON 序列化再反序列化,例如:
ViewBag.list = JsonConvert.DeserializeObject(JsonConvert.SerializeObject(q.ToList()));上面是 ViewBag 的情形,如果是直接 return View(匿名类集合);
那么在视图页面:
@model IEnumerable<dynamic>WGS-84(地球坐标)
用于 GPS 模块 及 Google Earth
CGCS 2000(国家大地坐标系)
用于天地图,与 WGS-84 最大差异 0.11mm
GCJ-02(火星坐标、国测局坐标系)
用于 Google Map、必应-中国地图、高德、腾讯、图灵、阿里地图,是对地理位置的一次加密
BD-09(百度坐标)
仅用于百度地图,是对火星坐标的再次加密
在线转换工具:https://xoyozo.net/Tools/GeoConverter
坐标转换:https://github.com/wandergis/coordtransform
微信网页开发的获取地理位置接口(wx.getLocation)默认使用 wgs84,可选 gcj02。
微信网页开发的使用微信内置地图查看位置接口(wx.openLocation)支持 gcj02。
“/”应用程序中的服务器错误。
无法为具有固定名称“MySql.Data.MySqlClient”的 ADO.NET 提供程序加载在应用程序配置文件中注册的实体框架提供程序类型“MySql.Data.MySqlClient.MySqlProviderServices, MySql.Data.Entity.EF6, Version=6.10.7.0, Culture=neutral, PublicKeyToken=c5687fc88969c44d”。请确保使用限定程序集的名称且该程序集对运行的应用程序可用。有关详细信息,请参阅 http://go.microsoft.com/fwlink/?LinkId=260882。
打开 nuget,卸载并重新安装 MySql.Data 和 MySql.Data.Entity
如果没有效果,打开 web.config,检查是否多次定义了 MySql.Data.MySqlClient
如果 .NET Core 项目发布到服务器上报以下错误:
HTTP Error 502.5 - Process Failure
Common causes of this issue:
The application process failed to start
The application process started but then stopped
The application process started but failed to listen on the configured port
Troubleshooting steps:
Check the system event log for error messages
Enable logging the application process' stdout messages
Attach a debugger to the application process and inspect
For more information visit: https://go.microsoft.com/fwlink/?LinkID=808681
在服务器上安装最新的 .NET Core Runtime 即可(Windows Server 选择 Hosting Bundle Installer)。

ASP.NET MVC 项目发布到 IIS 上,出现服务器错误“403 - 禁止访问:访问被拒绝”,排除权限设置问题后,原因在于 MVC 的 URL 通常没有扩展名,IIS 并未将其交由 ASP.NET 托管处理。
经过各种找资料,各种尝试后,最终确定:项目本身没有问题,本地运行正常,发布到另一台服务器上正常。
删除服务器 IIS 上的该网站和应用程序池,重新创建网站,恢复正常。原因未知。
在 Global.asax 的 Application_Start() 方法中添加以下代码,作用是在应用程序启动时预先访问一遍所有动态页面,办法虽土,但很有效。
本代码适合 Web Forms 项目,不适合 MVC 项目;修改代码中的 domain 值为真实的网站域名
代码整理中……
在 IIS 中设置应用程序池最长时效或永不过期,则效果更佳。
在应用程序池中选中网站对应的应用程序池,在右侧“操作”窗口中选择“正在回收...”
取消“固定间隔”框中的所有选项,确定。

实践证明,近 200 个动态页面一次性访问需要耗时近 10 分钟,发布后 10 分钟内无法正常浏览网站是同样是无法忍受的。因此更改方案为:
做一个 Winform 应用程序来定时访问这些页面,30 秒一个,一个半小时能完成一个循环,对正常浏览的影响非常小。