“DatabaseFacade”未包含“ExecuteSqlCommand”的定义,并且找不到可接受第一个“DatabaseFacade”类型参数的可访问扩展方法“ExecuteSqlCommand”(是否缺少 using 指令或程序集引用?)
思路:(以 Entity Framework Core 5.0 为例)
先借用 SELETE 的 IQueryable 语句,将 SELETE 关键字替换成 DELETE,再使用 FromSqlRaw 执行。这样可准确使用真实的数据库表名和字段名。
如:
using Microsoft.EntityFrameworkCore;
string sql = db.表.Where(c => c.Date < minDate).ToQueryString();
sql = Regex.Replace(sql, @"SELECT\s.*?\sFROM", "DELETE FROM", RegexOptions.Singleline); // SET @__minDate_0 = '2020-01-14'; DELETE FROM `表` AS `d` WHERE `d`.`Date` < @__minDate_0
sql = Regex.Replace(sql, @"\sAS\s`(.*?)`", "", RegexOptions.Singleline); // SET @__minDate_0 = '2020-01-14'; DELETE FROM `表` WHERE `d`.`Date` < @__minDate_0
sql = Regex.Replace(sql, @"`([^`]*?)`\.`", "`", RegexOptions.Singleline); // SET @__minDate_0 = '2020-01-14'; DELETE FROM `表` WHERE `Date` < @__minDate_0
db.Database.ExecuteSqlRaw(sql);
以上例子针对 MySQL,且未考虑字符串内容中包含符号 ` 的情况。实例操作中应严格防范 SQL 注入。
封装成方法:
using Microsoft.EntityFrameworkCore;
const RegexOptions ro = RegexOptions.Singleline;
/// <summary>
/// 组装一个 DELETE SQL 语句
/// </summary>
/// <typeparam name="T">数据库表实体类</typeparam>
/// <param name="queryableWithConditions">IQueryable,可带筛选条件</param>
/// <returns></returns>
public static string AssembleDeleteSql<T>(IQueryable<T> queryableWithConditions) where T : class
{
// MySQL 版
string sql = queryableWithConditions.ToQueryString(); // SET @__minDate_0 = '2020-01-14'; SELECT `d`.`Date`, `d`.`Code6` FROM `表` AS `d` WHERE `d`.`Date` < @__minDate_0
sql = Regex.Replace(sql, @"SELECT\s.*?\sFROM", "DELETE FROM", ro); // SET @__minDate_0 = '2020-01-14'; DELETE FROM `表` AS `d` WHERE `d`.`Date` < @__minDate_0
sql = Regex.Replace(sql, @"\sAS\s`(.*?)`", "", ro); // SET @__minDate_0 = '2020-01-14'; DELETE FROM `表` WHERE `d`.`Date` < @__minDate_0
sql = Regex.Replace(sql, @"`([^`]*?)`\.`", "`", ro); // SET @__minDate_0 = '2020-01-14'; DELETE FROM `表` WHERE `Date` < @__minDate_0
return sql;
}
/// <summary>
/// 组装一个 UPDATE SQL 语句
/// </summary>
/// <typeparam name="T">数据库表实体类</typeparam>
/// <param name="queryableWithConditions">IQueryable,可带筛选条件</param>
/// <param name="columnsAndValues">需要更改的字段和值</param>
/// <returns></returns>
public static string AssembleUpdateSql<T>(IQueryable<T> queryableWithConditions, Dictionary<string, object> columnsAndValues) where T : class
{
// MySQL 版
string sql = queryableWithConditions.ToQueryString(); // SET @__minDate_0 = '2020-01-14'; SELECT `d`.`Date`, `d`.`Code6` FROM `表` AS `d` WHERE `d`.`Date` < @__minDate_0
sql = Regex.Replace(sql, @"SELECT\s.*?\sFROM", "UPDATE", ro); // SET @__minDate_0 = '2020-01-14'; UPDATE `表` AS `d` WHERE `d`.`Date` < @__minDate_0
sql = Regex.Replace(sql, @"\sAS\s`(.*?)`", "", ro); // SET @__minDate_0 = '2020-01-14'; UPDATE `表` WHERE `d`.`Date` < @__minDate_0
sql = Regex.Replace(sql, @"`([^`]*?)`\.`", "`", ro); // SET @__minDate_0 = '2020-01-14'; UPDATE `表` WHERE `Date` < @__minDate_0
string sets = string.Join(",", columnsAndValues.Select(c => $"`{c.Key.Replace("`", @"\`")}` = '{c.Value.ToString().Replace("'", @"\'")}'"));
sql = Regex.Replace(sql, @"`\s*WHERE", $"` SET {sets} WHERE", ro); // SET @__minDate_0 = '2020-01-14'; UPDATE `表` SET `field1` = 'value1', `field2` = 'value2' WHERE `Date` < @__minDate_0
return sql;
}
调用示例:
using Microsoft.EntityFrameworkCore;
// 删除记录
DateTime minDate = DateTime.Today.AddYears(-1).AddMonths(-1);
string sql = AssembleDeleteSql(db.表.Where(c => c.Date < minDate));
db.Database.ExecuteSqlRaw(sql);
// 更新记录
var sets = new Dictionary<string, object>();
foreach (var p in db.Model.FindEntityType(typeof(表)).GetProperties())
{
switch (p.Name)
{
case nameof(表.字段一): sets.Add(p.GetColumnBaseName(), 123); break;
case nameof(表.字段二): sets.Add(p.GetColumnBaseName(), 456); break;
}
}
DateTime minDate = DateTime.Today.AddYears(-1).AddMonths(-1);
string sql = AssembleUpdateSql(db.表.Where(c => c.Date < minDate), sets);
db.Database.ExecuteSqlRaw(sql);
场景:
打印机只有 USB 接口,没有网线接口。
方案:
使用一台电脑连接打印机,并共享给局域网其它电脑。
将连接打印机的电脑称为服务机,局域网其它电脑称为客户机。
使用 Windows 7 作服务机比使用 Windows 10 更容易设置成功。
以下过程以使用 Windows 7 作服务机为例,Windows 10 的操作稍有不同。
服务机:
安装打印机驱动,打印测试页成功。
在“设备和打印机”界面选中该打印机,右键属性,切换到“共享”,共享这台打印机。
在“计算机管理”中打开“用户”,点击 Guest 属性,将“帐户已禁用”前的勾去掉。
打开“本地安全策略”(命令:secpol.msc),在 安全设置-本地策略-用户权限分配 中选中“拒绝从网络访问这台计算机”,将“Guest”用户删除。
如果使用打印机的人数超过10人,需要在 安全设置-本地策略-安全选项 中将“交互式登录:之前登录到缓存的次数(域控制器不可用时)”改大。
将“睡眠”关闭
客户机
以 Windows 10 为例,打开“我的电脑”或“计算机”或“此电脑”,点击菜单中的“网络”
双击打开打印机所在的计算机(或直接在地址栏输出入“\\192.168.1.*”即服务机的 IP 地址)
正常情况下,打开后显示打印机图标,右键点击“连接”后安装驱动即可正常使用。
如果提示以下错误 0x80070035
打开注册表(命令:regedit),定位到:
计算机\HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\LanmanWorkstation\Parameters
修改
AllowInsecureGuestAuth
值为 1,若没有找到,创建一个就行了,类型是 DWORD。如果“网络”中不显示该计算机图标,试试直接输入IP地址,仍然打不开的话,我还不知道怎么搞,可能是公用/私有网络的缘故,或者家庭网络/工作网络的区别,请自行百度解决。
扩展设置
设置电脑自动开机
以某 Dell 为例,开机进入 BIOS:
设置电脑自动关机
这里借助一款比较老牌又好用的关机助手
此处设置开机时自动运行关机助手,如果不生效,直接在系统启程程序中添加快捷方式。
另外,需要删除开机密码。
[root ~]# yum update xxx
Loaded plugins: security
Setting up Update Process
http://mirrors.aliyun.com/centos/6/os/x86_64/repodata/repomd.xml: [Errno 14] PYCURL ERROR 22 - "The requested URL returned error: 404 Not Found"
Trying other mirror.
To address this issue please refer to the below wiki article
https://wiki.centos.org/yum-errors
If above article doesn't help to resolve this issue please use https://bugs.centos.org/.
http://mirrors.aliyuncs.com/centos/6/os/x86_64/repodata/repomd.xml: [Errno 12] Timeout on http://mirrors.aliyuncs.com/centos/6/os/x86_64/repodata/repomd.xml: (28, 'connect() timed out!')
Trying other mirror.
http://mirrors.cloud.aliyuncs.com/centos/6/os/x86_64/repodata/repomd.xml: [Errno 14] PYCURL ERROR 22 - "The requested URL returned error: 404 Not Found"
Trying other mirror.
Error: Cannot retrieve repository metadata (repomd.xml) for repository: base. Please verify its path and try again
售后工程师:
您好,CentOS 6 已经停止技术支持通知 新购实例或者旧有的Centos 6 操作系统会导致yum安装失败,相关公告请见下面内容;
这边建议您考虑将数据备份一下,升级至Centos 7 版本。如果一定要使用,需要手动切换源,您可以将由原的yum 源做下备份,对/etc/yum.repos.d 内容进行清空处理,然后参考下面的教程做下源的更换处理。
有其他咨询,您继续反馈
简言之,对照自己的版本号,将文件 /etc/yum.repos.d/CentOS-Base.repo 与 /etc/yum.repos.d/epel.repo 的内容替换成教程中的内容即可。
.menu-nav 包裹
<ul class="menu-nav">
...
</ul>
.menu-section 区段
<li class="menu-section">
<h4 class="menu-text">区段</h4>
<i class="menu-icon ki ki-bold-more-hor icon-md"></i>
</li>
.menu-item 菜单
<li class="menu-item" aria-haspopup="true">
<a href="###" class="menu-link">
<span class="svg-icon menu-icon">
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="24px" height="24px" viewBox="0 0 24 24" version="1.1">
<g stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<rect x="0" y="0" width="24" height="24" />
<path d="M3,16 L5,16 C5.55228475,16 6,15.5522847 6,15 C6,14.4477153 5.55228475,14 5,14 L3,14 L3,12 L5,12 C5.55228475,12 6,11.5522847 6,11 C6,10.4477153 5.55228475,10 5,10 L3,10 L3,8 L5,8 C5.55228475,8 6,7.55228475 6,7 C6,6.44771525 5.55228475,6 5,6 L3,6 L3,4 C3,3.44771525 3.44771525,3 4,3 L10,3 C10.5522847,3 11,3.44771525 11,4 L11,19 C11,19.5522847 10.5522847,20 10,20 L4,20 C3.44771525,20 3,19.5522847 3,19 L3,16 Z" fill="#000000" opacity="0.3" />
<path d="M16,3 L19,3 C20.1045695,3 21,3.8954305 21,5 L21,15.2485298 C21,15.7329761 20.8241635,16.200956 20.5051534,16.565539 L17.8762883,19.5699562 C17.6944473,19.7777745 17.378566,19.7988332 17.1707477,19.6169922 C17.1540423,19.602375 17.1383289,19.5866616 17.1237117,19.5699562 L14.4948466,16.565539 C14.1758365,16.200956 14,15.7329761 14,15.2485298 L14,5 C14,3.8954305 14.8954305,3 16,3 Z" fill="#000000" />
</g>
</svg>
</span>
<span class="menu-text">一级菜单(无子菜单)</span>
</a>
</li>
其中,<svg /> 可以替换成其它图标库,如:
<li class="menu-item" aria-haspopup="true">
<a href="###" class="menu-link">
<span class="svg-icon menu-icon">
<i class="fab fa-app-store-ios"></i>
</span>
<span class="menu-text">一级菜单(无子菜单)</span>
</a>
</li>
并修正样式:
.aside-menu .menu-nav > .menu-item > .menu-heading .menu-icon i,
.aside-menu .menu-nav > .menu-item > .menu-link .menu-icon i { height: 23px; width: 23px; line-height: 23px; text-align: center; vertical-align: middle; font-size: 1.25rem; }
.menu-item-submenu 表示该 .menu-item 有子菜单,即内部包含 .menu-submenu。该规则在任意一级菜单均有效。
.menu-item-open 表示该 .menu-item 当前是打开状态(高亮),否则为闭合状态,需配合 <i class="menu-arrow"></i> 使用。该规则在任意一级菜单均有效。
.menu-item-active 表示该 .menu-item 对应当前页面,效果为灰色背景且文字持续高亮。
.menu-item-parent 作为一级菜单在不同设备上展示的替代,位于一级菜单下的 .menu-submenu 下的 .menu-subnav 内部。
.menu-link 表示可点击(hover 效果),用于 .menu-item 下的 <a /> 或 .menu-item-parent 下的 <span />。
aria-haspopup="true" 每个 .menu-item 都有。
data-menu-toggle="hover" 每个 .menu-item-submenu 都有。
.menu-icon 一级菜单项的左侧图标,支持 <svg /> 或 <i />。
.menu-bullet 非一级菜单项的左侧图标,与 .menu-text 同级,可选 .menu-bullet-line / .menu-bullet-dot。
.menu-text 菜单文字。
.menu-label 菜单项右侧徽章,与 .menu-text 同级。
.menu-arrow 菜单项右侧箭头(当包含子菜单时使用),与 .menu-text 同级。
.menu-toggle 放在 .menu-item-submenu 内部的 .menu-link,与 .menu-item-submenu 上的 data-menu-toggle="hover" 同时出现。
完整示例:
<ul class="menu-nav">
<li class="menu-item" aria-haspopup="true">
<a href="index.html" class="menu-link">
<span class="svg-icon menu-icon">
<!--begin::Svg Icon | path:assets/media/svg/icons/Design/Layers.svg-->
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="24px" height="24px" viewBox="0 0 24 24" version="1.1">
<g stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<polygon points="0 0 24 0 24 24 0 24" />
<path d="M12.9336061,16.072447 L19.36,10.9564761 L19.5181585,10.8312381 C20.1676248,10.3169571 20.2772143,9.3735535 19.7629333,8.72408713 C19.6917232,8.63415859 19.6104327,8.55269514 19.5206557,8.48129411 L12.9336854,3.24257445 C12.3871201,2.80788259 11.6128799,2.80788259 11.0663146,3.24257445 L4.47482784,8.48488609 C3.82645598,9.00054628 3.71887192,9.94418071 4.23453211,10.5925526 C4.30500305,10.6811601 4.38527899,10.7615046 4.47382636,10.8320511 L4.63,10.9564761 L11.0659024,16.0730648 C11.6126744,16.5077525 12.3871218,16.5074963 12.9336061,16.072447 Z" fill="#000000" fill-rule="nonzero" />
<path d="M11.0563554,18.6706981 L5.33593024,14.122919 C4.94553994,13.8125559 4.37746707,13.8774308 4.06710397,14.2678211 C4.06471678,14.2708238 4.06234874,14.2738418 4.06,14.2768747 L4.06,14.2768747 C3.75257288,14.6738539 3.82516916,15.244888 4.22214834,15.5523151 C4.22358765,15.5534297 4.2250303,15.55454 4.22647627,15.555646 L11.0872776,20.8031356 C11.6250734,21.2144692 12.371757,21.2145375 12.909628,20.8033023 L19.7677785,15.559828 C20.1693192,15.2528257 20.2459576,14.6784381 19.9389553,14.2768974 C19.9376429,14.2751809 19.9363245,14.2734691 19.935,14.2717619 L19.935,14.2717619 C19.6266937,13.8743807 19.0546209,13.8021712 18.6572397,14.1104775 C18.654352,14.112718 18.6514778,14.1149757 18.6486172,14.1172508 L12.9235044,18.6705218 C12.377022,19.1051477 11.6029199,19.1052208 11.0563554,18.6706981 Z" fill="#000000" opacity="0.3" />
</g>
</svg>
<!--end::Svg Icon-->
</span>
<span class="menu-text">Dashboard</span>
</a>
</li>
<li class="menu-section">
<h4 class="menu-text">区段 A(一级菜单示例)</h4>
<i class="menu-icon ki ki-bold-more-hor icon-md"></i>
</li>
<li class="menu-item" aria-haspopup="true">
<a href="item1" class="menu-link">
<span class="svg-icon menu-icon">
<i class="fas fa-dice-one"></i>
</span>
<span class="menu-text">菜单 A</span>
</a>
</li>
<li class="menu-item" aria-haspopup="true">
<a href="item2" class="menu-link">
<span class="svg-icon menu-icon">
<i class="fas fa-dice-two"></i>
</span>
<span class="menu-text">菜单 B(带徽章)</span>
<span class="menu-label">
<span class="label label-rounded label-primary">6</span>
</span>
</a>
</li>
<li class="menu-item menu-item-active" aria-haspopup="true">
<a href="item2" class="menu-link">
<span class="svg-icon menu-icon">
<i class="fas fa-dice-three"></i>
</span>
<span class="menu-text">菜单 C(高亮)</span>
</a>
</li>
<li class="menu-section">
<h4 class="menu-text">区段 B(二级菜单示例)</h4>
<i class="menu-icon ki ki-bold-more-hor icon-md"></i>
</li>
<li class="menu-item menu-item-submenu" aria-haspopup="true" data-menu-toggle="hover">
<a href="javascript:;" class="menu-link menu-toggle">
<span class="svg-icon menu-icon">
<i class="fas fa-dice-one"></i>
</span>
<span class="menu-text">一级菜单(默认关闭)</span>
<i class="menu-arrow"></i>
</a>
<div class="menu-submenu">
<i class="menu-arrow"></i>
<ul class="menu-subnav">
<li class="menu-item menu-item-parent" aria-haspopup="true">
<span class="menu-link">
<span class="menu-text">一级菜单(默认关闭)</span>
</span>
</li>
<li class="menu-item" aria-haspopup="true">
<a href="custom/apps/inbox.html" class="menu-link">
<i class="menu-bullet menu-bullet-line"><span></span></i>
<span class="menu-text">二级菜单 a</span>
</a>
</li>
<li class="menu-item" aria-haspopup="true">
<a href="custom/apps/inbox.html" class="menu-link">
<i class="menu-bullet menu-bullet-line"><span></span></i>
<span class="menu-text">二级菜单 b(带徽章)</span>
<span class="menu-label">
<span class="label label-danger label-inline">new</span>
</span>
</a>
</li>
</ul>
</div>
</li>
<li class="menu-item menu-item-submenu menu-item-here menu-item-open" aria-haspopup="true" data-menu-toggle="hover">
<a href="javascript:;" class="menu-link menu-toggle">
<span class="svg-icon menu-icon">
<i class="fas fa-dice-two"></i>
</span>
<span class="menu-text">一级菜单(高亮/默认打开)</span>
<i class="menu-arrow"></i>
</a>
<div class="menu-submenu">
<i class="menu-arrow"></i>
<ul class="menu-subnav">
<li class="menu-item menu-item-parent" aria-haspopup="true">
<span class="menu-link">
<span class="menu-text">一级菜单(高亮/默认打开)</span>
</span>
</li>
<li class="menu-item menu-item-active" aria-haspopup="true">
<a href="custom/apps/inbox.html" class="menu-link">
<i class="menu-bullet menu-bullet-line"><span></span></i>
<span class="menu-text">二级菜单 c(高亮)</span>
</a>
</li>
<li class="menu-item" aria-haspopup="true">
<a href="custom/apps/inbox.html" class="menu-link">
<i class="menu-bullet menu-bullet-line"><span></span></i>
<span class="menu-text">二级菜单 d(带徽章)</span>
<span class="menu-label">
<span class="label label-danger label-inline">new</span>
</span>
</a>
</li>
</ul>
</div>
</li>
<li class="menu-section">
<h4 class="menu-text">区段 C(三级菜单示例)</h4>
<i class="menu-icon ki ki-bold-more-hor icon-md"></i>
</li>
<li class="menu-item menu-item-submenu menu-item-open menu-item-here" aria-haspopup="true" data-menu-toggle="hover">
<a href="javascript:;" class="menu-link menu-toggle">
<span class="svg-icon menu-icon">
<i class="fas fa-dice-one"></i>
</span>
<span class="menu-text">一级菜单</span>
<i class="menu-arrow"></i>
</a>
<div class="menu-submenu">
<i class="menu-arrow"></i>
<ul class="menu-subnav">
<li class="menu-item menu-item-parent" aria-haspopup="true">
<span class="menu-link">
<span class="menu-text">一级菜单</span>
</span>
</li>
<li class="menu-item menu-item-submenu menu-item-open menu-item-here" aria-haspopup="true" data-menu-toggle="hover">
<a href="javascript:;" class="menu-link menu-toggle">
<i class="menu-bullet menu-bullet-line"><span></span></i>
<span class="menu-text">二级菜单(默认打开)</span>
<span class="menu-label">
<span class="label label-rounded label-primary">6</span>
</span>
<i class="menu-arrow"></i>
</a>
<div class="menu-submenu">
<i class="menu-arrow"></i>
<ul class="menu-subnav">
<li class="menu-item" aria-haspopup="true">
<a href="custom/apps/user/list-default.html" class="menu-link">
<i class="menu-bullet menu-bullet-dot"><span></span></i>
<span class="menu-text">三级菜单 1</span>
</a>
</li>
<li class="menu-item menu-item-active" aria-haspopup="true">
<a href="custom/apps/user/list-datatable.html" class="menu-link">
<i class="menu-bullet menu-bullet-dot"><span></span></i>
<span class="menu-text">三级菜单 2(高亮)</span>
</a>
</li>
</ul>
</div>
</li>
<li class="menu-item menu-item-submenu" aria-haspopup="true" data-menu-toggle="hover">
<a href="javascript:;" class="menu-link menu-toggle">
<i class="menu-bullet menu-bullet-line"><span></span></i>
<span class="menu-text">二级菜单(默认关闭)</span>
<i class="menu-arrow"></i>
</a>
<div class="menu-submenu">
<i class="menu-arrow"></i>
<ul class="menu-subnav">
<li class="menu-item menu-item-submenu" aria-haspopup="true" data-menu-toggle="hover">
<a href="javascript:;" class="menu-link menu-toggle">
<i class="menu-bullet menu-bullet-dot"><span></span></i>
<span class="menu-text">二级菜单(默认关闭)</span>
<i class="menu-arrow"></i>
</a>
<div class="menu-submenu">
<i class="menu-arrow"></i>
<ul class="menu-subnav">
<li class="menu-item" aria-haspopup="true">
<a href="custom/apps/profile/profile-1/overview.html" class="menu-link">
<i class="menu-bullet menu-bullet-line"><span></span></i>
<span class="menu-text">三级菜单 3</span>
</a>
</li>
</ul>
</div>
</li>
</ul>
</div>
</li>
</ul>
</div>
</li>
</ul>
效果:
关于手风琴功能:Metronic 菜单已实现手风琴功能,即当展开一个闭合的菜单时,非当前页面所在菜单项的房系菜单会自动闭合。
报错:
HTTP Error 500.30 - ASP.NET Core app failed to start
Common solutions to this issue:
The app failed to start
The app started but then stopped
The app started but threw an exception during startup
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=2028265
解决:
直接运行网站根目录的 .exe 启动文件可以找到答案。
一个常见的原因是:因 nuget 包升级(特别是需要服务器端安装组件的包)导致的发布后仍然是低版本的包,删除项目中的 /obj/ 目录重新发布即可。
通过 nuget 安装 UEditorNetCore
从 UEditor 官网 下载最新的包 ueditorx_x_x_x-utf8-net.zip
解压包,并复制到项目的 wwwroot/lib 目录下,删除 net 目录。
根据 UEditorNetCore 官方的使用说明进行操作
步骤中,控制器 UEditorController 接替原 controller.ashx 的功能,用于统一处理上传逻辑
原 config.json 复制到项目根目录,用于配置上传相关设置(若更改文件名和路径在 services.AddUEditorService() 中处理)。
个人喜欢将 xxxPathFormat 值改为:upload/ueditor/{yyyy}{mm}{dd}/{time}{rand:6},方便日后迁移附件后进行批量替换。
记得配置 catcherLocalDomain 项。
上传相关的身份验证和授权控制请在 UEditorController 中处理,如:
public void Do()
{
if (用户未登录) { throw new System.Exception("请登录后再试"); }
_ue.DoAction(HttpContext);
}
如果图片仅仅上传到网站目录下,那么这样配置就结束了,如果要上传到阿里云 OSS 等第三方图床,那么继续往下看。
因 UEditorNetCore 虽然实现了上传到 FTP 和 OSS 的功能,但未提供配置相关的账号信息的途径,所以只能(1)重写 action,或(2)下载 github 的源代码,将核心项目加入到您的项目中进行修改。
重写 action 不方便后期维护,这里记录修改源码的方式。
将源码改造为可配置账号的上传到 OSS 的功能,具体步骤如下:
将 Consts.cs 从项目中排除。
打开 Handlers/UploadHandler.cs,找到 UploadConfig 类,将
FtpUpload 改为 OssUpload,
FtpAccount 改为 OssAccessKeyId,
FtpPwd 改为 OssAccessKeySecret,
FtpIp 改为 OssAccessEndpoint,
再添加一个属性 OssBucketName。
打开 Handlers/UploadHandler.cs,找到 Process() 方法,
将 UploadConfig.FtpUpload 改为 UploadConfig.OssUpload,
将 Consts.AliyunOssServer.* 改为 UploadConfig.Oss*。
打开 Handlers/CrawlerHandler.cs,找到 Fetch() 方法,
将 Config.GetValue<bool>("catcherFtpUpload") 改为 Config.GetValue<bool>("OssUpload"),
将 Consts.AliyunOssServer.* 改为 Config.GetString("Oss*")。
打开 UEditorActionCollection.cs,找到 UploadImageAction,将
FtpUpload = Config.GetValue<bool>("imageFtpUpload"), FtpAccount = Consts.ImgFtpServer.account, FtpPwd = Consts.ImgFtpServer.pwd, FtpIp = Consts.ImgFtpServer.ip,
替换为
OssUpload = Config.GetValue<bool>("OssUpload"), OssAccessKeyId = Config.GetString("OssAccessKeyId"), OssAccessKeySecret = Config.GetString("OssAccessKeySecret"), OssAccessEndpoint = Config.GetString("OssAccessEndpoint"), OssBucketName = Config.GetString("OssBucketName"),
其余 3 个 Action(UploadScrawlAction、UploadVideoAction、UploadFileAction)按同样的方式修改。
在所有创建 UploadHandler 对象时补充添加 SaveAbsolutePath 属性。
打开 config.json,添加相关配置项(注:配置文件中的 *FtpUpload 全部废弃,统一由 OssUpload 决定)
"OssUpload": true, "OssAccessEndpoint": "", "OssAccessKeyId": "", "OssAccessKeySecret": "", "OssBucketName": "",
将 xxxUrlPrefix 的值改为 OSS 对应的 CDN 网址(以 / 结尾,如://cdn.xoyozo.net/)。
其它注意点:
若使用 UEditorNetCore github 提供的源代码类库代替 nuget,且使用本地存储,那么需要将 Handlers 目录中与定义 savePath 相关的代码(查找 var savePath)替换成被注释的行
纯真 IP 数据库官方下载地址:http://www.cz88.net/
下载安装纯真 IP,将 qqwry.dat 文件复制到项目下。(本文以放在 /App_Data/ 目录下为例)
在 qqwry.dat 上右键打开属性窗口,将“复制到输出目录”切换到“始终复制”或“如果较新则复制”。
通过 nuget 安装 QQWry,喜欢依赖注入方式的可以选择 QQWry.DependencyInjectio。
var config = new QQWryOptions
{
DbPath = dbPath ?? (AppContext.BaseDirectory + @"App_Data\qqwry.dat")
};
var ipSearch = new QQWryIpSearch(config);
var ipl = ipSearch.GetIpLocation(ip);
return new IpLocation
{
Ip = ipl.Ip,
Country = ipl.Country?.Replace("CZ88.NET", "").Trim(),
Area = ipl.Area?.Replace("CZ88.NET", "").Trim(),
};
github 开源地址:https://github.com/JadynWong/IP_qqwry
核心文件路径:/theme/html/demo*/src/js/components/core.datatable.js
所有参数的默认值见该文件 3369 行起。
data:
属性 | 功能 | 值 |
type | 数据源类型 | local / remote |
source | 数据源 | 链接或对象(见下方) |
pageSize | 每页项数 | 默认 10 |
saveState | 刷新、重新打开、返回时仍保持状态 | 默认 true |
serverPaging | 是否在服务端实现分页 | 默认 false |
serverFiltering | 是否在服务端实现筛选 | 默认 false |
serverSorting | 是否在服务端实现排序 | 默认 false |
autoColumns | 为远程数据源启用自动列功能 | 默认 false |
attr |
data.source:
属性 | 功能 | 值 |
url | 数据源地址 | |
params | 请求参数 |
|
headers |
自定义请求的头 |
|
map | 数据地图,作用是对返回的数据进行整理和定位 |
|
layout:
属性 | 功能 | 值 |
theme | 主题 | 默认 default |
class | 包裹的 CSS 样式 | |
scroll | 在需要时显示横向或纵向滚动条 | 默认 false |
height | 表格高度 | 默认 null |
minHeight | 表格最小高度 | 默认 null |
footer | 是否显示表格底部 | 默认 false |
header | 是否显示表头 | 默认 true |
customScrollbar | 自定义的滚动条 | 默认 true |
spinner |
Loading 样式 |
|
icons | 表格中的 icon |
|
sortable | 是否支持按列排序 | 默认 true |
resizable |
是否支持鼠标拖动改变列宽 | 默认 false |
filterable | 在列中过滤 | 默认 false |
pagination |
显示分页信息 | 默认 true |
editable |
行内编辑 | 默认 false |
columns |
列 | 见本文下方 |
search |
搜索 |
|
layout.columns:
属性 | 功能 | 解释 |
field | 字段名 | 对应 JSON 的属性名,点击表头时作为排序字段名 |
title | 表头名 | 显示在表格头部 |
sortable | 默认排序方式 | 可选:'asc' / 'desc' |
width | 单元格最小宽度 | 值与 CSS 值一致,填数字时默认单位 px |
type | 数据类型 | 'number' / 'date' 等,与本地排序有关 |
format | 数据格式化 | 例格式化日期:'YYYY-MM-DD' |
selector | 是否显示选择框 | 布尔值或对象,如:{ class: '' } |
textAlign | 文字对齐方式 | 'center' |
overflow | 内容超过单元格宽度时是否显示 | 'visible':永远显示 |
autoHide | 自适应显示/隐藏 | 布尔值 |
template | 用于显示内容的 HTML 模板 | function(row) { return row.Id; } |
sortCallback | 排序回调 | 自定义排序方式,参 local-sort.js |
其它:
属性 | 功能 | 解释 |
translate | 翻译 |
参 core.datatable.js 3512 行,简体中文示例:
|
extensions |
暂时没有找到对字符串内容进行自动 HTML 编码的属性,这可能带来 XSS 攻击风险,在 remote 方式中必须在服务端预先 HtmlEncode。即使在 layout.columns.template 中进行处理也是无济于事,恶意代码会在 ajax 加载完成后立即执行。
方法和事件:待完善。
更多信息请查询官方文档:https://keenthemes.com/keen/?page=docs§ion=html-components-datatable
最近,Microsoft 将其针对Web API 的默认序列化从 Newtonsoft JsonConvert 更改为 System.Text.Json JsonSerializer。
using System.Text.Json;
string s = JsonSerializer.Serialize(object);
var obj = JsonSerializer.Deserialize<T>(string);
System.Text.Json 命名空间:https://docs.microsoft.com/zh-cn/dotnet/api/system.text.json?view=net-5.0
如何从 Newtonsoft.Json 迁移到 System.Text.Json:https://docs.microsoft.com/zh-cn/dotnet/standard/serialization/system-text-json-migrate-from-newtonsoft-how-to?pivots=dotnet-5-0
不序列化属性
[System.Text.Json.Serialization.JsonIgnore]
当值为 null 时不序列化
[System.Text.Json.Serialization.JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
格式化/美化
System.Text.Json.JsonSerializer.Serialize(Obj, new System.Text.Json.JsonSerializerOptions {
WriteIndented = true
})
不编码中文(建议默认需要编码,可防止页面显示乱码,除非需要放在 <pre /> 标签中直接显示,可友好显示中文)
System.Text.Json.JsonSerializer.Serialize(Obj, new System.Text.Json.JsonSerializerOptions {
Encoder = System.Text.Encodings.Web.JavaScriptEncoder.UnsafeRelaxedJsonEscaping
})
本文使用 Oracle 官方提供的 MySql.Data.EntityFrameworkCore,如使用 Pomelo.EntityFrameworkCore.MySql 请移步。
对比 MySql.Data.EntityFrameworkCore 与 Pomelo.EntityFrameworkCore.MySql
在 ASP.NET Core 5.0 中使用 MySql.EntityFrameworkCore
本文以 Visual Studio 2019、ASP.NET Core 3.1 开发环境为例。
新建 ASP.NET Core Web 应用程序。
安装 NuGet 包:
MySql.Data.EntityFrameworkCore
Microsoft.EntityFrameworkCore.Design
如果使用 EF Core 2.0 还需安装:Microsoft.EntityFrameworkCore.Tools
根据已有数据库创建数据模型。在 NuGet 的程序包管理(Package Manager)控制台中(PowerShell)执行命令:
Scaffold-DbContext "server=数据库服务器;port=3306;user=数据库用户名;password=数据库密码;database=数据库名" MySql.Data.EntityFrameworkCore -OutputDir Data -f
.Net Core CLi:
dotnet ef dbcontext scaffold "server=数据库服务器;port=3306;user=数据库用户名;password=数据库密码;database=数据库名" MySql.Data.EntityFrameworkCore -o Data -f
搞定。
注:开发环境和生产环境都不需要安装 Connector/NET,只需要安装 ASP.NET Core。
补充:其它数据库提供程序请参考:https://docs.microsoft.com/zh-cn/ef/core/providers/
更多高级用法请参考官方文档。