Home > Back-end >  Many to Many crud operations in asp.net core
Many to Many crud operations in asp.net core

Time:12-19

I have two entities (Product and Supply) that have a many-to-many relationship. I also have an entity between then that holds the two ID's (SupplyProduct).

My entities:

    public class Product
    {
        [Key]
        public int ProductId { get; set; }
        [Required]
        public string? ProductName { get; set; }
        [Required]

        [Column(TypeName = "decimal(6,2)")]
        public decimal UnitPrice { get; set; }
        public int Quantity { get; set; }
        public string? Brand { get; set; }
        public DateTime CreatedDateTime { get; set; } = DateTime.Now;

        //Many to many relationship between the products and the stocks
        public virtual ICollection<SupplyProduct>? SupplyProducts { get; set; }
    }

    public class Supply
    {
        [Key]
        [Required]
        public int SupplyId { get; set; }
        [Required]

        [DisplayName("Supply's Label")]
        public string? Label { get; set; }
        
        //One to many relationship between the Stock and the Merchant
        public Merchant? Merchant { get; set; }

        //Many to many relationship between the stocks and the products
        public virtual ICollection<SupplyProduct>? SupplyProducts { get; set; }

    }

    public class SupplyProduct
    {
        [Key]
        public int SupplyId { get; set; }
        public virtual Supply? Supply { get; set; }
        [Key]
        public int ProductId { get; set; }
        public virtual Product? Product { get; set; }
    }

I want to assign a supply to a product while creating it . and then show the supply with it's associated products

this is my products controller:

ProductsController.cs

public class ProductController : Controller
    {
        private readonly ApplicationDbContext _db;

        public ProductController(ApplicationDbContext db)
        {
            _db = db;
        }

        // GET: ProductController
        public ActionResult Index()
        {
            IEnumerable<Product> ProductsList = _db.Products;
            return View(ProductsList);
        }

        // GET: ProductController/Create
        public ActionResult Create()
        {
            IEnumerable<Supply> SuppliesList = _db.Supplies.Include(s => s.Merchant);
            ViewBag.Supplies = SuppliesList;
            return View();
        }

        // POST: ProductController/Create
        [HttpPost]
        public ActionResult Create(Product model, List<int> supplyIds)
        {
            _db.Products.Add(model);
            _db.SaveChanges();
            
            SupplyProduct SP = new();
            foreach (var supplyId in supplyIds)
            {
                SP.SupplyId = supplyId;
                SP.ProductId = model.ProductId;
                SP.Product = model;
                SP.Supply = _db.Supplies.Where(x => x.SupplyId == supplyId).FirstOrDefault();
            }
            _db.SupplyProducts.Add(SP);
            _db.SaveChanges();
            return RedirectToAction(nameof(Index));
        }
}

Can you please check my post Create method if it is as it should be, and how can I get the Products data while returning the Supplies in the Index method into the index view?

Thank you so much for your help and happy coding :D

CodePudding user response:

Can you please check my post Create method if it is as it should be

Modify your code like below, otherwise you will always store the second supply in supplyIds:

 [HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Create(Product model, List<int> supplyIds)
{
    _context.Product.Add(model);
    _context.SaveChanges();

    SupplyProduct SP = new();
    foreach (var supplyId in supplyIds)
    {
        SP.SupplyId = supplyId;
        SP.ProductId = model.ProductId;
        SP.Product = model;
        SP.Supply = _context.Supply.Where(x => x.SupplyId == supplyId).FirstOrDefault();
        _context.SupplyProducts.Add(SP);   //move to here...
        _context.SaveChanges();
    }
    // _context.SupplyProducts.Add(SP);
    //_context.SaveChanges();
    return RedirectToAction(nameof(Index));
}

how can I get the Products data while returning the Supplies in the Index method into the index view?

Change your Index method like below:

// GET: Products
public async Task<IActionResult> Index()
{
    var data = await _context.Product.Include(p => p.SupplyProducts)
                             .ThenInclude(sp => sp.Supply).ToListAsync();
    return View(data);
}

CodePudding user response:

You can remove the SupplyProduct tabble if there are no additional properties in anything other than Supply Product you don't need it for many-to many. Then initialize the collections in the Supply and Product

public class Product
{
    public Product()
    {
        this.Supplys = new HashSet<Supply>();
    }

    //... your props

    public virtual ICollection<Supply> Supplys { get; set; }
}

public class Supply
{
    public Supply()
    {
        this.Products = new HashSet<Product>();
    }

    //... your props

    public virtual ICollection<Product> Products { get; set; }
}

Add Product to Supplys with only one query (in your code you make query for everyone Id in supplyIds)

[HttpPost]
public ActionResult Create(Product model, List<int> supplyIds)
{
     //Get all supplys you need by id
     var supplys = _db.Supplys
         .Where(x => supplyIds.Contains(x.SupplyId))
         .ToList();
     
     //Add product in each supply
     foreach (var supply in supplys)
     {
         supply.Products.Add(model);
     }

     //Update db
     _db.SaveChanges();

     return RedirectToAction(nameof(Index));
}

Get from DB

public ActionResult GetSuplys(List<int> supplyIds)
{
     //Here you get all Supplys with the Products in it
     var supplys = _db.Supplys
         .Include(x => x.Products)
         .Where(x => supplyIds.Contains(x.SupplyId))
         .ToList();

     //...
}

Save new Supply of Product

public ActionResult NewSuply()
{

     var supply = new Supply
     {
        ProductName = name,
        //Add all props you need
        //You can add Product here or add empty collection
        Products.Add(product), or = new List<Product>();
     }
     
     //No need to save Product separate
     _db.Add(supply);
     _db.SaveChanges();
}
  • Related