0

I have a problem figuring out how to implement the Create, Edit and Delete pages for a case where there are multiple Models that I want to display in a single set of Views. I'm using Visual Studio 2013 and MVC 5. And I also use the latest Entity Framework 6.1.1.

So far I've got the Models:

public class CompositeModel
    {
        public int ID { get; set; }

        public MediaPlan MediaPlanModel { get; set; }
        public ContactInfo ContactInfoModel { get; set; }
        public MediaPlanDate MediaPlanDateModel { get; set; }

        [Timestamp]
        public byte[] RowVersion { get; set; }


    }

public class MediaPlan
{
    public int ID { get; set; }

    public string Client { get; set; }
    public string Product { get; set; }
    public string AE { get; set; }
    public string Supervisor { get; set; }
    public string Traffic { get; set; }
}

public class ContactInfo
{
    public int ID { get; set; }

    public string CompanyName { get; set; }
    public string CompanyContact { get; set; }
    public string CompanyAddress { get; set; }
    public string CompanyPhoneNumber { get; set; }
    public string CompanyEmail { get; set; }
}

public class MediaPlanDate
{
    public int ID { get; set; }

    public string Client { get; set; }
    public string Product { get; set; }
    public string AE { get; set; }
    public string Supervisor { get; set; }
    public string Traffic { get; set; }
}

Using Code First approach the database was created correctly.

I did auto-generate the CompositeModelsController.cs:

public class CompositeModelsController : Controller
{
    private MprContext db = new MprContext();

    // GET: CompositeModels
    public ActionResult Index()
    {
        //var compositeModel = new CompositeModel();
        //compositeModel.MediaPlanModel = new MediaPlan();
        //compositeModel.ContactInfoModel = new ContactInfo();
        //compositeModel.MediaPlanDateModel = new MediaPlanDate();

        //return View(compositeModel.ToList());
        return View(db.Composites.ToList());
    }

    // GET: CompositeModels/Details/5
    public ActionResult Details(int? id)
    {
        if (id == null)
        {
            return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
        }
        CompositeModel compositeModel = db.Composites.Find(id);
        if (compositeModel == null)
        {
            return HttpNotFound();
        }
        return View(compositeModel);
    }

    // GET: CompositeModels/Create
    public ActionResult Create()
    {
        return View();
    }

    // POST: CompositeModels/Create
    // To protect from overposting attacks, please enable the specific properties you want to bind to, for 
    // more details see http://go.microsoft.com/fwlink/?LinkId=317598.
    [HttpPost]
    [ValidateAntiForgeryToken]
    public ActionResult Create([Bind(Include = "ID,RowVersion")] CompositeModel compositeModel)
    {
        if (ModelState.IsValid)
        {
            db.Composites.Add(compositeModel);
            db.SaveChanges();
            return RedirectToAction("Index");
        }

        return View(compositeModel);
    }

    // GET: CompositeModels/Edit/5
    public ActionResult Edit(int? id)
    {
        if (id == null)
        {
            return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
        }
        CompositeModel compositeModel = db.Composites.Find(id);
        if (compositeModel == null)
        {
            return HttpNotFound();
        }
        return View(compositeModel);
    }

    // POST: CompositeModels/Edit/5
    // To protect from overposting attacks, please enable the specific properties you want to bind to, for 
    // more details see http://go.microsoft.com/fwlink/?LinkId=317598.
    [HttpPost]
    [ValidateAntiForgeryToken]
    public ActionResult Edit([Bind(Include = "ID,RowVersion")] CompositeModel compositeModel)
    {
        if (ModelState.IsValid)
        {
            db.Entry(compositeModel).State = EntityState.Modified;
            db.SaveChanges();
            return RedirectToAction("Index");
        }
        return View(compositeModel);
    }

    // GET: CompositeModels/Delete/5
    public ActionResult Delete(int? id)
    {
        if (id == null)
        {
            return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
        }
        CompositeModel compositeModel = db.Composites.Find(id);
        if (compositeModel == null)
        {
            return HttpNotFound();
        }
        return View(compositeModel);
    }

    // POST: CompositeModels/Delete/5
    [HttpPost, ActionName("Delete")]
    [ValidateAntiForgeryToken]
    public ActionResult DeleteConfirmed(int id)
    {
        CompositeModel compositeModel = db.Composites.Find(id);
        db.Composites.Remove(compositeModel);
        db.SaveChanges();
        return RedirectToAction("Index");
    }

    protected override void Dispose(bool disposing)
    {
        if (disposing)
        {
            db.Dispose();
        }
        base.Dispose(disposing);
    }
}

I did not expect it to work and it doesn't.

I was able to get the Index to work.

I also created Editor Templates:

@model Online_Mpr.Models.MediaPlan

<h2>MediaPlan</h2>


