本文使用 Oracle 官方提供的 MySql.EntityFrameworkCore,如使用 Pomelo.EntityFrameworkCore.MySql 请移步。
MySql.EntityFrameworkCore 是 MySql.Data.EntityFrameworkCore 的升级版
本文以 Visual Studio 2019、ASP.NET Core 5.0 开发环境为例。
新建 ASP.NET Core Web 应用程序。
安装 NuGet 包:
MySql.EntityFrameworkCore
Microsoft.EntityFrameworkCore.Design
根据已有数据库创建数据模型。在 NuGet 的程序包管理(Package Manager)控制台中(PowerShell)执行命令:
Scaffold-DbContext "server=数据库服务器;port=3306;user=数据库用户名;password=数据库密码;database=数据库名" MySql.EntityFrameworkCore -OutputDir Data -f
.Net Core CLi:
dotnet ef dbcontext scaffold "server=数据库服务器;port=3306;user=数据库用户名;password=数据库密码;database=数据库名" MySql.EntityFrameworkCore -o Data -f
搞定。
注:开发环境和生产环境都不需要安装 Connector/NET,只需要安装 ASP.NET Core。
补充:其它数据库提供程序请参考:https://docs.microsoft.com/zh-cn/ef/core/providers/
更多高级用法请参考官方文档。
获取表名:
using Microsoft.EntityFrameworkCore;
var entityType = _context.Model.FindEntityType(typeof(表实体));
var tableName = entityType.GetTableName();
获取字段名:
using Microsoft.EntityFrameworkCore;
var entityType = _context.Model.FindEntityType(typeof(表实体));
var dic = new Dictionary<string, string>();
foreach (var p in entityType.GetProperties())
{
dic.Add(p.Name, p.GetColumnBaseName());
}
报错:
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
本文使用 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/
更多高级用法请参考官方文档。
ASP.NET MVC 使用 Authorize 过滤器验证用户登录。Authorize 过滤器首先运行在任何其它过滤器或动作方法之前,主要用来做登录验证或者权限验证。
示例:使用 Authorize 过滤器实现简单的用户登录验证。
1、创建登录控制器 LoginController
/// <summary>
/// 登录控制器
/// </summary>
[AllowAnonymous]
public class LoginController : Controller
{
/// <summary>
/// 登录页面
/// </summary>
public ActionResult Index()
{
return View();
}
/// <summary>
/// 登录
/// </summary>
[HttpPost]
public ActionResult Login(string loginName, string loginPwd)
{
if (loginName == "admin" && loginPwd == "123456")
{
// 登录成功
Session["LoginName"] = loginName;
return RedirectToAction("Index", "Home");
}
else
{
// 登录失败
return RedirectToAction("Index", "Login");
}
}
/// <summary>
/// 注销
/// </summary>
public ActionResult Logout()
{
Session.Abandon();
return RedirectToAction("Index", "Login");
}
}
注意:在登录控制器 LoginController 上添加 AllowAnonymous 特性,该特性用于标记在授权期间要跳过 AuthorizeAttribute 的控制器和操作。
2、创建登录页面
@{
ViewBag.Title = "登录页面";
Layout = null;
}
<h2>登录页面</h2>
<form action='@Url.Action("Login","Login")' id="form1" method="post">
用户:<input type="text" name="loginName" /><br />
密码:<input type="password" name="loginPwd" /><br />
<input type="submit" value="登录">
</form>
效果图:
3、创建主页控制器 LoginController
public class HomeController : Controller
{
public ActionResult Index()
{
// 获取当前登录用户
string loginName = Session["LoginName"].ToString();
ViewBag.Message = "当前登录用户:" + loginName;
return View();
}
}
4、创建主页页面
@{
ViewBag.Title = "Index";
Layout = null;
}
<h2>Index</h2>
<h3>@ViewBag.Message</h3>
<a href="@Url.Action("Logout","Login")">注销</a>
效果图:
5、创建授权过滤器 LoginAuthorizeAttribute 类
创建 Filter 目录,在该目录下创建授权过滤器 LoginAuthorizeAttribute 类,继承 AuthorizeAttribute。
using System.Web.Mvc;
namespace MvcApp.Filter
{
/// <summary>
/// 授权过滤器
/// </summary>
public class LoginAuthorizeAttribute : AuthorizeAttribute
{
public override void OnAuthorization(AuthorizationContext filterContext)
{
// 判断是否跳过授权过滤器
if (filterContext.ActionDescriptor.IsDefined(typeof(AllowAnonymousAttribute), true)
|| filterContext.ActionDescriptor.ControllerDescriptor.IsDefined(typeof(AllowAnonymousAttribute), true))
{
return;
}
// 判断登录情况
if (filterContext.HttpContext.Session["LoginName"] == null || filterContext.HttpContext.Session["LoginName"].ToString()=="")
{
//HttpContext.Current.Response.Write("认证不通过");
//HttpContext.Current.Response.End();
filterContext.Result = new RedirectResult("/Login/Index");
}
}
}
}
通常 Authorize 过滤器也是在全局过滤器上面的,在 App_Start 目录下的 FilterConfig 类的 RegisterGlobalFilters 方法中添加:
using System.Web;
using System.Web.Mvc;
using MvcApp.Filter;
namespace MvcApp
{
public class FilterConfig
{
public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
filters.Add(new HandleErrorAttribute());
// 添加全局授权过滤器
filters.Add(new LoginAuthorizeAttribute());
}
}
}
Global.asax 下的代码:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using System.Web.Optimization;
using System.Web.Routing;
namespace MvcApp
{
public class MvcApplication : System.Web.HttpApplication
{
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);
}
}
}
xoyozo:暂时没有按区域(Area)来统一配置的方法,建议加在 Controller 上。https://stackoverflow.com/questions/2319157/how-can-we-set-authorization-for-a-whole-area-in-asp-net-mvc
Unify Template 提供三种文件上传样式(如上图),“Plain File Input”和“File input”使用传统 <form /> 提交,“Advanced File input”调用组件实现。
ASP.NET Core 实现传统 <form /> 提交(以“Plain File Input”为例):
<form class="g-brd-around g-brd-gray-light-v4 g-pa-30 g-mb-30" id="form_avatar_upload" asp-action="AvatarUpload" enctype="multipart/form-data" method="post">
<!-- Plain File Input -->
<div class="form-group mb-0">
<p>Plain File Input</p>
<label class="u-file-attach-v2 g-color-gray-dark-v5 mb-0">
<input id="fileAttachment" name="Avatar" type="file" onchange="form_avatar_upload.submit()">
<i class="icon-cloud-upload g-font-size-16 g-pos-rel g-top-2 g-mr-5"></i>
<span class="js-value">Attach file</span>
</label>
</div>
<!-- End Plain File Input -->
</form>
public class AvatarUploadModel
{
public IFormFile Avatar { get; set; }
}
[HttpPost]
public IActionResult AvatarUpload(AvatarUploadModel Item)
{
return RedirectToAction("");
}
ASP.NET Core 调用 HSFileAttachment 组件实现:
<div class="form-group mb-20">
<label class="g-mb-10">上传作品文件(包)</label>
<input class="js-file-attachment_project" type="file" name="" id="file_project">
<div style="display: none;" id="file_project_tip">
<div class="u-file-attach-v3 g-mb-15" id="file_project_tip_content">
<h3 class="g-font-size-16 g-color-gray-dark-v2 mb-0">拖曳文件到此处或者 <span class="g-color-primary">浏览你的电脑</span></h3>
<p class="g-font-size-14 g-color-gray-light-v2 mb-0">
如果您的作品包含多个文件,请添加到压缩包再上传
</p>
<p class="g-font-size-14 g-color-gray-light-v2 mb-0">
支持的文件格式:
<code>zip</code>
<code>rar</code>
<code>7z</code>
<code>psd</code>
<code>ai</code>
<code>cdr</code>
<code>jpg</code>
<code>png</code>
<code>gif</code>
<code>tif</code>
<code>webp</code>
</p>
<p class="g-font-size-14 g-color-gray-light-v2 mb-0">文件(包)的大小不能超过 <span>@options.Value.MaxSizeOfSigleFile_MB</span> MB</p>
</div>
</div>
<small class="form-control-feedback">请上传作品文件</small>
</div>
<!-- JS Implementing Plugins -->
<script src="//cdn.***.com/unify/unify-v2.6.2/html/assets/vendor/jquery.filer/js/jquery.filer.min.js"></script>
<!-- JS Unify -->
<script src="//cdn.***.com/unify/unify-v2.6.2/html/assets/js/helpers/hs.focus-state.js"></script>
<script src="//cdn.***.com/unify/unify-v2.6.2/html/assets/js/components/hs.file-attachement.js"></script>
<!-- JS Plugins Init. -->
<script>
$(document).on('ready', function () {
// initialization of forms
$.HSCore.components.HSFileAttachment.init('.js-file-attachment_project', {
changeInput: $('#file_project_tip').html(),
uploadFile: {
url: url_UploadFile,
data: { type: 'project' },
success: function (data, element) {
console.log(data);
if (!data.success) {
var parent = element.find(".u-progress-bar-v1").parent();
element.find(".u-progress-bar-v1").fadeOut("slow", function () {
$("<div class=\"text-error g-px-10\"><i class=\"fa fa-minus-circle\"></i> Error</div>").hide().appendTo(parent).fadeIn("slow");
});
alert(data.err_msg);
} else {
var parent = element.find(".u-progress-bar-v1").parent();
element.find(".u-progress-bar-v1").fadeOut("slow", function () {
$("<div class=\"text-success g-px-10\"><i class=\"fa fa-check-circle\"></i> Success</div>").hide().appendTo(parent).fadeIn("slow");
});
$('#file_project_tip_content').slideUp();
upload_project_id = data.id;
}
}
}
});
$.HSCore.helpers.HSFocusState.init();
});
</script>
public IActionResult UploadFile([FromForm(Name = "[]")]IFormFile file, string type)
{
return Ok(new { id = 1 });
}