Take the 2-minute tour ×
Stack Overflow is a question and answer site for professional and enthusiast programmers. It's 100% free, no registration required.

I am creating a new recipe entry form and I am finding two interesting problems:

  1. The Ingredient does not post to the recipe collection, and
  2. The Form will only post a single ingredient and sends null when I have the parameter set to a collection.

Ideally, I'd like the ingredient collection to be under the recipe item, but I'll settle for just getting the ingredient collection to write to the parameter.

Here are the two models (Generated by entity framework):

Recipe:

    public int recipeId { get; set; }

    [Required]
    [StringLength(255)]
    [DisplayName("Name")]
    public string name { get; set; }

    [DisplayName("Category")]
    public int? mealCategoryId { get; set; }

    [DisplayName("Preperation Time")]
    public int? prepTime { get; set; }

    [DisplayName("Cook Time")]
    public int? cookTime { get; set; }

    [DisplayName("Stand Time")]
    public int? standTime { get; set; }

    [DisplayName("Serving Size")]
    public int? servingSize { get; set; }

    [DisplayName("Status")]
    public int recipeStatusId { get; set; }

    [DisplayName("Description")]
    [UIHint("Multiline")]
    public string description { get; set; }

    public virtual ICollection<Direction> Directions { get; set; }

    public virtual ICollection<Image> Images { get; set; }

    public virtual ICollection<Ingredient> Ingredients { get; set; }

    public virtual MealCategory MealCategory { get; set; }

    public virtual ICollection<Nutrition> Nutritions { get; set; }

    public virtual ICollection<Rating> Ratings { get; set; }

    public virtual RecipeStatus RecipeStatus { get; set; }
}

Ingredient:

public partial class Ingredient
    {
        public int ingredientId { get; set; }

        public int? recipeId { get; set; }

        public int? subCategoryId { get; set; }

        public int measurementId { get; set; }

        public int amount { get; set; }

        public virtual Recipe Recipe { get; set; }

        public virtual SubCategory SubCategory { get; set; }

        public virtual Measurement Measurement { get; set; }

    }

View Code:

   <div class="form-horizontal">
        <h4>Ingredients</h4>
        <hr />
        <table class="table IngredientList">
            <tr>
                <th>
                    @Html.LabelFor(model => model.Recipe.Ingredients)
                </th>
                <th>
                    @Html.LabelFor(model => model.SelectedMeasurementId)
                </th>
                <th>
                    @Html.Label("Amount")
                </th>
            </tr>

            @for (int i = 0; i < Model.Recipe.Ingredients.Count; i++)
            {
                <tr>
                    <td>
                        @Html.DropDownListFor(m => Model.Recipe.Ingredients.ElementAt(i).ingredientId, Model.SubIngredients, new { @class = "form-control input-block-level" })
                    </td>
                    <td>
                        @Html.DropDownListFor(m => Model.Recipe.Ingredients.ElementAt(i).measurementId, Model.Measurements, new { @class = "form-control input-block-level" })
                    </td>
                    <td>
                        @Html.TextBoxFor(m => Model.Recipe.Ingredients.ElementAt(i).amount, new { @class = "form-control input-block-level" })
                    </td>
                </tr>
            }

        </table>
</div>

Controller:

   [HttpPost]
    [ValidateAntiForgeryToken]
    public ActionResult Create(Recipe recipe, int SelectedCategoryId, List<Ingredient> Ingredients)

    {
        recipe.mealCategoryId = SelectedCategoryId;
        recipe.recipeStatusId = 1;

        if (ModelState.IsValid)
        {
            dbContext.Entry(recipe).State = EntityState.Added;
            dbContext.SaveChanges();
            return RedirectToAction("Details", new {id = recipe.recipeId});
        }

        return View(recipe);
    }

So, if I have the parameter set to List Ingredients then null is returned. If I have it set to Ingredient Ingredients then I get only the first of four ingredients. (I have the model set to create 4 ingredient records upon loading.)

My question is: How do I get it to pass the collection of ingredients?

EDIT:

I have confirmed that the request is sending all 4 of the ingredients over. It seems to be a problem with the binding.

share|improve this question
    
Is there any reason why you chose to use an ICollection<Ingredients> instead of List<Ingredients> in the model definition –  C Sharper Jul 7 at 17:16
    
@C Sharper It was generated by the entity framework. I am not sure if there's any implications in changing it. However, I have tried doing so and it does not correct the issue. –  Yecats Jul 7 at 17:20
1  
Don't use the Models that EF generates as your "ViewModels" it is bad practice for this reason. Create your own Model Classes and give them the properties that they need. –  C Sharper Jul 7 at 17:32

1 Answer 1

up vote 1 down vote accepted

Create your own ViewModels to hold Data, do not use Entity Frameworks pre generated classes for Model Binding. Here's a very basic example for Proof of Concept

public class Recipe
{
  public int RecipeId {get; set;}
  public List<Ingredients> Ingredient {get; set;}
} 

public class Ingredients
{
  public string Name {get; set;}
  public string Amount {get; set;}
}

public ActionResult Index()
{
   Recipe model = new Recipe();
   model.RecipeId = 1;
   model.Ingredient = new List<Ingredients>();//Better if done inside Constructor

   model.Ingredient.Add(new Ingredients{Name = "Salt", 
                                        Amount = "A Pinch"});
   model.Ingredient.Add(new Ingredients{Name = "Hot Sauce", 
                                        Amount = "HOT HOT HOT!"});   
  return View(model);
}

View

@Html.TextBoxFor(x => x.RecipeId)

  for (int i = 0; i < Model.Ingredient.Count; i++)
    {
          @Html.TextBoxFor(modelItem => Model.Ingredient[i].Name)
          @Html.TextBoxFor(modelItem => Model.Ingredient[i].Amount)
    }

And post it and it will bind

share|improve this answer
    
I've tried this and encounter the same problem. –  Yecats Jul 7 at 17:40
    
This is the correct answer. @Yecats: if it still doesn't work, there's something else going on, but make this change at least, or we won't be able to further help you. –  Chris Pratt Jul 7 at 17:55

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.