MongoDB使用IgnoreExtraElementsConvention解决字段不匹配错误
MongoDB使用IgnoreExtraElementsConvention解决字段不匹配错误
问题现象
在多个子系统项目访问同一个MongoDB的应用场景中,有时候会出现一个子系统给Collection的实体类增加了字段,导致别的子系统查询数据库出错的情况。
新建Blazor Server项目MyWeb1,修改一下WeatherForecast,增加Id字段,可以作为实体类保存到数据库。
public class WeatherForecast { public string Id { get; set; } = Guid.NewGuid().ToString(); public DateTimeOffset Date { get; set; } public int TemperatureC { get; set; } public int TemperatureF => 32 + (int)(TemperatureC / 0.5556); public string? Summary { get; set; } }
在项目中NuGet安装MongoDB驱动
<PackageReference Include="MongoDB.Driver" Version="3.3.0" />
在appsettings.json填写数据库连接字符串
"ConnectionStrings": {
"MongoDb": "mongodb://localhost:27017/"
}
把WeatherForecastService代码改一下,从数据库查询天气记录。
using MongoDB.Driver; using MongoDB.Driver.Linq; public class WeatherForecastService { private readonly MongoClient _mongoDbClient = null; private readonly IConfiguration _configuration; public WeatherForecastService(IConfiguration configuration) { _configuration = configuration; _mongoDbClient = new MongoClient(_configuration.GetConnectionString("MongoDb")); } private static readonly string[] Summaries = new[] { "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching" }; public async Task<WeatherForecast[]> GetForecastAsync(DateTimeOffset startDate) { var db = _mongoDbClient.GetDatabase("WeatherDb"); var collection = db.GetCollection<WeatherForecast>("WeatherForecast"); var items = await collection.AsQueryable().OrderByDescending(x => x.Date).ToListAsync(); if (items.Count == 0) { items = Enumerable.Range(1, 5).Select(index => new WeatherForecast { Date = startDate.AddDays(index), TemperatureC = Random.Shared.Next(-20, 55), Summary = Summaries[Random.Shared.Next(Summaries.Length)] }).ToList(); await collection.InsertManyAsync(items); } return items.ToArray(); } }
运行MyWeb1项目,查看天气预报,可以看到数据库中增加WeatherForecast集合,有5条记录。
新建Blazor Server项目MyWeb2,修改一下WeatherForecast,增加Id字段,并且增加一个湿度字段Humidity。
public class WeatherForecast { public string Id { get; set; } = Guid.NewGuid().ToString(); public DateTimeOffset Date { get; set; } public int TemperatureC { get; set; } public int TemperatureF => 32 + (int)(TemperatureC / 0.5556); public string? Summary { get; set; } /// <summary> /// 湿度 /// </summary> public int Humidity { get; set; } }
把WeatherForecastService代码改一下,从数据库查询天气记录,并且如果不足10条记录,就增加5条带有湿度字段的记录。
public class WeatherForecastService { private readonly MongoClient _mongoDbClient = null; private readonly IConfiguration _configuration; public WeatherForecastService(IConfiguration configuration) { _configuration = configuration; _mongoDbClient = new MongoClient(_configuration.GetConnectionString("MongoDb")); } private static readonly string[] Summaries = new[] { "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching" }; public async Task<WeatherForecast[]> GetForecastAsync(DateTimeOffset startDate) { var db = _mongoDbClient.GetDatabase("WeatherDb"); var collection = db.GetCollection<WeatherForecast>("WeatherForecast"); var items = await collection.AsQueryable().OrderByDescending(x => x.Date).ToListAsync(); if (items.Count < 10) { items = Enumerable.Range(1, 5).Select(index => new WeatherForecast { Date = startDate.AddDays(index), TemperatureC = Random.Shared.Next(-20, 55), Summary = Summaries[Random.Shared.Next(Summaries.Length)], Humidity = Random.Shared.Next(0, 100) }).ToList(); await collection.InsertManyAsync(items); } return items.ToArray(); } }
运行MyWeb2项目,查看天气预报,可以看到数据库中WeatherForecast集合有10条记录了,并且有5条记录是带有湿度Humidity字段的。
然后,再运行MyWeb1项目,查看天气预报,报错字段不匹配。
System.FormatException: Element 'Humidity' does not match any field or property of class WeatherForecast.
解决方案
在MyWeb1项目的WeatherForecastService注册IgnoreExtraElementsConvention,在反序列化记录时,忽略实体类中未定义的字段。
public WeatherForecastService(IConfiguration configuration) { _configuration = configuration; _mongoDbClient = new MongoClient(_configuration.GetConnectionString("MongoDb")); var ignoreExtraElementsConvention = new ConventionPack { new IgnoreExtraElementsConvention(true) }; ConventionRegistry.Register("IgnoreExtraElements", ignoreExtraElementsConvention, _ => true);
再运行MyWeb1项目,查看天气预报,没有报错了。
为了避免出现这个错误,需要给所有访问同一个MongoDB的子系统项目,都加上这个IgnoreExtraElementsConvention。每个子系统项目在升级实体类的时候,严格遵从只增不改不删的原则,就不会影响其他已上线子系统的运行。
浙公网安备 33010602011771号