C#实现JSON响应按条件写入SQL Server数据库的问题求助
问题修正与解决方案
一、核心问题梳理
你的代码存在以下关键问题,导致无法实现需求:
- 模型字段映射错误:JSON返回字段(如
logtime、lat_value)与模型属性名不匹配,反序列化会失败 - 反序列化结果未保留:
JsonParser方法中反序列化后未存储结果,后续无法使用数据 - 批量操作不适用业务逻辑:
SqlBulkCopy仅适合无差别批量插入,无法实现你需要的「带条件插入/更新」逻辑 - 缺少条件判断逻辑:未实现
UnemploymentRate>70及后续的插入/更新判断 - 冗余代码:
using块会自动关闭数据库连接,无需手动调用sqlCon.Close()
二、分步修正与实现
1. 修正模型类(添加JSON字段映射)
使用JsonProperty特性将模型属性与JSON字段一一对应,确保反序列化成功:
using Newtonsoft.Json; public class ModelClass { [JsonProperty("logtime")] public string LogTime { get; set; } [JsonProperty("lat_value")] public string Lat { get; set; } [JsonProperty("long_value")] public string Long { get; set; } [JsonProperty("name")] public string Name { get; set; } [JsonProperty("population")] public string Population { get; set; } [JsonProperty("unemployment_rate")] public decimal UnemploymentRate { get; set; } }
2. 保存API获取的数据
修改数据获取方法,将反序列化后的结果存储为类成员变量,供后续数据库操作使用:
using RestSharp; using Newtonsoft.Json; using System.Collections.Generic; using System.Linq; public class ControllerClass { private readonly string _url = "https://my-url-goes-here.net"; private List<ModelClass> _apiData; // 存储API返回的数据 public void FetchApiData() { var client = new RestClient(_url); var request = new RestRequest("api/constructorName", Method.Get) { Timeout = -1 }; request.AddHeader("api-key", "myKeyGoesHere"); request.AddParameter("param1", "someParam"); RestResponse response = client.Execute(request); if (!string.IsNullOrEmpty(response.Content)) { _apiData = JsonConvert.DeserializeObject<List<ModelClass>>(response.Content); } }
3. 实现带条件的插入/更新逻辑
这里提供两种实现方式,推荐使用MERGE语句(高效批量处理):
方式一:使用SQL MERGE语句(推荐)
通过MERGE语句一次性处理所有符合条件的记录,减少数据库交互次数:
using System.Data.SqlClient; using System.Configuration; public void SyncDataToDatabase() { if (_apiData == null || !_apiData.Any()) return; // 过滤出失业率>70的记录 var filteredData = _apiData.Where(d => d.UnemploymentRate > 70).ToList(); if (!filteredData.Any()) return; using (SqlConnection sqlCon = new SqlConnection(ConfigurationManager.ConnectionStrings["Conn"].ConnectionString)) { sqlCon.Open(); // 创建临时表存储待处理数据 using (var createTempTableCmd = new SqlCommand(@" CREATE TABLE #TempData ( LogTime VARCHAR(50), Lat VARCHAR(50), Long VARCHAR(50), Name VARCHAR(100), Population VARCHAR(50), UnemploymentRate DECIMAL(18,2) )", sqlCon)) { createTempTableCmd.ExecuteNonQuery(); } // 将过滤后的数据批量插入临时表 using (SqlBulkCopy bulkCopy = new SqlBulkCopy(sqlCon)) { bulkCopy.DestinationTableName = "#TempData"; bulkCopy.ColumnMappings.Add("LogTime", "LogTime"); bulkCopy.ColumnMappings.Add("Lat", "Lat"); bulkCopy.ColumnMappings.Add("Long", "Long"); bulkCopy.ColumnMappings.Add("Name", "Name"); bulkCopy.ColumnMappings.Add("Population", "Population"); bulkCopy.ColumnMappings.Add("UnemploymentRate", "UnemploymentRate"); bulkCopy.WriteToServer(filteredData.AsDataReader()); } // 使用MERGE执行插入/更新逻辑 using (var mergeCmd = new SqlCommand(@" MERGE INTO tbl_name AS Target USING #TempData AS Source ON Target.Name = Source.Name WHEN MATCHED AND Target.Unemployment != Source.UnemploymentRate THEN UPDATE SET Target.logtime = Source.LogTime, Target.Latitude = Source.Lat, Target.Longitude = Source.Long, Target.Population = Source.Population, Target.Unemployment = Source.UnemploymentRate WHEN NOT MATCHED THEN INSERT (logtime, Latitude, Longitude, Name, Population, Unemployment) VALUES (Source.LogTime, Source.Lat, Source.Long, Source.Name, Source.Population, Source.UnemploymentRate); DROP TABLE #TempData;", sqlCon)) { mergeCmd.ExecuteNonQuery(); } } }
方式二:逐行查询判断(适合数据量小的场景)
如果数据量不大,可以逐行查询数据库现有记录,再执行插入或更新:
public void SyncDataToDatabase() { if (_apiData == null || !_apiData.Any()) return; using (SqlConnection sqlCon = new SqlConnection(ConfigurationManager.ConnectionStrings["Conn"].ConnectionString)) { sqlCon.Open(); foreach (var item in _apiData.Where(d => d.UnemploymentRate > 70)) { // 查询数据库中是否存在同名记录 using (var checkCmd = new SqlCommand(@" SELECT Unemployment FROM tbl_name WHERE Name = @Name", sqlCon)) { checkCmd.Parameters.AddWithValue("@Name", item.Name); var dbUnemployment = checkCmd.ExecuteScalar(); if (dbUnemployment == null) { // 插入新记录 using (var insertCmd = new SqlCommand(@" INSERT INTO tbl_name (logtime, Latitude, Longitude, Name, Population, Unemployment) VALUES (@LogTime, @Lat, @Long, @Name, @Population, @UnemploymentRate)", sqlCon)) { insertCmd.Parameters.AddWithValue("@LogTime", item.LogTime); insertCmd.Parameters.AddWithValue("@Lat", item.Lat); insertCmd.Parameters.AddWithValue("@Long", item.Long); insertCmd.Parameters.AddWithValue("@Name", item.Name); insertCmd.Parameters.AddWithValue("@Population", item.Population); insertCmd.Parameters.AddWithValue("@UnemploymentRate", item.UnemploymentRate); insertCmd.ExecuteNonQuery(); } } else if (Convert.ToDecimal(dbUnemployment) != item.UnemploymentRate) { // 更新现有记录的失业率 using (var updateCmd = new SqlCommand(@" UPDATE tbl_name SET logtime = @LogTime, Latitude = @Lat, Longitude = @Long, Population = @Population, Unemployment = @UnemploymentRate WHERE Name = @Name", sqlCon)) { updateCmd.Parameters.AddWithValue("@LogTime", item.LogTime); updateCmd.Parameters.AddWithValue("@Lat", item.Lat); updateCmd.Parameters.AddWithValue("@Long", item.Long); updateCmd.Parameters.AddWithValue("@Name", item.Name); updateCmd.Parameters.AddWithValue("@Population", item.Population); updateCmd.Parameters.AddWithValue("@UnemploymentRate", item.UnemploymentRate); updateCmd.ExecuteNonQuery(); } } } } } }
4. 主流程调用
在控制台程序的Main方法中调用:
class Program { static void Main(string[] args) { var controller = new ControllerClass(); controller.FetchApiData(); controller.SyncDataToDatabase(); } }
三、关键说明
- MERGE语句优势:相比逐行处理,MERGE只需一次数据库交互,性能更优,适合大数据量场景
- 数据过滤:先过滤出
UnemploymentRate>70的记录,减少不必要的数据库操作 - 参数化查询:所有SQL操作均使用参数化查询,避免SQL注入风险
- 空值判断:添加了
_apiData的空值判断,防止空引用异常
内容的提问来源于stack exchange,提问作者StackUseR
相关产品推荐
相关产品推荐

