3

I'm writing a simple tutorial app for my C# MVC class and our professor has us using some god awful in memory system using session and Lists because he hasn't started teaching entity framework yet

the problem I'm running into is that when i am trying to bind a dropdown list to my model it is giving me an unexpected result.

the code im using in the view is:

<div class="editor-label">
    @Html.LabelFor(model => model.Author)
</div>
<div class="editor-field">
    @Html.DropDownListFor(m => m.Author, new SelectList(new Lab2Repository().GetAuthors(), "Id", "Name"), "---- Select Category ----")
    @Html.ValidationMessageFor(m=>m.Author)
</div>
<div class="editor-label">
    @Html.LabelFor(m=>m.Category)
</div>
<div class="editor-field">
    @Html.DropDownListFor(m => m.Category, new SelectList(new Lab2Repository().GetCategories(), "Id", "Name"), "---- Select Category ----")
    @Html.ValidationMessageFor(m => m.Category)

</div>

and when i run that code it give a validation error no matter what is chosen enter image description here

Repository Code:

using System;
using System.Linq;
using System.Collections.Generic;
using Int422.Lab2.Data;
using System.Web;
using Int422.Lab2.Models;


public class Lab2Repository
{
    public Lab2Context Data { get; set; }
    public Lab2Repository()
    {
        Data = (Lab2Context)HttpContext.Current.Application["State"];
    }

    #region Retrieval

    public IEnumerable<Book> GetBooks()
    {
        return Data.Books;
    }
    public IEnumerable<Author> GetAuthors()
    {
        return Data.Authors.Select(obj => new Author(obj.Name) 
        { 
            Id = obj.Id, 
            Books = GetBooks().Where(b=>b.Author.Id == obj.Id).ToList() 
        });
    }

    public IEnumerable<Category> GetCategories()
    {
        return Data.Categories.Select(obj => new Category(obj.Name, obj.Description) 
        {  
            Id = obj.Id, 
            Books = GetBooks().Where(b=>b.Category.Id == obj.Id).ToList(),
        });


    } 
    #endregion
    #region Singles
    public Author GetAuthor(int id)
    {
        return GetAuthors().Where(obj => obj.Id == id).Single();
    }

    public Category GetCategory(int id)
    {
        return GetCategories().Where(obj => obj.Id == id).Single();
    }

    public Book GetBook(int id)
    {
        return Data.Books.Where(obj => obj.Id == id).Single();
    }
    #endregion
    #region Edit
    public void Edit(int id, Category rec)
    {
        if (id != rec.Id)
            throw new InvalidOperationException("Id Mismatch during edit process");

        int index = Data.Categories.IndexOf(Data.Categories.Single(p => p.Id == id));
        if (index == -1)
            throw new InvalidOperationException("Record not found no record to edit");
        Data.Categories[index] = rec;
        SaveChanges();

    }
    public void Edit(int id, Book rec)
    {
        if (id != rec.Id)
            throw new InvalidOperationException("Id Mismatch during edit process");

        int index = Data.Books.IndexOf(Data.Books.Single(p => p.Id == id));
        if (index == -1)
            throw new InvalidOperationException("Record not found no record to edit");
        Data.Books[index] = rec;
        SaveChanges();
    }


    public void Edit(int id, Author rec)
    {
        if (id != rec.Id)
            throw new InvalidOperationException("Id Mismatch during edit process");

        int index = Data.Authors.IndexOf(Data.Authors.Single(p => p.Id == id));
        if (index == -1)
            throw new InvalidOperationException(String.Format("Record id {0} not found", id));
        Data.Authors[index] = rec;
        SaveChanges();

    }
    #endregion
    #region Create
    //
    public int Create(Category m)
    {
        m.Id = Data.Categories.Max(o => o.Id) + 1;
        Data.Categories.Add(m);
        SaveChanges();
        return m.Id;

    }

    //adds new author to the collection returns the id 
    public int Create(Author m)
    {
        m.Id = Data.Authors.Max(o => o.Id) + 1;
        Data.Authors.Add(m);
        SaveChanges();
        return m.Id;
    }

