Stefan Piskorski
Stefan Piskorski

Reputation: 105

DB ViewModel Data not Displaying in MVC View

New to C# and MVC, so probably an obvious error: My Controller logic does not send any db items to the View. I don't know what I'm doing wrong.

I am building a multiple choice online test and have two db entitiy classes

public partial class Answer
{
    public int ID { get; set; }
    public string CorrectAnswer { get; set; }
    public string Foil1 { get; set; }
    public string Foil2 { get; set; }
    public string Foil3 { get; set; }
}


public partial class Question
{
    public int ID { get; set; }
    public string Text { get; set; }
    public string Level { get; set; }
    public string GrammarPoint { get; set; }
}

I want to display questions and answer options in a View so I have this ViewModel

public class ExamViewModel
{
    public string CorrectAnswer { get; set; }
    public string Foil1 { get; set; }
    public string Foil2 { get; set; }
    public string Foil3 { get; set; }
    public string Text { get; set; }
}

This is the Controller.

public ActionResult TakeTest()
{
    ActiveTestEntities db = new ActiveTestEntities();
    ExamViewModel exam = new ExamViewModel();
    exam.Text = db.Questions.FirstOrDefault(t => t.ID == 1).ToString();
    exam.Foil1 = db.Answers.FirstOrDefault(t => t.ID == 1).ToString();
    exam.Foil2 = db.Answers.FirstOrDefault(t => t.ID == 1).ToString();
    exam.Foil3 = db.Answers.FirstOrDefault(t => t.ID == 1).ToString();
    exam.CorrectAnswer = db.Answers.FirstOrDefault(t => t.ID == 1).ToString();

    return View(exam); 
}

The LINQ is supposed to send db items to this View:

 @model AccessEsol.Models.ExamViewModel

@{
     ViewBag.Title = "TakeTest";
}

<h2>TakeTest</h2>

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

<fieldset>

<div class="display-label">
    <h3>Click the correct answer:</h3>
</div>

<div class="display-field">

    @Html.DisplayFor(model => model.Text )
</div>

<input id="Checkbox1" type="checkbox" />
@Html.DisplayFor(model => model.Foil1)

<input id="Checkbox1" type="checkbox" />
@Html.DisplayFor(model => model.Foil2)

<input id="Checkbox1" type="checkbox" />
@Html.DisplayFor(model => model.CorrectAnswer )


<input id="Checkbox1" type="checkbox" />
@Html.DisplayFor(model => model.Foil3)

<p>
    <input type="submit" value="Submit Answers" />
</p>

</fieldset>
}

But beside each Checkbox displays AccessEsol.Models.Answer only, the db items have not been retrieved.

Much appreciated if you can tell me what I am doing wrong.

Upvotes: 0

Views: 1326

Answers (3)

Matt Bodily
Matt Bodily

Reputation: 6423

since you have a question and view class defined I would use them in your view model instead of redefining the fields

public class ExamViewModel
{
    public Answer Answer{ get; set; }
    public Question Question { get; set; }
}

then on your view you will use it as

 @Html.DisplayFor(x => x.Answer.Foil1)

On your controller you define the object but I don't see where you are calling the database. put a break point on your controller and make sure you database object has the data you are wanting to send to the view

Upvotes: 1

Grundy
Grundy

Reputation: 13381

you use linq wrong

db.Answers.FirstOrDefault(t => t.ID == 1)

this return object Answer but you need for example Answer.Foil1

so try change your code like this

 ExamViewModel exam = (from q in db.Questions
                      from a in db.Answers
                      where q.ID == 1 && a.ID==1
                      select new ExamViewModel{
                          Foil1 = a.Foil1,
                          Foil2 = a.Foil2,
                          Foil3 = a.Foil3, 
                          CoorectAnswer = a.CorrectAnswer,
                          Text = q.Text
                      }).FirstOrDefault()

Upvotes: 1

David
David

Reputation: 218808

When you call .ToString() on an object, the output is the type name of that object. That's why you're seeing:

AccessEsol.Models.Answer

That is the result of calling .ToString() on any object of type AccessEsol.Models.Answer.

What are those objects? Instead of trying to convert them to strings, use the objects themselves. So your view model would be more like this:

public class ExamViewModel
{
    public Answer CorrectAnswer { get; set; }
    public Answer Foil1 { get; set; }
    public Answer Foil2 { get; set; }
    public Answer Foil3 { get; set; }
    public Question Text { get; set; }
}

And then you'd just remove the calls to .ToString() in your controller:

exam.Text = db.Questions.FirstOrDefault(t => t.ID == 1);
exam.Foil1 = db.Answers.FirstOrDefault(t => t.ID == 1);
exam.Foil2 = db.Answers.FirstOrDefault(t => t.ID == 1);
exam.Foil3 = db.Answers.FirstOrDefault(t => t.ID == 1);
exam.CorrectAnswer = db.Answers.FirstOrDefault(t => t.ID == 1);

Then in your view, you'd access the actual properties on those objects. I don't know what those properties are called, so this is a guess, but it might be something like:

@Html.DisplayFor(model => model.Text.QuestionText)

This assumes that there's a property called QuestionText on the Question object which holds its text. Whatever that property is actually called in your code, just use that.

You could make your existing code work by overriding .ToString() on your Question and Answer objects to output the desired strings. But that's not really a common approach and would needlessly complicate the code. The best approach is to just use the models you have, and keep them as their actual model types instead of trying to convert them to strings.

This isn't even necessarily an MVC issue, this is just plain C# code. Prefer strongly-typed objects over serialized strings.

Upvotes: 0

Related Questions