博客 (61)

本文使用 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 开发环境为例。

  1. 新建 ASP.NET Core Web 应用程序。

  2. 安装 NuGet 包:

    MySql.Data.EntityFrameworkCore

    Microsoft.EntityFrameworkCore.Design

    image.png

    如果使用 EF Core 2.0 还需安装:Microsoft.EntityFrameworkCore.Tools

  3. 根据已有数据库创建数据模型。在 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
  4. 搞定。


注:开发环境和生产环境都不需要安装 Connector/NET,只需要安装 ASP.NET Core。

补充:其它数据库提供程序请参考:https://docs.microsoft.com/zh-cn/ef/core/providers/

更多高级用法请参考官方文档

xoyozo 5 年前
3,786

在升级 kernel 后若无法启动系统,网站无法打开,SSH 无法连接,无法 ping 通。

使用 VNC 进入操作界面:

VNC.png

一种界面是可选择次新的内核版本:

选择内核版本.png

应该能正常启动。

另一种界面提示:

Give root password for maintenance

输入密码后可启动。


阿里云工程师的建议:

1、当前默认是以最新内核启动的,由于新版内核文件存在异常无法正常运行,在手动选择低内核版本启动后,可以先更改下默认内核引导顺序,配置默认使用低版本内核运行,避免重启再次出现问题。 修改内核引导顺序 https://help.aliyun.com/knowledge_detail/41463.html 2、升级内核本身属于高危操作,建议操作前先做好快照备份,同时更新时可以参考下文档方案 避免Linux实例升级内核系统无法启动的方法 https://help.aliyun.com/knowledge_detail/59360.html

xoyozo 5 年前
3,630

刚开始,新增了名为 video 的用户群组,新增了名为 tv 的用户帐号,关联到这个群组,是能够在 DS video 中登录的。但今天登录却提示:

帐号或密码有误。请再试一次。

在勾选了这个帐号的所有权限和应用程序仍然无果后,尝试关联到 administrators 群组能正常登录。当然为了安全性,这个群组是不能随便关联的。

查阅了官网关于 DS video 的文档 https://www.synology.com/zh-cn/knowledgebase/Mobile/help/DSvideo,如果登录失败,请进行以下操作:请以 admin(或属于 administrators 群组的用户)的身份登录到 DSM,然后进入控制面板 > 权限以检查用户帐户是否有使用 Video Station 的权限。

既然官方建议使用 administrators 群组,那基本上没有其它的好办法了。

tv 帐号关联到 administrators 群组,并将所有权限“禁止访问”,将除“Video Station”外的所有其它应用程序设置为“拒绝”。

最后,尝试在 DSM 上登录 tv 帐号,提示:

您没有权限使用本项服务。

那就放心了。

xoyozo 5 年前
18,618

Nuget 安装:X.PagedList.Mvc.Core


控制器:

using X.PagedList;

public IActionResult Index(int page = 1)
{
    ……
    return View(q.ToPagedList(page, size));
}


视图:

@using X.PagedList
@using X.PagedList.Mvc.Core
@model IPagedList<xxx>

@Html.PagedListPager(Model, page => Url.Action("Index", new { page }))


自定义(options 默认值):