    public int Create(Book m)
    {
        m.Id = Data.Authors.Max(o => o.Id) + 1;
        Data.Books.Add(m);
        SaveChanges();
        return m.Id;
    }
    #endregion
    #region Delete

    public void Delete(Author rec)
    {
        Data.Authors.Remove(Data.Authors.Single(o => o.Id == rec.Id));
        SaveChanges();
    }

    public void Delete(Category m)
    {
        Data.Categories.Remove(Data.Categories.Single(o => o.Id == m.Id));
        SaveChanges();
    }

    public void Delete(Book m)
    {
        Data.Books.Remove(Data.Books.Single(o => o.Id == m.Id));
        SaveChanges();
    }





    #endregion
    #region Session Persistance

    public void SaveChanges()
    {
        //save to session 
        HttpContext.Current.Application["State"] = Data;
        //refresh repo state from session 
        Data = (Lab2Context)HttpContext.Current.Application["State "];
    }

    #endregion




}

Models:

    public class Author
    {
        public Author(string name)
        {
            Name = name;
        }
        public Author()
        {
            Books = new List<Book>();
        }
        public int Id { get; set; }
        public string Name { get; set; }
        public virtual ICollection<Book> Books { get; set; }

    }

public class Category
    {

        public Category(string name, string desc)
        {
            Name = name;
            Description = desc;
            Books = new List<Book>();
        }
        public Category()
        {

        }
        public int Id { get; set; }
        public string Name { get; set; }
        public string Description { get; set; }
        public virtual ICollection<Book> Books  { get; set; }

    }

public class Book
    {
        public Book(string title, Author author, Category category, double retailPrice, double cost)
        {
            Title = title;
            Author = author;
            Category = category;
            RetailPrice = retailPrice;
            Cost = cost; 
        }
        public Book()
        {

        }

        public int Id { get; set; }
        public Category Category { get; set; }
        public Author Author { get; set; }
        public string Title { get; set; }
        public double RetailPrice { get; set; }
        public double Cost { get; set; }

    }

Controller:

public partial class BooksController : Controller
    {
        private Lab2Repository repo = new Lab2Repository();

        //
        // GET: /Books/

        public virtual ViewResult Index()
        {
            return View(repo.GetBooks());
        }

        //
        // GET: /Books/Details/5
        [GET("Books/{id:int}")]
        public virtual ActionResult Details(int id)
        {
            Book book = repo.GetBook(id);
            return View(book);
        }

        //
        // GET: /Books/Create

        public virtual ActionResult Create()
        {
            return View();
        } 

        //
        // POST: /Books/Create

        [HttpPost]
        public virtual ActionResult Create(Book book)
        {
            if (!ModelState.IsValid)
                return View(book);

            repo.Create(book);

            return RedirectToAction(MVC.Books.Index());

        }

        //
        // GET: /Books/Edit/5

        public virtual ActionResult Edit(int id)
        {
            Book book = repo.GetBook(id);
            return View(book);
        }

        //
        // POST: /Books/Edit/5

        [HttpPost]
        public virtual ActionResult Edit(int id, Book book)
        {
            if (!ModelState.IsValid)
                return View(book);

            repo.Edit(id, book);

            return RedirectToAction(MVC.Books.Details(id));
        }

        //
        // GET: /Books/Delete/5

        public virtual ActionResult Delete(int id)
        {
            Book book = repo.GetBook(id);
            return View(book);
        }

        //
        // POST: /Books/Delete/5

        [HttpPost, ActionName("Delete")]
        public virtual ActionResult DeleteConfirmed(int id)
        {
            Book book = repo.GetBook(id);
            repo.Delete(book);

            return RedirectToAction("Index");
        }


    }

Initializer:

