博客 (148)

  1. HttpHelper类:

    using System;
    using System.Collections.Specialized;
    using System.IO;
    using System.Net;
    using System.Text;
    
    namespace ConsoleApplication1
    {
        public static class HttpHelper
        {
            private static readonly Encoding DEFAULTENCODE = Encoding.UTF8;
    
            /// <summary>
            /// HttpUploadFile
            /// </summary>
            /// <param name="url"></param>
            /// <param name="file"></param>
            /// <param name="data"></param>
            /// <returns></returns>
            public static string HttpUploadFile(string url, string file, NameValueCollection data)
            {
                return HttpUploadFile(url, file, data, DEFAULTENCODE);
            }
    
            /// <summary>
            /// HttpUploadFile
            /// </summary>
            /// <param name="url"></param>
            /// <param name="file"></param>
            /// <param name="data"></param>
            /// <param name="encoding"></param>
            /// <returns></returns>
            public static string HttpUploadFile(string url, string file, NameValueCollection data, Encoding encoding)
            {
                return HttpUploadFile(url, new string[] { file }, data, encoding);
            }
    
            /// <summary>
            /// HttpUploadFile
            /// </summary>
            /// <param name="url"></param>
            /// <param name="files"></param>
            /// <param name="data"></param>
            /// <returns></returns>
            public static string HttpUploadFile(string url, string[] files, NameValueCollection data)
            {
                return HttpUploadFile(url, files, data, DEFAULTENCODE);
            }
    
            /// <summary>
            /// HttpUploadFile
            /// </summary>
            /// <param name="url"></param>
            /// <param name="files"></param>
            /// <param name="data"></param>
            /// <param name="encoding"></param>
            /// <returns></returns>
            public static string HttpUploadFile(string url, string[] files, NameValueCollection data, Encoding encoding)
            {
                string boundary = "---------------------------" + DateTime.Now.Ticks.ToString("x");
                byte[] boundarybytes = Encoding.ASCII.GetBytes("\r\n--" + boundary + "\r\n");
                byte[] endbytes = Encoding.ASCII.GetBytes("\r\n--" + boundary + "--\r\n");
    
                //1.HttpWebRequest
                HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url);
                request.ContentType = "multipart/form-data; boundary=" + boundary;
                request.Method = "POST";
                request.KeepAlive = true;
                request.Credentials = CredentialCache.DefaultCredentials;
    
                using (Stream stream = request.GetRequestStream())
                {
                    //1.1 key/value
                    string formdataTemplate = "Content-Disposition: form-data; name=\"{0}\"\r\n\r\n{1}";
                    if (data != null)
                    {
                        foreach (string key in data.Keys)
                        {
                            stream.Write(boundarybytes, 0, boundarybytes.Length);
                            string formitem = string.Format(formdataTemplate, key, data[key]);
                            byte[] formitembytes = encoding.GetBytes(formitem);
                            stream.Write(formitembytes, 0, formitembytes.Length);
                        }
                    }
    
                    //1.2 file
                    string headerTemplate = "Content-Disposition: form-data; name=\"{0}\"; filename=\"{1}\"\r\nContent-Type: application/octet-stream\r\n\r\n";
                    byte[] buffer = new byte[4096];
                    int bytesRead = 0;
                    for (int i = 0; i < files.Length; i++)
                    {
                        stream.Write(boundarybytes, 0, boundarybytes.Length);
                        string header = string.Format(headerTemplate, "file" + i, Path.GetFileName(files[i]));
                        byte[] headerbytes = encoding.GetBytes(header);                    
                        stream.Write(headerbytes, 0, headerbytes.Length);
                        using (FileStream fileStream = new FileStream(files[i], FileMode.Open, FileAccess.Read))
                        {                        
                            while ((bytesRead = fileStream.Read(buffer, 0, buffer.Length)) != 0)
                            {
                                stream.Write(buffer, 0, bytesRead);
                            }
                        }
                    }
    
                    //1.3 form end
                    stream.Write(endbytes, 0, endbytes.Length);
                }
                //2.WebResponse
                HttpWebResponse response = (HttpWebResponse)request.GetResponse();
                using (StreamReader stream = new StreamReader(response.GetResponseStream()))
                {
                    return stream.ReadToEnd();
                }
            }
        }
    }
  2. 调用示例:

    using System;
    using System.Collections.Specialized;
    
    namespace ConsoleApplication1
    {
        class Program
        {
            static void Main(string[] args)
            {
                NameValueCollection data = new NameValueCollection();
                data.Add("name", "木子屋");
                data.Add("url", "http://www.mzwu.com/");
                Console.WriteLine(HttpHelper.HttpUploadFile("http://localhost/Test", new string[] { @"E:\Index.htm", @"E:\test.rar" }, data));
    
                Console.ReadKey();
            }
        }
    }