@Html.PagedListPager(
    Model, 
    page => Url.Action("Index", new { page }), 
    new X.PagedList.Mvc.Common.PagedListRenderOptionsBase
    {
        HtmlEncoder = HtmlEncoder.get_Default(),
        DisplayLinkToFirstPage = PagedListDisplayMode.IfNeeded,
        DisplayLinkToLastPage = PagedListDisplayMode.IfNeeded,
        DisplayLinkToPreviousPage = PagedListDisplayMode.IfNeeded,
        DisplayLinkToNextPage = PagedListDisplayMode.IfNeeded,
        DisplayLinkToIndividualPages = true,
        DisplayPageCountAndCurrentLocation = false, // 显示总页数和当前页码
        MaximumPageNumbersToDisplay = 10, // 最多显示页码数
        DisplayEllipsesWhenNotShowingAllPageNumbers = true,
        EllipsesFormat = "&#8230;",
        LinkToFirstPageFormat = "<<",
        LinkToPreviousPageFormat = "<",
        LinkToIndividualPageFormat = "{0}",
        LinkToNextPageFormat = ">",
        LinkToLastPageFormat = ">>",
        PageCountAndCurrentLocationFormat = "Page {0} of {1}.",
        ItemSliceAndTotalFormat = "Showing items {0} through {1} of {2}.",
        FunctionToDisplayEachPageNumber = null,
        ClassToApplyToFirstListItemInPager = null,
        ClassToApplyToLastListItemInPager = null,
        ContainerDivClasses = new string[1]
        {
            "pagination-container"
        },
        UlElementClasses = new string[1]
        {
            "pagination"
        },
        LiElementClasses = Enumerable.Empty<string>(),
        PageClasses = Enumerable.Empty<string>(),
        UlElementattributes = null,
        ActiveLiElementClass = "active",
        EllipsesElementClass = "PagedList-ellipses",
        PreviousElementClass = "PagedList-skipToPrevious",
        NextElementClass = "PagedList-skipToNext",
    })


保留地址栏参数:

@{ 
    string query = Context.Request.QueryString.Value;
}
@Html.PagedListPager(Model, page => Regex.IsMatch(query, @"[?&]page=\d+")
    ? Regex.Replace(query, @"([?&])page=\d+", $"$1page={page}")
    : (query.StartsWith("?") ? $"{query}&page={page}" : $"{query}?page={page}"),
    new X.PagedList.Web.Common.PagedListRenderOptionsBase
    {
        DisplayPageCountAndCurrentLocation = true,
        MaximumPageNumbersToDisplay = 5,
    })

这里从查询字符串中判断并替换 page 值,如果有更简单的方法敬请告知。比如 Webdiyer 的分页组件会自动携带所有参数。


更多使用方式参官方文档:https://github.com/dncuug/X.PagedList


附应用于 Unify Template(一款基于 Bootstrap 的 HTML 模板)中的配置:

