0

I have a many-to-many relationship defined in my models among the following entities -

Diseases > Symptoms (One disease can have more than one symptom. One symptom may belong to more than one disease)

I am using a jQuery based Multiple Select Dropdown (Select2) to associate mutiple symptoms at the time of creating a disease entry.

I have got the Adding Disease Information correct so far. I am unable to get the Edit part. I have reviewed a couple of posts here but don't quite seem to get it correct as some of them deal with checkboxes rather than a dropdown.

Here is the code so far:

Model

public class Disease
{
    public Disease() 
    {
        this.Indications = new HashSet<App.Models.Indication.Indication>();
    }

    [Key]
    [DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
    public Int32 DiseaseID { get; set; }

    [Required]
    [Display(Name = "Disease")]
    [StringLength(100)]
    public string DiseaseName { get; set; }

    public virtual ICollection<App.Models.Indication.Indication> Indications { get; set; }              
}

public class DiseaseAddViewModel
{
    public Disease Disease {get; set;}
    public IEnumerable<SelectListItem> Indications { get; set; }
    public Int32[] SelectedIndications { get; set; }        
}

An excerpt from the view is as follows -

<div class="col-sm-9">
@Html.DropDownListFor(m => m.SelectedIndications, Model.Indications, new { @class="select2_demo_1 form-control", @multiple="multiple" })
</div>

Controller

    [HttpGet]
    public ActionResult Add()
    {
        var model = new DiseaseAddViewModel();
        var allIndications = db.Indication.ToList();
        model.Indications = allIndications.Select(o => new SelectListItem
            {
                Text = o.IndicationName,
                Value = o.IndicationID.ToString()
            });           
    }

   [HttpPost]
    [ValidateAntiForgeryToken]
    public ActionResult Add(DiseaseAddViewModel model)
    {
        if (ModelState.IsValid)
        {
            if (!db.Disease.Any(d => d.DiseaseName == model.Disease.DiseaseName))
            {
                Int32[] selectedIndications = model.SelectedIndications;

                var Disease = new Disease { DiseaseName = model.Disease.DiseaseName };
                AddIndications(Disease, selectedIndications);
                db.Disease.Add(Disease);
                db.SaveChanges();
                TempData["SuccessMsg"] = "Disease information added successfully.";
            }
            else
            {
                TempData["ErrorMsg"] = "Disease name already exists in the database.";
            }
            return RedirectToAction("Index", "Diseases");
        }
        return View();
    }

    private void AddIndications(Disease disease, Int32[] assignedIndications)
    {
        for (int item = 0; item < assignedIndications.Length; item++)
        {
            var indication = new Indication();
            indication.IndicationID = assignedIndications[item];
            db.Indication.Attach(indication);
            disease.Indications.Add(indication);
        }
    }

How do I code the Edit (Get and Post) so that the related indications are pre-populated in the dropdown and upon posting how do I manage the addition/removal of related entities?

Please feel free to suggest if there is a better way to do the Add functionality from above.

Many thanks!

Jason
  • 5
  • 5
  • Please note that Indications and Symptoms are the same entity. – Jason Mar 16 '17 at 15:10
  • To pre-populate the selected options, you just set the value of `SelectedIndications` in the GET method before you pass the model to the view (e.g. `model.SelectedIndications = new int[]{ 2, 4 }; return View(model);` and the options with values 2 and 4 will be selected. But you cannot use `DropDownListFor() with `@multiple="multiple"` - it needs to be `@Html.ListBoxFor()` –  Mar 16 '17 at 22:27
  • And for a detailed explanation of why `DropDownList()` will not work, refer [this answer](http://stackoverflow.com/questions/40725358/why-does-the-dropdownlistfor-lose-the-multiple-selection-after-submit-but-the-li/40732481#40732481) –  Mar 16 '17 at 22:30
  • @Stephen Muecke Thanks for your comment. I was using a DropDownListFor in my add view which has been working fine so far. I have now changed it to a ListBoxFor. However, when I try to read the values into the int array before passing it to the view, I get an error that says - The array type 'System.Int32[]' cannot be initialized in a query result. Consider using 'System.Collections.Generic.List`1[System.Int32]' instead. – Jason Mar 17 '17 at 08:01
  • Then make it `IEnumerable` rather than `int[]` (or modify the query) - but you have not shown your code for that so I cant comment on what your doing wrong –  Mar 17 '17 at 08:04
  • @Stephen Muecke Thanks for your reply. Here is the code : var model1 = db.Disease.Where(d => d.DiseaseID == id).Select(d => new DiseaseEditViewModel { DiseaseName = d.DiseaseName, SelectedIndications = new int[] { 1,2,3 } }).FirstOrDefault(); – Jason Mar 17 '17 at 08:34

1 Answers1

0

Problem resolved by changing the model and controller code as follows -

Controller

    [HttpGet]
    public ActionResult Edit(int? id)
    {
        if (id == null)
        {
            return RedirectToAction("Index");
        }

        var model = new DiseaseEditViewModel
        {
            Disease = db.Disease.Include(i => i.Indications).Include(p => p.LaboratoryParameters).First(i => i.DiseaseID == id)
        };

        if (model.Disease == null)
            return HttpNotFound();

        var allIndicationsList = db.Indication.ToList();
        model.Indications = allIndicationsList.Select(o => new SelectListItem
        {
            Text = o.IndicationName,
            Value = o.IndicationID.ToString()
        });

        return View(model);
    }

Model

    public class DiseaseEditViewModel
        {
            public Disease Disease {get; set;}
            public IEnumerable<SelectListItem> Indications { get; set; }

            private List<int> _selectedIndications;

            public List<int> SelectedIndications
            {
                get
                {
                    if (_selectedIndications == null)
                    {
                        _selectedIndications = Disease.Indications.Select(m => m.IndicationID).ToList();
                    }
                    return _selectedIndications;
                }
                set { _selectedIndications = value; }
            }    
}

Finally, changed the DropdownlistFor in the view to ListBoxFor as suggested by @Stephen Muecke. Thanks.

Jason
  • 5
  • 5