b站视频:2022年C#进阶教程-C#应该学到什么程度(针对编程思维)
前提
UI框架:WinForm(基于.net framework 4.6.1)、MaterialSkin.2(v2.3.0.0)
//第一步 NuGet下载MaterialSkin.2
// 第二步
// 初始化MaterialSkinManager
MaterialSkinManager materialSkinManager = MaterialSkinManager.Instance;
//将此设置为false,以禁用对非材质蒙皮组件的背景色强制
//这必须在AddFormToManager()之前设置
materialSkinManager.EnforceBackcolorOnAllComponents = true;
//MaterialSkinManager 属性
materialSkinManager.AddFormToManage(this);
materialSkinManager.Theme = MaterialSkinManager.Themes.LIGHT;
materialSkinManager.ColorScheme = new ColorScheme(Primary.Teal400, Primary.Teal200, Primary.Teal400, Accent.DeepOrange100, TextShade.WHITE);
ps:如果你想使用wpf到达类似界面效果可以参考。
https://github.com/MaterialDesignInXAML/MaterialDesignInXamlToolkit
章节
1.程序员福音-定时提醒(C#版)
会使用自定义控件
ps:等到后面学习ef6的时候 我们把数据存储到数据库里面 以便打开时能查询到我们设置的任务
2.C#使用Apose.PDF给Pdf文件添加自定义水印
依赖:Aspose.Pdf (v10.0.0.0)
官网:https://docs.aspose.com/pdf/net/
Stream LStream = new MemoryStream(Convert.FromBase64String(ConfigurationManager.AppSettings.Get("aposeLicense")));
new Aspose.Pdf.License().SetLicense(LStream);
ps:仅支持10.0.0.0版本以上
破解版秘钥许可:
PExpY2Vuc2U+DQogIDxEYXRhPg0KICAgIDxMaWNlbnNlZFRvPkFzcG9zZSBTY290bGFuZCBUZWFtPC9MaWNlbnNlZFRvPg0KICAgIDxFbWFpbFRvPmJpbGx5Lmx1bmRpZUBhc3Bvc2UuY29tPC9FbWFpbFRvPg0KICAgIDxMaWNlbnNlVHlwZT5EZXZlbG9wZXIgT0VNPC9MaWNlbnNlVHlwZT4NCiAgICA8TGljZW5zZU5vdGU+TGltaXRlZCB0byAxIGRldmVsb3BlciwgdW5saW1pdGVkIHBoeXNpY2FsIGxvY2F0aW9uczwvTGljZW5zZU5vdGU+DQogICAgPE9yZGVySUQ+MTQwNDA4MDUyMzI0PC9PcmRlcklEPg0KICAgIDxVc2VySUQ+OTQyMzY8L1VzZXJJRD4NCiAgICA8T0VNPlRoaXMgaXMgYSByZWRpc3RyaWJ1dGFibGUgbGljZW5zZTwvT0VNPg0KICAgIDxQcm9kdWN0cz4NCiAgICAgIDxQcm9kdWN0PkFzcG9zZS5Ub3RhbCBmb3IgLk5FVDwvUHJvZHVjdD4NCiAgICA8L1Byb2R1Y3RzPg0KICAgIDxFZGl0aW9uVHlwZT5FbnRlcnByaXNlPC9FZGl0aW9uVHlwZT4NCiAgICA8U2VyaWFsTnVtYmVyPjlhNTk1NDdjLTQxZjAtNDI4Yi1iYTcyLTdjNDM2OGYxNTFkNzwvU2VyaWFsTnVtYmVyPg0KICAgIDxTdWJzY3JpcHRpb25FeHBpcnk+MjAxNTEyMzE8L1N1YnNjcmlwdGlvbkV4cGlyeT4NCiAgICA8TGljZW5zZVZlcnNpb24+My4wPC9MaWNlbnNlVmVyc2lvbj4NCiAgICA8TGljZW5zZUluc3RydWN0aW9ucz5odHRwOi8vd3d3LmFzcG9zZS5jb20vY29ycG9yYXRlL3B1cmNoYXNlL2xpY2Vuc2UtaW5zdHJ1Y3Rpb25zLmFzcHg8L0xpY2Vuc2VJbnN0cnVjdGlvbnM+DQogIDwvRGF0YT4NCiAgPFNpZ25hdHVyZT5GTzNQSHNibGdEdDhGNTlzTVQxbDFhbXlpOXFrMlY2RThkUWtJUDdMZFRKU3hEaWJORUZ1MXpPaW5RYnFGZkt2L3J1dHR2Y3hvUk9rYzF0VWUwRHRPNmNQMVpmNkowVmVtZ1NZOGkvTFpFQ1RHc3pScUpWUVJaME1vVm5CaHVQQUprNWVsaTdmaFZjRjhoV2QzRTRYUTNMemZtSkN1YWoyTkV0ZVJpNUhyZmc9PC9TaWduYXR1cmU+DQo8L0xpY2Vuc2U+
3.C#实现文件分片上传,前端winform+后端.net core api
简述分片上传
所谓分片上传,也就是把文件分成一小片,形成多个文件,上传后服务器将文件组成一个完整的文件,当然此时需要校验文件的hashcode保证上传与组成的文件是同一个文件
.net core 3.1 关于文件上传:
https://learn.microsoft.com/zh-cn/aspnet/core/mvc/models/file-uploads?view=aspnetcore-3.1#upload-large-files-with-streaming
public static string UploadFileHttpRequest(string url,byte[] fileBytes, string fileName, string name = "files"
, Dictionary<string,string> paramDict=null)
{
using (HttpClient client = new HttpClient())
{
try
{
var content = new MultipartFormDataContent();
if (paramDict != null)
{
foreach (var item in paramDict)
{
content.Add(new StringContent(item.Value), item.Key);
}
}
// 上传文件类型
content.Add(new ByteArrayContent(fileBytes), name, fileName);
string result = client.PostAsync(url, content).Result.Content.ReadAsStringAsync().Result;
return result;
}
catch (Exception ex)
{
return ex.Message;
}
}
}
/// <summary>
/// 分片上传(未记录文件hash码)
/// </summary>
/// <param name="url">上传url</param>
/// <param name="filePath">文件位置</param>
/// <param name="sectionLen">每片长度(按M为单位)</param>
/// <param name="name">文件后端接收映射的key值</param>
/// <param name="paramDict">相关参数</param>
/// <returns></returns>
public static string UploadSectionFile(string url, string filePath, int sectionLen = 5, string name = "file"
, Dictionary<string, string> paramDict = null)
{
FileInfo file = new FileInfo(filePath);
string result=null;
int partLen = 1024 * 1024 * sectionLen;//5M
long total = file.Length % partLen == 0 file.Length / partLen : file.Length / partLen + 1;
byte[] sends = new byte[partLen];
int restLen = (int)(file.Length % partLen);
int position = 0;
using (FileStream fileStream = file.OpenRead())
{
for (int i = 0; i < total; i++)
{
if (i == total-1 && restLen > 0)
{
sends = new byte[restLen];
partLen = restLen;
}
fileStream.Read(sends, 0, partLen);
paramDict = paramDictnew Dictionary<string, string>();
if (paramDict.ContainsKey("position"))
{
paramDict.Remove("position");
}
paramDict.Add("position", position + "");//记录文件位置
result = UploadFileHttpRequest(url, sends, file.Name, name, paramDict);
position += partLen;//位置
}
}
return result;
}
/// <summary>
/// 分片上传
/// </summary>
/// <returns></returns>
[HttpPost("partUpload")]
public async Task<IActionResult> OnPostPartUploadAsync(IFormFile file,[FromForm]long position)
{
if (file?.Length > 0)
{
long size = file.Length;
string filePath = Path.Combine(@"F:\", file.FileName);//可以修改保存路径
using (var stream = System.IO.File.Open(filePath, FileMode.OpenOrCreate))
{
stream.Seek(position, SeekOrigin.Begin);
byte[] reads = new byte[1024];
using (var inputStream = file.OpenReadStream())
{
int readV;
while ((readV = inputStream.Read(reads)) > 0)
{
await stream.WriteAsync(reads, 0, readV);
}
}
}
return Ok(new { msg = "上传成功", position, size });
}
return Ok(new { msg = "没有文件需要上传", count = 0 });
}
4.winform的数据绑定
//方式一
xxControl.DataBindings.Add(binding);
//方式二
xxControl.DataBindings.Add(propName,dataSource,memberName);
5.EF6框架(EntityFramework)
需要三张表: t_user_info t_book_info t_borrow_info
5.1 官方地址
源码地址: https://github.com/dotnet/ef6/tree/main/src
文档地址: https://learn.microsoft.com/zh-cn/ef/ef6/
5.2 相关集成(配置文件+代码层面)
本地 SQL Server数据库链接:
Data Source=(LocalDb)\MSSQLLocalDB;AttachDbFilename=C:\Users\CNC\Desktop\MusicDBContext.mdf;Initial Catalog=MusicDBContext;Integrated Security=True
Data Source=(localdb)\MSSQLLocalDB;Initial Catalog=FileApplication.MyDbContext;Integrated Security=True;Connect Timeout=30;Encrypt=False;TrustServerCertificate=False;ApplicationIntent=ReadWrite;MultiSubnetFailover=False
-
第一步 NuGet安装核心依赖项
**第一种:轻便型**
NuGet安装 EntityFramework(v6.2.0)、MySQL.Data.Entities(v6.8.3)
<connectionStrings>
<add name="mysqlCon" connectionString="server=127.0.0.1;port=3306;user=root;password=123456; database=book_manage;Charset=utf8" providerName="MySql.Data.MySqlClient" />
</connectionStrings>
<entityFramework>
<!--MySql.Data.MySqlClient部分安装后需要自己配置,而System.Data.SqlClient是安装依赖后自己生成的-->
<providers>
<provider invariantName="MySql.Data.MySqlClient" type="MySql.Data.MySqlClient.MySqlProviderServices, MySql.Data.Entity.EF6" />
<provider invariantName="System.Data.SqlClient" type="System.Data.Entity.SqlServer.SqlProviderServices, EntityFramework.SqlServer" />
</providers>
</entityFramework>
<!--安装依赖后自动生成的-->
<system.data>
<DbProviderFactories>
<remove invariant="MySql.Data.MySqlClient" />
<add name="MySQL Data Provider" invariant="MySql.Data.MySqlClient" description=".Net Framework Data Provider for MySQL" type="MySql.Data.MySqlClient.MySqlClientFactory, MySql.Data, Version=6.8.8.0, Culture=neutral, PublicKeyToken=c5687fc88969c44d" />
</DbProviderFactories>
</system.data>
第二种: 方便型
(仅安装MySql.Data.EntityFramework,缺点是安装的关联依赖很多,见下图)
直接NuGet安装MySql.Data.EntityFramework(v8.0.30)它会把关联的依赖项加入 如MySql.Data.DLL(8.0.30) + EntityFramework .DLL (v6.2.0)+EntityFramework.SqlServer.DLL (v6.2.0)
- [图片上传失败...(image-136f33-1668222314579)]
<connectionStrings>
<!--mysql 连接信息-->
<add name="mysqlCon" connectionString="server=127.0.0.1;user id=root;password=123456;database=smart-parking;sslmode=none;charset=utf8" providerName="MySql.Data.MySqlClient" />
</connectionStrings>
<system.data>
<DbProviderFactories>
<remove invariant="MySql.Data.MySqlClient" />
<add name="MySQL Data Provider" invariant="MySql.Data.MySqlClient" description=".Net Framework Data Provider for MySQL" type="MySql.Data.MySqlClient.MySqlClientFactory,MySql.Data, Version=8.0.30.0, Culture=neutral, PublicKeyToken=c5687fc88969c44d" />
</DbProviderFactories>
</system.data>
- 第二步
建立实体 DbContext里面具体数据集
5.3 相关特性(注解)
约束注解:
https://learn.microsoft.com/zh-cn/dotnet/api/system.componentmodel.dataannotations.schema?view=net-6.0
组件模型注解:
https://learn.microsoft.com/zh-cn/dotnet/api/system.componentmodel.dataannotations?view=net-6.0&viewFallbackFrom=entity-framework-6.2.0
5.4 日志打印
自定义 DatabaseLogFormatter
通过创建一个派生自 DatabaseLogFormatter 并适当替代方法的新类,可以更改记录的内容及其格式。 最常见的替代方法是:
- LogCommand - 替代此选项可更改命令在执行前的记录方式。 默认情况下,LogCommand 会针对每个参数调用 LogParameter;可选择在替代中执行相同的操作或以不同的方式处理参数。
- LogResult - 替代此选项可更改执行命令结果的记录方式。
- LogParameter - 替代此选项可更改参数记录的格式和内容。
例如,假设我们只想在每个命令发送到数据库之前记录一行。 可通过两个替代来完成:
- 替代 LogCommand 以格式化和写入单行 SQL
- 替代 LogResult,不执行任何操作。
代码将如下所示:
public class OneLineFormatter : DatabaseLogFormatter
{
public OneLineFormatter(DbContext context, Action<string> writeAction)
: base(context, writeAction)
{
}
public override void LogCommand<TResult>(
DbCommand command, DbCommandInterceptionContext<TResult> interceptionContext)
{
Write(string.Format(
"Context '{0}' , Executing Command:\r\n '{1}'{2}",
Context.GetType().Name,
command.CommandText.Replace(Environment.NewLine, ""),
Environment.NewLine));
}
public override void LogResult<TResult>(
DbCommand command, DbCommandInterceptionContext<TResult> interceptionContext)
{
}
}
若要记录输出,只需调用 Write 方法,该方法会将输出发送到配置的写入委托。
(请注意,此代码会简单删除换行符,就像示例一样。它可能无法很好地查看复杂的 SQL.)
设置 DatabaseLogFormatter
创建一个新的 DatabaseLogFormatter 类后,需要向 EF 注册该类。 使用基于代码的配置完成此操作。 简而言之,这意味着在与 DbContext 类相同的程序集中创建一个派生自 DbConfiguration 的新类,然后在这个新类的构造函数中调用 SetDatabaseLogFormatter。 例如:
public class MyDbConfiguration : DbConfiguration
{
public MyDbConfiguration()
{
SetDatabaseLogFormatter(
(context, writeAction) => new OneLineFormatter(context, writeAction));
}
}
5.5 CRUD+事务
5.5.1 封装的DbSet
https://learn.microsoft.com/zh-cn/dotnet/api/system.data.entity.dbset-1?source=recommendations&view=entity-framework-6.2.0
//单个新增
//将给定实体以“已添加”状态添加到集的基础上下文中,这样一来,当调用 SaveChanges 时,会将该实体插入到数据库中。
DataSet.Add(Object);
//多个新增
//将给定的实体集合添加到该集的上下文中,并将每个实体放入“添加”状态,以便调用 SaveChanges 时,该实体将插入数据库。
DataSet.AddRange(IEnumerable)
//将在此上下文中所做的所有更改保存到基础数据库。
DbContext.SaveChanges()
//扩展方法
//需要引入命名空间: System.Data.Entity.Migrations
DataSet.AddOrUpdate(TEntity);
//将在此上下文中所做的所有更改保存到基础数据库。
DbContext.SaveChanges(TEntity[]);
//单个删除
//将给定实体标记为“已删除”,这样一来,当调用 SaveChanges 时,将从数据库中删除该实体。 请注意,在调用此方法之前,该实体必须以另一种状态存在于该上下文中。
DataSet.Remove(Object)
//多个删除
//从设置的上下文中删除给定的实体集合,并将每个实体放入 Deleted 状态,以便调用 SaveChanges 时,将从数据库中删除该实体。
DataSet.RemoveRange(IEnumerable)
//将在此上下文中所做的所有更改保存到基础数据库。
DbContext.SaveChanges()
//方式一
//创建一个原始 SQL 查询,该查询将返回此集中的实体。
DataSet.SqlQuery(String, Object[]);
示例:SqlQuery ("SELECT * FROM dbo.表名 WHERE Author = @author", new SqlParameter ("@author", userSuppliedAuthor) ) ;
//方式二
DataSet.Find(Object[]);
5.5.2 原生SQL
DataSet.SqlQuery("dbo.表名");//sql不带参数
DataSet.SqlQuery("dbo.表名",参数数组);//sql带参数
DataSet.ExecuteSqlCommand(新增/修改/删除语句, Object[]);
//object[] 对应的类型=>new SqlParameter ("@author", userSuppliedAuthor)
5.5.3 自动检测更改
//如果跟踪上下文中的大量实体,并且在循环中多次调用其中某个方法,则可关闭在循环期间的更改检测来获得显著的性能改进。 例如:
using (var context = new DbContext())
{
try
{
context.Configuration.AutoDetectChangesEnabled = false;
// 在循环中进行多次调用(Add方法)
foreach (var blog in aLotOfBlogs)
{
context.Blogs.Add(blog);
}
}
finally
{
context.Configuration.AutoDetectChangesEnabled = true;
}
}
//不要忘记在循环后重新启用更改检测 - 我们使用了 try/finally 来确保始终重新启用更改,即使循环中的代码引发异常。
//禁用和重新启用的替代方法是,使自动检测更改一直保持关闭状态,并且显式调用context.ChangeTracker.DetectChanges或努力使用更改跟踪代理。 这两个都是高级选项,可以轻松地在应用程序中引入细微 bug,因此请谨慎使用它们。
//如果需要在上下文中添加或删除多个对象,请考虑使用 DbSet.AddRange 和 DbSet.RemoveRange。 此方法仅在添加或删除操作完成后自动检测更改一次。
5.5.4 使用事务:
从 EF6 开始,框架现在提供:
- Database.BeginTransaction():一种更简单的方法,让用户在现有的 DbContext 中自己启动和完成事务 - 允许在同一事务中合并多个操作,因此所有已提交或所有回滚都为一个事务。 它还允许用户更轻松地指定事务的隔离级别。
- Database.UseTransaction():它允许 DbContext 使用在实体框架外部启动的事务。
5.5.4 处理并发冲突
方案一:通过 Reload(数据库优先)解决乐观并发异常
方案二:在客户端优先时解决乐观并发异常
方案三:自定义乐观并发异常的解决方案
方案四:使用对象自定义乐观并发异常的解决方案
5.6 拓展(关于迁移)
选项一:使用现有架构作为起点
Code First 迁移使用存储在最近迁移中的模型快照来检测模型的更改(可以在团队环境中的 Code First 迁移中找到关于此的详细信息)。 由于我们将假设数据库已拥有当前模型的架构,因此我们将生成一个空(无操作)迁移,该迁移将当前模型作为快照。
- 在包管理器控制台中运行 Add-Migration InitialCreate –IgnoreChanges 命令。 这将创建一个以当前模型作为快照的空迁移。
- 在包管理器控制台运行 Update-Database 命令。 这会将 InitialCreate 迁移应用到数据库。 由于实际迁移不包含任何更改,因此它只会向 __MigrationsHistory 表添加一行,指示已应用此迁移。
- [图片上传失败...(image-48a274-1668222314579)]
选项二:使用空数据库作为起点
在这个场景中,我们需要迁移能够从头开始创建整个数据库,包括本地数据库中已存在的表。 我们将生成一个 InitialCreate 迁移,其中包含用于创建现有架构的逻辑。 然后,我们将使现有数据库看起来就像已应用此迁移一样。
- 在包管理器控制台中运行 Add-Migration InitialCreate 命令。 这将创建一个迁移以创建现有架构。
[图片上传失败...(image-104fb8-1668222314579)]
[图片上传失败...(image-3cd56e-1668222314579)]
- 注释掉新创建迁移的 Up 方法中的所有代码。 这样便可以将迁移“应用”到本地数据库,而无需尝试重新创建所有已经存在的表等。
- 在包管理器控制台运行 Update-Database 命令。 这会将 InitialCreate 迁移应用到数据库。 由于实际迁移不包含任何更改(因为已暂时将其注释掉),因此它只会向 __MigrationsHistory 表添加一行,指示已应用此迁移。
[图片上传失败...(image-40a36-1668222314579)]
[图片上传失败...(image-dfa547-1668222314579)]
- 取消注释 Up 方法中的代码。 这意味着,当此迁移应用于将来的数据库时,本地数据库中已经存在的架构将由迁移创建。
EF6: 创建 mysql 迁移文件报错
未为提供程序“MySql.Data.MySqlClient”找到任何 MigrationSqlGenerator。请在目标迁移配置类中使用 SetSqlGenerator 方法以注册其他 SQL 生成器。
[图片上传失败...(image-d44b9e-1668222314579)]
解决:
添加特性:
[DbConfigurationType(typeof(MySql.Data.Entity.MySqlEFConfiguration))]
关于注解的详细使用:
https://learn.microsoft.com/zh-cn/ef/ef6/modeling/code-first/data-annotations
关于Mysql(EF6的支持):
https://dev.mysql.com/doc/connector-net/en/connector-net-entityframework60.html