如何使用最新的 MongoDB C# 驱动程序根据多个条件删除复杂数组对象的嵌套数组中的元素?
How can I use latest MongoDB C# Driver to delete an element within a nested array of complex array objects based on multiple conditions?
总体目标是能够根据 userid 和 mealid 执行膳食查找。如果找到该项目,我希望能够仅删除与 userid 和 mealid 匹配的整个元素。
目前,在 post 请求中通过 postman 发送请求对象后,我尝试使用 C# 编写查询的变体来创建构建器对象,然后过滤到找到具体的数组对象,如果userid和mealid都匹配,最后删除数组对象。最初,我遇到的问题是整个元素没有被删除,但只有嵌套在元素内部的内部数组元素被删除(没有删除,但设置回空值)。但是,现在的问题是整个数组元素根本没有被删除,我收到以下错误。
BsonArraySerializer Error from Visual Studio
有人可以帮我解决这个问题吗?
这是我通过 postman 发送的我试图删除的示例对象:
Sample Postman POST request with Body data
这是我要删除的数据的示例图像:
Sample Image of Json Array Elemet I'm trying to delete
您将需要 MongoDb Compass 或 Atlas、.NET Core 3.1、MongoDB C# Driver 2.0、Postman 和 .NET Core 3.1 WebApi Project/Solution 为了帮助解决这个问题。
下面是复制问题所需的代码:
Startup.cs
将这行代码添加到这个文件的Configuration方法中
services.AddScoped<IMealsRepository, MealsRepository>();
将此行添加到 ConfigureServices 方法
services.AddSingleton(sp =>
sp.GetRequiredService<IOptions<DatabaseSettings>>().Value);
appSettings.json
将这些代码行添加到此文件
"DatabaseSettings": {
"ConnectionString": "your connection string to MongoDb"
}
数据库Settings.cs
public class DatabaseSettings
{
public string ConnectionString { get; set; }
}
MealPlanModel.cs:
using MongoDB.Bson;
using MongoDB.Bson.Serialization.Attributes;
public class MealPlanModel
{
#region MealPlanModel fields
[BsonElement("userid")]
public int? UserId { get; set; }
[BsonElement("mealid")]
public int? MealId { get; set; }
[BsonElement("mealname")]
public string MealName { get; set; }
[BsonElement("mealimage")]
public string MealImage { get; set; }
[BsonElement("foods")]
public FoodModel[] Foods { get; set; }
[BsonElement("totalcalories")]
public double TotalCalories { get; set; }
[BsonElement("totalprotein")]
public double TotalProtein { get; set; }
[BsonElement("totalcarbs")]
public double TotalCarbs { get; set; }
[BsonElement("totalfat")]
public double TotalFat { get; set; }
[BsonElement("totalsugar")]
public double TotalSugar { get; set; }
[BsonElement("totalfiber")]
public double TotalFiber { get; set; }
#endregion
#region MealPlanModel ctor
public MealPlanModel()
{
}
public MealPlanModel(int userid, int mealid)
{
UserId = userid;
MealId = mealid;
}
}
MealPlanDto.cs
using MongoDB.Bson;
using MongoDB.Bson.Serialization.Attributes;
using System.Collections.Generic;
public class MealPlanDto
{
[BsonId]
public ObjectId Id { get; set; }
[BsonElement("meals")]
public List<MealPlanModel> MealPlans { get; set; }
}
**MealsController.cs:**
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Security.Claims;
using System.Threading.Tasks;
using AutoMapper;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Newtonsoft.Json;
[Route("api/[controller]")]
[ApiController]
public class MealsController : ControllerBase
{
private readonly IMealsRepository _repo;
private readonly IMapper _mapper;
public MealsController(IMealsRepository repo, IMapper mapper)
{
_repo = repo;
_mapper = mapper;
}
[HttpGet, Route("CheckConnection")]
public async Task<IActionResult> CheckConnection()
{
var result = await _repo.CheckConnection();
if (result == null || result.Count <= 0)
return BadRequest("Failed to connect to database.");
return Ok("Database connection was successful");
}
[HttpPost("deletecustommeal")]
public async Task<IActionResult> DeleteCustomMealPlan(int id)
{
var requestBody = await new StreamReader(HttpContext.Request.Body).ReadToEndAsync();
var mealPlanToDelete = JsonConvert.DeserializeObject<MealPlanModel>(requestBody);
MealPlanDto deleteMealPlan = new MealPlanDto();
deleteMealPlan.MealPlans = new List<MealPlanModel>();
deleteMealPlan.MealPlans.Add(mealPlanToDelete);
var result = await _repo.DeleteCustomMealPlanById(deleteMealPlan);
if (!result)
return BadRequest("Failed to delete meal");
return Ok("Successfully deleted meal plan");
}
}
MealsRepository.cs
using Microsoft.Extensions.Configuration;
using MongoDB.Bson;
using MongoDB.Driver;
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
public class MealsRepository : IMealsRepository
{
private readonly MongoClient _client;
private readonly IMongoDatabase _database;
private readonly IMongoCollection<MealPlanDto> _userMealsCollection;
public MealsRepository(IConfiguration configuration)
{
_client = new
MongoClient(configuration.GetSection("DatabaseSettings").GetSection("ConnectionString").Value);
_database = _client.GetDatabase("MealsDb");
_userMealsCollection = _database.GetCollection<MealPlanDto>("meal");
}
public async Task<List<BsonDocument>> CheckConnection()
{
List<BsonDocument> list = await _database.ListCollections().ToListAsync();
var populatedList = (list != null && list.Count > 0) ? list : null;
return populatedList;
}
public async Task<bool> DeleteCustomMealPlanById(MealPlanDto mealPlanToDelete)
{
var builder = Builders<MealPlanDto>.Filter;
var filter = builder.Eq(x => x.MealPlans[0].UserId, mealPlanToDelete.MealPlans[0].UserId);
var update = Builders<MealPlanDto>.Update.PullFilter(
p => (IEnumerable<MealPlanModel>)p.MealPlans[0],
f => f.MealId.Value == mealPlanToDelete.MealPlans[0].MealId);
try
{
await _userMealsCollection.UpdateOneAsync(filter, update);
return true;
}
catch (Exception ex)
{
Console.WriteLine($"Failed to delete meal plan. {ex} occured.");
return false;
}
}
}
感谢所有试图为我的上述问题找到答案的人,但我实际上发现了一个简单的解决方案
我只是将 Repository.cs 文件中的上述方法替换为以下方法,效果非常好
public bool DeleteCustomMealPlanForUserById(MealPlanModel mealPlanToDelete)
{
var result = _customUserMealsCollection.DeleteOne(p => p.MealPlans[0].UserId == mealPlanToDelete.UserId
&& p.MealPlans[0].MealId == mealPlanToDelete.MealId);
return result.DeletedCount != 0;
}
总体目标是能够根据 userid 和 mealid 执行膳食查找。如果找到该项目,我希望能够仅删除与 userid 和 mealid 匹配的整个元素。
目前,在 post 请求中通过 postman 发送请求对象后,我尝试使用 C# 编写查询的变体来创建构建器对象,然后过滤到找到具体的数组对象,如果userid和mealid都匹配,最后删除数组对象。最初,我遇到的问题是整个元素没有被删除,但只有嵌套在元素内部的内部数组元素被删除(没有删除,但设置回空值)。但是,现在的问题是整个数组元素根本没有被删除,我收到以下错误。 BsonArraySerializer Error from Visual Studio
有人可以帮我解决这个问题吗?
这是我通过 postman 发送的我试图删除的示例对象: Sample Postman POST request with Body data
这是我要删除的数据的示例图像: Sample Image of Json Array Elemet I'm trying to delete
您将需要 MongoDb Compass 或 Atlas、.NET Core 3.1、MongoDB C# Driver 2.0、Postman 和 .NET Core 3.1 WebApi Project/Solution 为了帮助解决这个问题。
下面是复制问题所需的代码:
Startup.cs
将这行代码添加到这个文件的Configuration方法中
services.AddScoped<IMealsRepository, MealsRepository>();
将此行添加到 ConfigureServices 方法
services.AddSingleton(sp =>
sp.GetRequiredService<IOptions<DatabaseSettings>>().Value);
appSettings.json
将这些代码行添加到此文件
"DatabaseSettings": {
"ConnectionString": "your connection string to MongoDb"
}
数据库Settings.cs
public class DatabaseSettings
{
public string ConnectionString { get; set; }
}
MealPlanModel.cs:
using MongoDB.Bson;
using MongoDB.Bson.Serialization.Attributes;
public class MealPlanModel
{
#region MealPlanModel fields
[BsonElement("userid")]
public int? UserId { get; set; }
[BsonElement("mealid")]
public int? MealId { get; set; }
[BsonElement("mealname")]
public string MealName { get; set; }
[BsonElement("mealimage")]
public string MealImage { get; set; }
[BsonElement("foods")]
public FoodModel[] Foods { get; set; }
[BsonElement("totalcalories")]
public double TotalCalories { get; set; }
[BsonElement("totalprotein")]
public double TotalProtein { get; set; }
[BsonElement("totalcarbs")]
public double TotalCarbs { get; set; }
[BsonElement("totalfat")]
public double TotalFat { get; set; }
[BsonElement("totalsugar")]
public double TotalSugar { get; set; }
[BsonElement("totalfiber")]
public double TotalFiber { get; set; }
#endregion
#region MealPlanModel ctor
public MealPlanModel()
{
}
public MealPlanModel(int userid, int mealid)
{
UserId = userid;
MealId = mealid;
}
}
MealPlanDto.cs
using MongoDB.Bson;
using MongoDB.Bson.Serialization.Attributes;
using System.Collections.Generic;
public class MealPlanDto
{
[BsonId]
public ObjectId Id { get; set; }
[BsonElement("meals")]
public List<MealPlanModel> MealPlans { get; set; }
}
**MealsController.cs:**
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Security.Claims;
using System.Threading.Tasks;
using AutoMapper;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Newtonsoft.Json;
[Route("api/[controller]")]
[ApiController]
public class MealsController : ControllerBase
{
private readonly IMealsRepository _repo;
private readonly IMapper _mapper;
public MealsController(IMealsRepository repo, IMapper mapper)
{
_repo = repo;
_mapper = mapper;
}
[HttpGet, Route("CheckConnection")]
public async Task<IActionResult> CheckConnection()
{
var result = await _repo.CheckConnection();
if (result == null || result.Count <= 0)
return BadRequest("Failed to connect to database.");
return Ok("Database connection was successful");
}
[HttpPost("deletecustommeal")]
public async Task<IActionResult> DeleteCustomMealPlan(int id)
{
var requestBody = await new StreamReader(HttpContext.Request.Body).ReadToEndAsync();
var mealPlanToDelete = JsonConvert.DeserializeObject<MealPlanModel>(requestBody);
MealPlanDto deleteMealPlan = new MealPlanDto();
deleteMealPlan.MealPlans = new List<MealPlanModel>();
deleteMealPlan.MealPlans.Add(mealPlanToDelete);
var result = await _repo.DeleteCustomMealPlanById(deleteMealPlan);
if (!result)
return BadRequest("Failed to delete meal");
return Ok("Successfully deleted meal plan");
}
}
MealsRepository.cs
using Microsoft.Extensions.Configuration;
using MongoDB.Bson;
using MongoDB.Driver;
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
public class MealsRepository : IMealsRepository
{
private readonly MongoClient _client;
private readonly IMongoDatabase _database;
private readonly IMongoCollection<MealPlanDto> _userMealsCollection;
public MealsRepository(IConfiguration configuration)
{
_client = new
MongoClient(configuration.GetSection("DatabaseSettings").GetSection("ConnectionString").Value);
_database = _client.GetDatabase("MealsDb");
_userMealsCollection = _database.GetCollection<MealPlanDto>("meal");
}
public async Task<List<BsonDocument>> CheckConnection()
{
List<BsonDocument> list = await _database.ListCollections().ToListAsync();
var populatedList = (list != null && list.Count > 0) ? list : null;
return populatedList;
}
public async Task<bool> DeleteCustomMealPlanById(MealPlanDto mealPlanToDelete)
{
var builder = Builders<MealPlanDto>.Filter;
var filter = builder.Eq(x => x.MealPlans[0].UserId, mealPlanToDelete.MealPlans[0].UserId);
var update = Builders<MealPlanDto>.Update.PullFilter(
p => (IEnumerable<MealPlanModel>)p.MealPlans[0],
f => f.MealId.Value == mealPlanToDelete.MealPlans[0].MealId);
try
{
await _userMealsCollection.UpdateOneAsync(filter, update);
return true;
}
catch (Exception ex)
{
Console.WriteLine($"Failed to delete meal plan. {ex} occured.");
return false;
}
}
}
感谢所有试图为我的上述问题找到答案的人,但我实际上发现了一个简单的解决方案
我只是将 Repository.cs 文件中的上述方法替换为以下方法,效果非常好
public bool DeleteCustomMealPlanForUserById(MealPlanModel mealPlanToDelete)
{
var result = _customUserMealsCollection.DeleteOne(p => p.MealPlans[0].UserId == mealPlanToDelete.UserId
&& p.MealPlans[0].MealId == mealPlanToDelete.MealId);
return result.DeletedCount != 0;
}