转自 木子屋 4 年前
2,957

Nuget 包:System.Runtime.Caching

依赖注入:

public class HomeController : Controller
{
    private IMemoryCache _cache;

    public HomeController(IMemoryCache memoryCache)
    {
        _cache = memoryCache;
    }

定义键:

public static class CacheKeys
{
    public static string Entry { get { return "_Entry"; } }
}

赋值与取值:

public IActionResult CacheTryGetValueSet()
{
    DateTime cacheEntry;

    // 尝试从缓存获取,若获取失败则重新赋值
    if (!_cache.TryGetValue(CacheKeys.Entry, out cacheEntry))
    {
        // 新的内容
        cacheEntry = DateTime.Now;

        // 缓存选项
        var cacheEntryOptions = new MemoryCacheEntryOptions()
            // Keep in cache for this time, reset time if accessed.
            .SetAbsoluteExpiration(TimeSpan.FromSeconds(3));

        // 保存到缓存中
        _cache.Set(CacheKeys.Entry, cacheEntry, cacheEntryOptions);
    }

    return View("Cache", cacheEntry);
}

注意:

.SetAbsoluteExpiration() 用于设置绝对过期时间,它表示只要时间一到就过期

.SetSlidingExpiration() 用于设置可调过期时间,它表示当离最后访问超过某个时间段后就过期

xoyozo 4 年前
2,756

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

MySql.EntityFrameworkCore 是 MySql.Data.EntityFrameworkCore 的升级版


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

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

  2. 安装 NuGet 包:

    MySql.EntityFrameworkCore

    Microsoft.EntityFrameworkCore.Design

    image.png

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


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

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

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


xoyozo 5 年前
4,921

您好,MySQL Connector/NET  社区,

从 8.0.23  版本开始,我们的  Entity Framework Core 提供程序有了一个新名称。主要目标是保持对 Microsoft Entity Framework Core 不同版本的支持,并确保这些版本与我们的发行版保持紧密联系。同样,此新命名在包装目的方面更为具体。因此,名称的“数据”部分已删除。

之前

    MySql.Data.EntityFrameworkCore v8.0.x

现在:

    MySql.EntityFrameworkCore v8.0.x

现在,Microsoft 维护的不仅是 Entity Framework Core 的单个版本,我们还需要找到一种方法来命名程序包并维护 Entity Framework Core 和 MySQL 版本之间的相关性。这就是我们想到使用软件包元数据的时候。软件包的版本现在由两部分组成,第一部分对应于当前支持的 Microsoft Entity Framework Core 版本,然后是一个“加号”,指示我们包含 MySQL 版本的元数据。