<style>
    .u-pagination-v1-1--active .u-pagination-v1-1 { color: #fff; border-color: #72c02c; }
    .PagedList-pageCountAndLocation { float: right !important; }
</style>
@{
    string query = Context.Request.QueryString.Value;
}
@Html.PagedListPager(Model, page => Regex.IsMatch(query, @"[?&]page=\d+")
    ? Regex.Replace(query, @"([?&])page=\d+", $"$1page={page}")
    : (query.StartsWith("?") ? $"{query}&page={page}" : $"{query}?page={page}"),
    new X.PagedList.Web.Common.PagedListRenderOptionsBase
    {
        DisplayPageCountAndCurrentLocation = true,
        MaximumPageNumbersToDisplay = 5,
        UlElementClasses = new string[] { "list-inline" },
        LiElementClasses = new string[] { "list-inline-item" },
        PageClasses = new string[] { "u-pagination-v1__item", "u-pagination-v1-1", "g-pa-7-14" },
        ActiveLiElementClass = "u-pagination-v1-1--active",
        EllipsesElementClass = "g-pa-7-14",
    })


xoyozo 6 年前
10,304

安装 Nuget 包:

BuildBundlerMinifier


项目根目录创建绑定配置文件:bundleconfig.json

示例:

[
  {
    "outputFileName": "wwwroot/css/site.min.css",
    "inputFiles": [
      "wwwroot/css/site.css",
      "wwwroot/css/custom.css"
    ]
  },
  {
    "outputFileName": "wwwroot/js/site.min.js",
    "inputFiles": [
      "wwwroot/js/site.js"
    ],
    "minify": {
      "enabled": true,
      "renameLocals": true
    },
    "sourceMap": false
  }
]

配置文件中所有路径都相对于项目根目录(而非静态文件根目录 wwwroot),因此配置的路径都要以“wwwroot”开头。

outputFileName 是压缩合并后的文件,inputFiles 是被压缩合并的原始文件集合。

对于 js 配置部分,minify.enabled 配置是否缩小,renameLocals 配置是否修改变量名,sourceMap 配置是否生成 map 映射文件。


引用示例:

<link rel="stylesheet" href="~/css/site.min.css" asp-append-version="true" />

asp-append-version 表示是否在引用路径添加版本参数,可实现在文件有修改时及时在客户端浏览器中生效。


* 注意:有时候“生成”不一定生效,“重新生成”肯定会生效。


更多高级用法请参考官方文档

xoyozo 6 年前
7,752

ASP.NET Core 缓存 Caching 提供了包括但不限于以下几种存储方式:

内存缓存:https://xoyozo.net/Blog/Details/aspnetcore-memory-cache

SQL Server 缓存:https://xoyozo.net/Blog/Details/aspnetcore-sql-cache

Redis 缓存:https://xoyozo.net/Blog/Details/aspnetcore-redis-cache

MySQL 缓存:https://xoyozo.net/Blog/Details/aspnetcore-mysql-cache


Nuget 安装:Microsoft.Extensions.Caching.SqlServer

执行 .NET Core CLI 命令,在数据库中创建表“TestCache”

dotnet sql-cache create "数据库连接字符串" dbo TestCache

若提示

找不到 "dotnet sql-cache" 命令,请运行以下命令进行安装

则运行

dotnet tool install --global dotnet-sql-cache

表和索引创建成功提示:

Table and index were created successfully.

表结构:

image.png

在 Startup.cs 文件的方法 ConfigureServices() 中添加:

services.AddDistributedSqlServerCache(options =>
{
    options.ConnectionString = "数据库连接字符串";
    options.SchemaName = "dbo";
    options.TableName = "TestCache";
});

在控制器中注入 IDistributedCache:

public class TestController : Controller
{
    private readonly IDistributedCache cache;

    public TestController(IDistributedCache cache)
    {
        this.cache = cache;
    }

    public IActionResult Index()
    {
        string k = "count";
        int v = Convert.ToInt32(cache.GetString(k));
        v++;
        cache.SetString(k, v.ToString());
        return Content(v.ToString());
    }
}

结果:

image.png

更多用法详见官方文档:https://docs.microsoft.com/zh-cn/aspnet/core/performance/caching/distributed?view=aspnetcore-3.0


若要实现将 Session 保存至 SQL Server 中,参此文:https://xoyozo.net/Blog/Details/aspnetcore-session-sql

xoyozo 6 年前
7,181

ASP.NET Core 缓存 Caching 提供了包括但不限于以下几种存储方式:

内存缓存:https://xoyozo.net/Blog/Details/aspnetcore-memory-cache

SQL Server 缓存:https://xoyozo.net/Blog/Details/aspnetcore-sql-cache

Redis 缓存:https://xoyozo.net/Blog/Details/aspnetcore-redis-cache

MySQL 缓存:https://xoyozo.net/Blog/Details/aspnetcore-mysql-cache


Nuget 安装:Microsoft.Extensions.Caching.StackExchangeRedis

在 Startup.cs 文件的方法 ConfigureServices() 中添加:

services.AddStackExchangeRedisCache(options =>
{
    // 连接字符串
    options.Configuration = "192.168.111.134,password=aW1HAyupRKmiZn3Q";
    // 键名前缀
    options.InstanceName = "xoyozo_";
});

在控制器中注入 IDistributedCache:

public class TestController : Controller
{
    private readonly IDistributedCache cache;

    public TestController(IDistributedCache cache)
    {
        this.cache = cache;
    }

    public IActionResult Index()
    {
        string k = "count";
        int v = Convert.ToInt32(cache.GetString(k));
        v++;
        cache.SetString(k, v.ToString());
        return Content(v.ToString());
    }
}

结果:

image.png

更多用法详见官方文档:https://docs.microsoft.com/zh-cn/aspnet/core/performance/caching/distributed?view=aspnetcore-3.0


注:.NET 6 已不支持 Redis on Windows 3.0。


经过以上配置,Session 也会保存到 Redis 中,键名是 前缀_Guid 格式。

关于 Session,参此文:https://xoyozo.net/Blog/Details/aspnetcore-session-redis

xoyozo 6 年前
6,415

首先在项目根目录中创建目录 Areas。

在 Areas 中添加区域,以“Admin”、“Console”两个区域为例:

image.png


VS 提醒在 Startup.cs 的 Configure() 方法中添加 app.UseMvc,此与 app.UseEndpoints 冲突,我们改为在 app.UseEndpoints 中添加 MapControllerRoute(放在 default 之前):

app.UseEndpoints(endpoints =>
{
    endpoints.MapControllerRoute(
        name: "MyArea",
        pattern: "{area:exists}/{controller=Home}/{action=Index}/{id?}");

    endpoints.MapControllerRoute(
        name: "default",
        pattern: "{controller=Home}/{action=Index}/{id?}");
});


给区域的控制器添加属性,如:

[Area("Admin")]
public class HomeController : Controller
{
    public IActionResult Index()
    {
        return View();
    }
}

更多详细请参官方文档:https://docs.microsoft.com/zh-cn/aspnet/core/mvc/controllers/areas?view=aspnetcore-3.1#area-folder-structure

xoyozo 6 年前
7,107

本文使用 Pomelo 提供的 Pomelo.EntityFrameworkCore.MySql,如使用 MySql.Data.EntityFrameworkCore 请移步

对比 MySql.Data.EntityFrameworkCore 与 Pomelo.EntityFrameworkCore.MySql


本文以 Visual Studio 2019、ASP.NET Core 3.0 开发环境为例。

  1. 新建 ASP.NET Core Web 应用程序。

  2. 安装 NuGet 包:

    Microsoft.EntityFrameworkCore.Tools

    Pomelo.EntityFrameworkCore.MySql(3.0.0 预发行版以上)

    image.png

  3. 根据已有数据库创建数据模型。在 NuGet 的程序包管理(Package Manager)控制台中(PowerShell)执行命令:

    Scaffold-DbContext "server=数据库服务器;uid=数据库用户名;pwd=数据库密码;database=数据库名;" Pomelo.EntityFrameworkCore.MySql -OutputDir Data -Force

    .Net Core CLi:

    dotnet ef dbcontext scaffold "server=数据库服务器;uid=数据库用户名;pwd=数据库密码;database=数据库名;" Pomelo.EntityFrameworkCore.MySql -o Data -f
  4. 搞定。


补充:其它数据库提供程序请参考:https://docs.microsoft.com/zh-cn/ef/core/providers/


代码参数说明:

-OutputDir (-o) *** 实体文件所存放的文件目录

-ContextDir *** DbContext文件存放的目录

-Context *** DbContext文件名

-Schemas *** 需要生成实体数据的数据表所在的模式

-Tables(-t) *** 需要生成实体数据的数据表的集合

-DataAnnotations

-UseDatabaseNames 直接使用数据库中的表名和列名(某些版本不支持)

-Force (-f) 强制执行,重写已经存在的实体文件


更多高级用法请参考官方文档


xoyozo 6 年前
8,734
  1. 微信的 H5 页面前端引导到:

    https://api.magcloud.cc/magshare/{siteId}?jump_url={jump_url}&content_url={content_url}

    各参数含义见官方文档:http://doc.magcloud.net/325339

    content_url 地址(一般仍为活动页面)接收参数 magshareredirect,值为 1 则跳转到自定义的引导页。

  2. 自定义引导页按 iOS 和安卓显示图文和箭头,引导用户点击右上角菜单,在浏览器中打开。

  3. 在浏览器 H5 中引导打开:

    {siteId}://pagejump?jump_url={jump_url}

    各参数含义见第 1 步中的文档。

    这里涉及一个“若未安装则引导安装”的过程,可以选择下方合适的一种方式实现:

    • A 标签的 href 填入下载地址,onclick 事件 location 到 App

    • onclick 事件 location 到 App,再用 setTimeOut location 到下载地址

    • 若仍无法实现预期效果,可以在弹出层中放置“未安装,前往下载”和“已安装,立即打开”两个按钮,让用户自己选择

  4. 若下载地址为应用宝,那么在 iOS 中可能会出现“打开 App 后继而自动打开 App Store”的情况,可以自建中转的 App 介绍页引导到 App Store 或应用宝。

  5. 分别实现微信和 MAGAPP 客户端的分享

xoyozo 6 年前
6,392