使用.Net搭配EF Core记录你城市的天气

使用.Net搭配EF Core记录你城市的天气

项目简述

这个项目是我在学习.Net Core编程的时候用到的简单例子,整体就是通过Get请求获取某地区的天气信息,经过json反向序列化后写入到数据库中存储起来。通过.net6的EntityFramework我们可以很优雅地为这个小项目进行CodeFirst的设计(沿用杨中科老师的一个观点:“关系型数据库对程序员来讲只是一个存放数据模型的媒介,理想状态下程序员不用关心数据库的操作。”)这个项目将会用EntityFramework进行数据库的CURD操作,任务目标:

  • 获取和风天气接口的数据
  • 解析数据并进行数据库插入动作

配置条件

  • DotNetCore控制台应用(.Net 6.0)
  • Microsoft SQL Server 2022
  • Nuget Packages
    • EntityFrameworkCore.SqlServer(version6.0.25)
    • EntityFrameworkCore.Tools(version6.0.25)
  • 和风天气Qweather开发服务 (可免费订阅)

关于关系型数据库:

对于SQL Server数据库安装配置,请自行谷歌。

关于和风天气Qweather开发服务:

和风天气免费订阅的接口地址是:

https://devapi.qweather.com/v7/weather/3d?location={LocationCode}&key={Key}

通过简单的Get请求即可获得天气信息结构体,但注意和风天气开发服务的API均使用Gzip进行了压缩,代码中要进行解压处理。详细信息参考官方的开发文档。同时城市代码可以在这个Github仓库当中查询得到。接口返回的json如下

{
  "code": "200",
  "updateTime": "2023-12-07T14:47+08:00",
  "fxLink": "https://www.qweather.com/weather/beijing-101010100.html",
  "now": {
    "obsTime": "2023-12-07T14:38+08:00",
    "temp": "8",
    "feelsLike": "5",
    "icon": "504",
    "text": "浮尘",
    "wind360": "30",
    "windDir": "东北风",
    "windScale": "2",
    "windSpeed": "10",
    "humidity": "30",
    "precip": "0.0",
    "pressure": "1000",
    "vis": "6",
    "cloud": "0",
    "dew": "-8"
  },
  "refer": {
    "sources": [
      "QWeather"
    ],
    "license": [
      "CC BY-SA 4.0"
    ]
  }
}

代码框架设计

创建基础代码框架

通过Visual Studio 2022新建一个C#控制台应用,注意不要选.Net Framework。框架选择.Net 6.0(长期支持)。当然如果想学习.NET 8也可以积极尝试~

对于这次任务用到的数据模型Models有:

  • 和风天气数据模型
  • 城市昵称及其对应的代号

创建城市代码字典

我们可以在解决方案目录下新建Model文件夹(本文假设你的解决方案昵称是WeatherCollectorConsole),,在Model文件夹中新建CityCode文件,我们用Dictionary存放string对象的城市昵称,用int对象存放对应城市代码。代码如下:

namespace WeatherCollectorConsole.Models
{
    public class CityCode
    {
        public readonly Dictionary<string, int>  cityCodeDict = new()
        {
                {"北京", 101010100},
                {"上海", 101020100}
            };
    }
}

创建和风天气接口的数据模型

对于和风天气的json格式,我们在Model文件夹中另行新建QWeatherjson文件,通过类去建立起数据模型。代码如下:

namespace WeatherCollectorConsole.Models
{
    public class QWeatherAPIjson
    {
        public string code { get; set; }
        public string updateTime { get; set; }
        public string fxLink { get; set; }
        public WeatherDetails now { get; set; }
        public Refer refer { get; set; }
    }
    public class WeatherDetails
    {
        public string obsTime { get; set; }
        public string temp { get; set; }
        public string feelsLike { get; set; }
        public string icon { get; set; }
        public string text { get; set; }
        public string wind360 { get; set; }
        public string windDir { get; set; }
        public string windScale { get; set; }
        public string windSpeed { get; set; }
        public string humidity { get; set; }
        public string precip { get; set; }
        public string pressure { get; set; }
        public string vis { get; set; }
        public string cloud { get; set; }
        public string dew { get; set; }
    }

    public class Refer
    {
        public List<string> sources { get; set; }
        public List<string> license { get; set; }
    }
}

注意到,这里类中的每“项”的名称都应该和json的物件名称一一对应,这会在后面存放数值提供大大的方便。

配置EntityFramework环境

对于本文的.NET6.0框架,请在解决方案处右键-选取“管理Nuget程序包”,检索下列nuget包昵称及版本进行安装

  • EntityFrameworkCore.SqlServer(version6.0.25)
  • EntityFrameworkCore.Tools(version6.0.25)

如果是.Net 8.0的项目,就安装对应的version8.x 配置CodeFirst的EntityFramework环境大概需要三类代码:

  • 数据表类的声明
  • 数据表的配置
  • 数据库的操作

数据表WeatherTable的声明

我们可以在Models文件下中建立声明(因为本质上都是一种数据模型~),新建WeatherTable.cs文件,键入代码:

using System.ComponentModel.DataAnnotations;