    MySql.EntityFrameworkCore v5.0.0+MySQL8.0.23
    MySql.EntityFrameworkCore v3.1.10+MySQL8.0.23

好吧,还有另外一种未来情况需要考虑。如果 Microsoft 没有为我们的较新的 Connector/NET 版本及时发布 Entity Framework Core 的新版本怎么办?假设当前受支持的 Entity Framework Core 版本是5.0.0,所以我们的软件包看起来像这样 MySql.EntityFrameworkCore 5.0.0+MySQL8.0.23,并且在我们发布下一个版本 8.0.24 时,Microsoft Entity Framework Core 的最新版本仍然是 5.0.0在这种情况下,我们将在版本中添加第四个数字,将包命名为:MySql.EntityFrameworkCore 5.0.0.1+MySQL8.0.23

我们希望您发现此新的命名模型有用且更精确。始终欢迎您提供反馈,您的所有意见都会激励我们不断改进,以便我们为您提供高品质的产品。

最后,以下是一些可能对您有用的链接:

我们希望能与您保持联系!


D
转自 Daniel Valdez 5 年前
2,894

喜欢“数据库优先”的朋友会发现,一旦 nuget 中将 MySql.Data 与 MySql.Data.EntityFramework 升级到版本超过 8.0.19,将无法正常打开 .edmx 文件进行从数据库更新模型。这个问题从 8.0.20 开始出现,在经过等待试验后续多个更新版本后,笔者认为 Oracle 官方不会再针对 8.0.20 及以后的版本支持可视化更新实体模型。

于是面临两个选择:退回到 8.0.19 版本,或使用其它数据库连接组件(如 Pomelo)。

使用其它组件将会更改映射的属性名称,对 Include 延时加载等机制也会发生改变,对于代码量比较大的项目修改成本还是比较大的。那么用回 8.0.19 呢?

在服务器上卸载 8.0.20+ 版本的 mysql-connector-net,安装 8.0.19 版本,重启 IIS 或重启服务器。

项目中,MySql.Data 与 MySql.Data.EntityFramework 也降至 8.0.19,发布后发现,仍然提示:

未能加载文件或程序集“MySql.Data.EntityFramework, Version=8.0.19.0, Culture=neutral, PublicKeyToken=c5687fc88969c44d”或它的某一个依赖项。找到的程序集清单定义与程序集引用不匹配。 (异常来自 HRESULT:0x80131040)

几个月来,我一直认为是服务器上的高版本 mysql-connector-net 没有卸载干净,今天才发现原因是新发布的项目 bin 目录中的 MySql.Data.EntityFramework.dll 版本号仍然高于 8.0.19。手动替换成 8.0.19 后可正常访问。

既然 NuGet 已经将 MySql.Data.EntityFramework 的版本降至 8.0.19,而且 web.config 中所有的版本号也都一致,解决方案的 packages 目录下的也只有 MySql.Data.EntityFramework.8.0.19 这一个版本,甚至连项目源代码中的 bin 目录下的该文件版本也是 8.0.19,可发布后的 MySql.Data.EntityFramework.dll 版本却仍高于 8.0.19?

于是在解决方案中找到以下路径:

\obj\Release\Package\PackageTmp\bin\MySql.Data.EntityFramework.dll

重新发布时,这个文件并没有按照 NuGet 中的版本来覆盖,因此只需要在发布前删除整个 \obj\ 目录再次发布即可解决问题。

xoyozo 5 年前
6,223

“DatabaseFacade”未包含“ExecuteSqlCommand”的定义,并且找不到可接受第一个“DatabaseFacade”类型参数的可访问扩展方法“ExecuteSqlCommand”(是否缺少 using 指令或程序集引用?)


参:原生 SQL 查询

思路:(以 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);


xoyozo 5 年前
8,435

获取表名:

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());
}


xoyozo 5 年前
5,033

报错:

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/ 目录重新发布即可。

xoyozo 5 年前
8,606

通过 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 的功能,具体步骤如下:

  1. 将 Consts.cs 从项目中排除。

  2. 打开 Handlers/UploadHandler.cs,找到 UploadConfig 类,将

    FtpUpload 改为 OssUpload,

    FtpAccount 改为 OssAccessKeyId,

    FtpPwd 改为 OssAccessKeySecret,

    FtpIp 改为 OssAccessEndpoint,

    再添加一个属性 OssBucketName。

  3. 打开 Handlers/UploadHandler.cs,找到 Process() 方法, 

    将 UploadConfig.FtpUpload 改为 UploadConfig.OssUpload,

    将 Consts.AliyunOssServer.* 改为 UploadConfig.Oss*。

  4. 打开 Handlers/CrawlerHandler.cs,找到 Fetch() 方法,

    将 Config.GetValue<bool>("catcherFtpUpload") 改为 Config.GetValue<bool>("OssUpload"),

    将 Consts.AliyunOssServer.* 改为 Config.GetString("Oss*")。

  5. 打开 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 属性。

  6. 打开 config.json,添加相关配置项(注:配置文件中的 *FtpUpload 全部废弃,统一由 OssUpload 决定)

    "OssUpload": true,
    "OssAccessEndpoint": "",
    "OssAccessKeyId": "",
    "OssAccessKeySecret": "",
    "OssBucketName": "",

    将 xxxUrlPrefix 的值改为 OSS 对应的 CDN 网址(以 / 结尾,如://cdn.xoyozo.net/)。


其它注意点:

  • 若使用 UEditorNetCore github 提供的源代码类库代替 nuget,且使用本地存储,那么需要将 Handlers 目录中与定义 savePath 相关的代码(查找  var savePath)替换成被注释的行

xoyozo 5 年前
6,719

纯真 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


xoyozo 5 年前
4,019