@using (Html.BeginForm())
{
    @Html.AntiForgeryToken()

    <div class="form-horizontal">
        <h4>MediaPlan</h4>
        <hr />
        @Html.ValidationSummary(true, "", new { @class = "text-danger" })
        @Html.HiddenFor(model => model.ID)

        <div class="form-group">
            @Html.LabelFor(model => model.Client, htmlAttributes: new { @class = "control-label col-md-2" })
            <div class="col-md-10">
                @Html.EditorFor(model => model.Client, new { htmlAttributes = new { @class = "form-control" } })
                @Html.ValidationMessageFor(model => model.Client, "", new { @class = "text-danger" })
            </div>
        </div>

        <div class="form-group">
            @Html.LabelFor(model => model.Product, htmlAttributes: new { @class = "control-label col-md-2" })
            <div class="col-md-10">
                @Html.EditorFor(model => model.Product, new { htmlAttributes = new { @class = "form-control" } })
                @Html.ValidationMessageFor(model => model.Product, "", new { @class = "text-danger" })
            </div>
        </div>

        <div class="form-group">
            @Html.LabelFor(model => model.AE, htmlAttributes: new { @class = "control-label col-md-2" })
            <div class="col-md-10">
                @Html.EditorFor(model => model.AE, new { htmlAttributes = new { @class = "form-control" } })
                @Html.ValidationMessageFor(model => model.AE, "", new { @class = "text-danger" })
            </div>
        </div>

        <div class="form-group">
            @Html.LabelFor(model => model.Supervisor, htmlAttributes: new { @class = "control-label col-md-2" })
            <div class="col-md-10">
                @Html.EditorFor(model => model.Supervisor, new { htmlAttributes = new { @class = "form-control" } })
                @Html.ValidationMessageFor(model => model.Supervisor, "", new { @class = "text-danger" })
            </div>
        </div>

        <div class="form-group">
            @Html.LabelFor(model => model.Traffic, htmlAttributes: new { @class = "control-label col-md-2" })
            <div class="col-md-10">
                @Html.EditorFor(model => model.Traffic, new { htmlAttributes = new { @class = "form-control" } })
                @Html.ValidationMessageFor(model => model.Traffic, "", new { @class = "text-danger" })
            </div>
        </div>

        <div class="form-group">
            <div class="col-md-offset-2 col-md-10">
                <input type="submit" value="Save" class="btn btn-default" />
            </div>
        </div>
    </div>
}

<div>
    @Html.ActionLink("Back to List", "Index")
</div>

@section Scripts {
    @Scripts.Render("~/bundles/jqueryval")
}

And so forth for the other Models.

Then I have the Create View:

@model Online_Mpr.Models.CompositeModel

@{
    ViewBag.Title = "Create";
}

<h2>Create</h2>


@using (Html.BeginForm()) 
{
    @Html.AntiForgeryToken()

    <div class="form-horizontal">
        <h4>CompositeModel</h4>
        <hr />
        @Html.ValidationSummary(true, "", new { @class = "text-danger" })
        @Html.EditorFor(model => model.MediaPlanModel)
        @Html.EditorFor(model => model.MediaPlanDateModel)
        @Html.EditorFor(model => model.ContactInfoModel)
        <div class="form-group">
            <div class="col-md-offset-2 col-md-10">
                <input type="submit" value="Create" class="btn btn-default" />
            </div>
        </div>
    </div>
}



<div>
    @Html.ActionLink("Back to List", "Index")
</div>

@section Scripts {
    @Scripts.Render("~/bundles/jqueryval")
}

I was planning to do similar thing for the Edit View.

When I go to the Create View it displays the edit boxes for all the elements but how do I get to code the controller that will create the records in the database. Preferably I would like to have just one "Create" button in the Create View that would create all four records.

Any help is appreciated as I scoured the internet for info about similar cases and could not find any.

Also if you could provide code samples. They don't have to be elaborate but the core thought will lead me on.

Thanks, -Arek

3
  • What is that you are trying to do? All that you want is to post a list of records in one go to Controller? If yes, you may take a look here Commented Dec 10, 2014 at 16:15
  • Do you want to edit all models at once? or only one model at a time ever? Commented Dec 10, 2014 at 17:56
  • I wanted to edit all models at once. But then it came to me that straggling with multiple models might be pointless and I maybe should create one big model. It will accomplish the same thing. And since I had it this way the first time around I know it will work. What do you think? And thanks for responses. Commented Dec 10, 2014 at 18:32

1 Answer 1

3

Your CompositeModel is essentially a view model. This class shouldn't have an Id property and should n't actually be tied to Entity Framework in any way (don't add a DbSet for it). All it needs to do is contain properties for the other model instances that you want to edit. You should also add a constructor to this class that news up all other models inside:

public class CompositeModel
{
    public CompositeModel()
    {
        MediaPlanModel = new MediaPlan();
        ContactInfoModel = new ContactInfo();
        MediaPlanDateModel = new MediaPlanDate();
    }

    ...
}

Then in post version of your Create action. You'll simply save each sub-model individually:

[HttpPost]
public ActionResult Create(CompositeModel model)
{
    if (ModelState.IsValid)
    {
        db.MediaPlans.Add(model.MediaPlanModel);
        db.ContactInfos.Add(model.ContactInfoModel);
        db.MediaPlanDates.Add(model.MediaPlanDateModel)
        db.SaveChanges();

        return RedirectToAction("Index");
    }

    return View(model);
}

For your edit version:

[HttpPost]
public ActionResult Edit(CompositeModel model)
{
    if (ModelState.IsValid)
    {
        db.Entry(model.MediaPlanModel).State = EntityState.Modified;
        db.Entry(model.ContactInfoModel).State = EntityState.Modified;
        db.Entry(model.MediaPlanDateModel).State = EntityState.Modified;
        db.SaveChanges();

        return RedirectToAction("Index");
    }

    return View(model);
}
Sign up to request clarification or add additional context in comments.

2 Comments

Thanks for the response. But currently I'm thinking of abandoning the multi model approach and I will do just one big model which essentially accomplishes the same thing.
That's fine, but don't just throw it all in one entity because it's easier that way; only do so if it makes sense to the application. MediaPlan and MediaPlanDate already seemed to me like something that should have been combined, but ContactInfo seems like something that should on its own, as it can be reused in other scenarios and other relationships with different entities.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.