美文网首页
C#基础巩固与进阶(定时语音播报+PDF水印+分片上传+EF6框

C#基础巩固与进阶(定时语音播报+PDF水印+分片上传+EF6框

作者: 程序员ken | 来源:发表于2022-11-12 11:14 被阅读0次

    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 = paramDict??new 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 开始,框架现在提供:

    1. Database.BeginTransaction():一种更简单的方法,让用户在现有的 DbContext 中自己启动和完成事务 - 允许在同一事务中合并多个操作,因此所有已提交或所有回滚都为一个事务。 它还允许用户更轻松地指定事务的隔离级别。
    2. Database.UseTransaction():它允许 DbContext 使用在实体框架外部启动的事务。

    5.5.4 处理并发冲突

    方案一:通过 Reload(数据库优先)解决乐观并发异常
    方案二:在客户端优先时解决乐观并发异常
    方案三:自定义乐观并发异常的解决方案
    方案四:使用对象自定义乐观并发异常的解决方案

    5.6 拓展(关于迁移)

    选项一:使用现有架构作为起点

    Code First 迁移使用存储在最近迁移中的模型快照来检测模型的更改(可以在团队环境中的 Code First 迁移中找到关于此的详细信息)。 由于我们将假设数据库已拥有当前模型的架构,因此我们将生成一个空(无操作)迁移,该迁移将当前模型作为快照。

    1. 在包管理器控制台中运行 Add-Migration InitialCreate –IgnoreChanges 命令。 这将创建一个以当前模型作为快照的空迁移。
    2. 在包管理器控制台运行 Update-Database 命令。 这会将 InitialCreate 迁移应用到数据库。 由于实际迁移不包含任何更改,因此它只会向 __MigrationsHistory 表添加一行,指示已应用此迁移。
    3. [图片上传失败...(image-48a274-1668222314579)]

    选项二:使用空数据库作为起点

    在这个场景中,我们需要迁移能够从头开始创建整个数据库,包括本地数据库中已存在的表。 我们将生成一个 InitialCreate 迁移,其中包含用于创建现有架构的逻辑。 然后,我们将使现有数据库看起来就像已应用此迁移一样。

    1. 在包管理器控制台中运行 Add-Migration InitialCreate 命令。 这将创建一个迁移以创建现有架构。

    [图片上传失败...(image-104fb8-1668222314579)]
    [图片上传失败...(image-3cd56e-1668222314579)]

    1. 注释掉新创建迁移的 Up 方法中的所有代码。 这样便可以将迁移“应用”到本地数据库,而无需尝试重新创建所有已经存在的表等。
    2. 在包管理器控制台运行 Update-Database 命令。 这会将 InitialCreate 迁移应用到数据库。 由于实际迁移不包含任何更改(因为已暂时将其注释掉),因此它只会向 __MigrationsHistory 表添加一行,指示已应用此迁移。

    [图片上传失败...(image-40a36-1668222314579)]
    [图片上传失败...(image-dfa547-1668222314579)]

    1. 取消注释 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

    相关文章

      网友评论

          本文标题:C#基础巩固与进阶(定时语音播报+PDF水印+分片上传+EF6框

          本文链接:https://www.haomeiwen.com/subject/uwzwrltx.html