0

The overall goal is to be able to perform a lookup of a meal based on the userid, and mealid. If that item is found, I want to be able to delete only the entire element that matches both the userid and mealid.

Currently, After sending the request object via postman in a post request, I have tried to write a variation of queries using the C# to create a builder object, and then filter to find the specific array object, and finally delete the array object if there is a match on both userid and mealid. Initially, I was getting the issue where the entire element was not being deleted, but only the interal array element that is nested inside of the element was being (not deleted, but set back to null values). However, now the issue is that the entire array element is not being deleted at all and i'm getting the following error. BsonArraySerializer Error from Visual Studio

Can someone please help me to resolve this issue?

Here is a sample object that I'm sending via postman that I'm trying to delete: Sample Postman POST request with Body data

Here is an example image of data that I'm trying to delete: Sample Image of Json Array Elemet I'm trying to delete

You will need MongoDb Compass or Atlas, .NET Core 3.1, MongoDB C# Driver 2.0, Postman, and .NET Core 3.1 WebApi Project/Solution in order to help solve this issue.

Below, is the code that is needed to replicate the issue:

Startup.cs

Add this line of code to the Configuration method of this file

services.AddScoped<IMealsRepository, MealsRepository>();

Add this line to the ConfigureServices Method

services.AddSingleton(sp =>
            sp.GetRequiredService<IOptions<DatabaseSettings>>().Value);

appSettings.json

Add these lines of code to this file

 "DatabaseSettings": {

    "ConnectionString": "your connection string to MongoDb"

}

Database 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;
        }
    }

}

1 Answers1

0

Thanks for all that attempted to find an answer to my question above, but I actually discovered a simple solution

I simply replaced the above method in the Repository.cs file with the following and it works like a charm

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;
    }