public class Lab2StoreInitializer
{
public void Seed(Lab2Context c)
{

    //add categories 
    var cat_Programming = new Category("Programming", "Books about writing computer code and developing software") { Id = 1 };
    var cat_Politics = new Category("Politics", "books with politically charged subjects") { Id = 2 };
    var cat_NonFiction = new Category("Non-Fiction", "Books about real life events") { Id = 3 };


    c.Categories.Add(cat_Programming);
    c.Categories.Add(cat_Politics);
    c.Categories.Add(cat_NonFiction);


    //add authors 
    var noamChomsky = new Author("Noam Chomsky") { Id = 1};
    var howardZinn = new Author("Howard Zinn") { Id = 2 };
    var scottHanselman = new Author("Scott Hanselman") { Id = 3 };
    var philHaack = new Author("Phil Haack") { Id = 4 };
    var danielleSteele = new Author("Danielle Steele") { Id = 5 };
    var johnSkeet = new Author("John Skeet") { Id = 6 };
    var johnResig = new Author("John Resig") { Id = 7 };
    var scottKlein = new Author("Scott Klein") { Id = 8 };


    c.Authors.Add(noamChomsky);
    c.Authors.Add(howardZinn);
    c.Authors.Add(scottHanselman);
    c.Authors.Add(philHaack);
    c.Authors.Add(danielleSteele);
    c.Authors.Add(johnSkeet);
    c.Authors.Add(johnResig);
    c.Authors.Add(scottKlein);

    // add books 

    c.Books.Add(new Book("Manufacturing Consent", noamChomsky, cat_Politics, 16.57, 12.00) { Id = 1 });
    c.Books.Add(new Book("The Zinn Reader", howardZinn, cat_Politics, 35.75, 30.00) { Id = 2 });
    c.Books.Add(new Book("C# In Depth", johnSkeet, cat_Programming, 29.99, 25.00) { Id = 3 });
    c.Books.Add(new Book("Professional ASP.NET MVC 2", scottHanselman, cat_Programming, 34.99, 30.00) { Id = 4 });
    c.Books.Add(new Book("Professional MVC 4", philHaack, cat_Programming, 49.99, 45.00) { Id = 5 });
    c.Books.Add(new Book("His Bright Light: The Story of Nick Traina", danielleSteele, cat_NonFiction, 19.99, 14.00) { Id = 6 });
    c.Books.Add(new Book("Secrets of the JavaScript Ninja", johnResig, cat_Programming, 34.99, 29.99) { Id = 7 });
    c.Books.Add(new Book("Professional LINQ", scottKlein, cat_Programming, 39.99, 35.00) { Id = 8 });
    c.Books.Add(new Book("jQuery in Action", johnResig, cat_Programming, 49.99, 43.00) { Id = 9 });


    HttpContext.Current.Application["State"] = c;
    //base.Seed(c);
}
}
Simon Whitehead
  • 63,300
  • 9
  • 114
  • 138
Chris McGrath
  • 1,727
  • 3
  • 19
  • 45

2 Answers2

5
@Html.DropDownListFor(m => m.Author.Id, new SelectList(new Lab2Repository().GetAuthors(), "Id", "Name"), "---- Select Category ----")
Dmitry Efimenko
  • 10,973
  • 7
  • 62
  • 79
  • this sort of worked but not really in the way i want im not using entity framework so i don't get the full object but this is workable for the time being – Chris McGrath Jan 30 '13 at 09:17
0

The SelectList is using the Id property of the Author to identify it within a Book. However, your Book model has it defined as:

public Author Author { get; set; }

The default model binder won't know how to associate the Id with an Author. To fix this, you can declare it like so:

public int Author { get; set; }

Since Id and Author are now the same data type (int), it will work. The same goes for Category:

public int Category { get; set; }

Your alternative is to write your own model binder and register it with MVC. Then you can force it to load an entire Author object when only an int is passed through from the dropdown list. See this question for some suggestions with that: ASP.Net MVC Custom Model Binding explanation

Community
  • 1
  • 1
Simon Whitehead
  • 63,300
  • 9
  • 114
  • 138
  • I am specifically trying to avoid having to change my navigation properties to an int. if i wanted to do that i would have created a view model to process this but in my experience this shouldnt be needed – Chris McGrath Jan 28 '13 at 01:21
  • 2
    @ChrisMcGrath I have provided an alternative.. a custom model binder. You didn't mention that changing the data types was not something you wanted to do. – Simon Whitehead Jan 28 '13 at 01:22