namespace WeatherCollectorConsole.Models
{
    public class WeatherTable
    {
        [Key]
        public long Id { get; set; }
        public DateTime DataUpdateTime { get; set; }
        public string CityName { get; set; }
        public DateTime ObsTime { get; set; }
        public int? Temp { get; set; }
        public int? TempFeelsLike { get; set; }
        public string? WeatherText { get; set; }
        public int? Wind360 { get; set; }
        public string? WindDir { get; set; }
        public int? WindScale { get; set; }
        public int? WindSpeed { get; set; }
        public int? Humidity { get; set; }
        public double? RainPrecip { get; set; }
        public int? Pressure { get; set; }
        public int? Vis { get; set; }
        public int? Cloud { get; set; }
        public int? Dew { get; set; }
    }
}

注意到:

  • 采用了DataAnnotations标注自增种子主键key
  • 类中声明的变量类型会通过EF(EntityFrameworkd的缩写)对数据库对应的变量类型进行设计,当然可以后续我们可以微调
数据表的配置

数据表的配置建议新建Config代码文件,后续进行代码的维护。新建WeatherTableConfig.cs文件,键入代码:

using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
using WeatherCollectorConsole.Models;
namespace WeatherCollectorConsole
{
    class WeatherTableConfig:IEntityTypeConfiguration<WeatherTable>
    {
        public void Configure(EntityTypeBuilder<WeatherTable> builder)
        {
            builder.ToTable("WeatherTable");
        }
    }
}

这个代码是EF通过WeatherTable类确定好数据表的设计,所以就称呼为“数据表的配置代码”。EF对数据表的约定是以保证不会爆的前提下进行的,如string对应的nvchar长度多少好呢?肯定是max不会爆啊~为了环保主义,我这里会对城市名称CityName、天气状况WeatherText、风向WindDir三个项目限制长度,并对数据更新时间DataUpdateTime(当作数据写入数据库的时间,非数据观测/检测的时间)要求不能为空。将会对builder额外加入属性要求:

builder.Property(b=>b.CityName).HasMaxLength(25).IsRequired();
builder.Property(b=>b.DataUpdateTime).IsRequired();
builder.Property(b=>b.WeatherText).HasMaxLength(30);
builder.Property(b => b.WindDir).HasMaxLength(30);

整体代码如下:

using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
using WeatherCollectorConsole.Models;
namespace WeatherCollectorConsole
{
    class WeatherTableConfig:IEntityTypeConfiguration<WeatherTable>
    {
        public void Configure(EntityTypeBuilder<WeatherTable> builder)
        {
            builder.ToTable("WeatherTable");
            builder.Property(b=>b.CityName).HasMaxLength(25).IsRequired();
            builder.Property(b=>b.DataUpdateTime).IsRequired();
            builder.Property(b=>b.WeatherText).HasMaxLength(30);
            builder.Property(b => b.WindDir).HasMaxLength(30);
        }
    }
}

数据库物件的建立

在解决方案中新建WeatherDBContext.cs文件,键入以下代码:

using Microsoft.EntityFrameworkCore;
using WeatherCollectorConsole.Models;

namespace WeatherCollectorConsole
{
    class WeatherDBContext:DbContext
    {
        public DbSet<WeatherTable> WeatherTables { get;set;}

        protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
        {
            base.OnConfiguring(optionsBuilder);
            optionsBuilder.UseSqlServer("Server=serveraddress,port;Database=database;User ID=username;Password=password");
        }

        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            base.OnModelCreating(modelBuilder);
            modelBuilder.ApplyConfigurationsFromAssembly(this.GetType().Assembly);
        }
    }
}

对于一个练手程序我们暂且通过hardcore并明文保存数据库连接字符串…而且我当你是已经解决好数据库的连接问题的了… 简单看一下这段代码的意义:

  • 声明WeatherDBContext这个类继承EF的DbContext
  • WeatherDBContext中通过DbSet加载你定制的各种数据表的结构
  • OnModelCreating方法中的ApplyConfigurationsFromAssembly可以自动获取DbSet中的T的类型
  • OnConfiguring用于开启你的数据库的连接..

CodeFirtst!

CodeFirtst是最精髓的操作~对于这种非生产型的小项目可能会有无数改动,我们通过修改WeatherTable的定义就可以快速同步修改数据库的数据类型,无论增减还是删库跑路都不用麻烦其他的同事~具体我们会用到EntityFrameworkCore.Tools的Migration进行操作: 首先找到Visual Studio的程序包管理器控制台,如果没有的话在“视图-其他窗口-程序包管理器控制台”可以找到。 请注意Migration这个“迁移”操作需要当前代码能够正常通过编译。排除代码的错误后在程序包管理器控制台键入命令:

Add-Migration MigrationName

请把MigrationName改成实际有意义的remark,Migration操作会在Migration文件夹下生成Datetime_MigrationName.cs文件用于区分每次“迁移”,当Migration命令正常执行后,可以通过SSMS查看得数据库并没有任何变化。这是因为“迁移”命令并没有对数据库执行任何SQL指令。此时,在程序包管理器控制台键入命令:

Update-database

 

数据库就会神奇地按照WeatherTable所声明的形式建立起数据表。

小结

在本文中我们配置了数据库的连接方式,并借用EF轻松地执行了数据库的操作。下文我们将会进行数据的获取与写入库的操作。

引用及参考

  1. 杨中科.NET6视频教程2022版https://www.youtube.com/playlist?list=PL9sJKk6XPMxehYCui7OysUV6trlBbJ4T_

  2. 杨中科.NET6视频教程Part3-EFCore-2-忘掉DBFirst吧,正确搭建Entity Framework Corehttps://www.youtube.com/watch?v=cPpim_K2h9g

 

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

Gravatar 个人资料