4

I have a linq query (in Entity Framework Core) that joins two sql server tables, filters the results in the where clause, and then selects a set of columns from the results.

var resultsObj = (from rd in _db.ResData
                                  join ra in _db.ResAvailability on rd.RecNo equals ra.RecNoDate
                                  where ra.TotalPrice < Int32.Parse(resDeals.priceHighEnd) && ra.TotalPrice > Int32.Parse(resDeals.priceLowEnd)

                                  select new
                                  {
                                      Name = rd.Name,
                                      ImageUrl = rd.ImageUrl,
                                      ResortDetails = rd.ResortDetails,
                                      CheckIn = ra.CheckIn,
                                      Address = rd.Address,
                                      TotalPrice = ra.TotalPrice
                                  });
                ViewBag.resultSet = resultsObj;

I've created a viewmodel class to store the attributes a row of the query results and another viewmodel class which contains a List object to store all instances of the other viewmodel, essentially functioning as a collection for all of the rows returned from the query:

public class ResortDataJoinObj
{
    public ResortData ResData { get; set; }
    public ResortAvailability ResAvail { get; set; }
}

public class ResortDealResultsObject
{
    public List<ResortDataJoinObj> resultsList { get; set; }
}

Once the List has been created, I need to essentially import it into a view page so that the query results can be stored in a table. I've done this process of obtaining query results before, except with stored procedures where I was able to iterate through the reader object and store the returned row data as necessary. I know that it's standard practice to pass data from the controller to the view using Viewmodels, but I'm still new at using linq queries so I'm not sure how to go about actually extracting data returned from the linq query results and binding it to viewmodels.

How can I go about extracting the data from the linq query results and storing it in the viewmodel classes?

loremIpsum1771
  • 2,497
  • 5
  • 40
  • 87
  • 1
    You need the controller to handle it, and you need to declare a @model in your view (at the top). See: https://stackoverflow.com/questions/24469192/mvc-viewmodel-example – AussieJoe May 09 '18 at 16:13
  • 1
    Possible duplicate of [MVC ViewModel example](https://stackoverflow.com/questions/24469192/mvc-viewmodel-example) – AussieJoe May 09 '18 at 16:23
  • 1
    @AussieJoe I took a look at the answer in that post and it doesn't really seem to be solving the issue here. In that answer, the code provided simply assigns the properties returned from the query to a viewmodel object, but only one time. It doesn't iterate over the entire collection to extract each "row" and then do the assignment. Wouldn't you have to iterate over the entire collection to extract the data from each returned row. – loremIpsum1771 May 09 '18 at 19:30
  • 1
    You need to make use of the "model" in your view. Re-look at the example carefully and notice the foreach loop, in the HTML. You are trying to re-invent the wheel, per say. This is MVC, it's already handled for you. It's literally that easy :) – AussieJoe May 09 '18 at 19:34

2 Answers2

3

How I would do this, would be by creating a class object that will have all of the properties that you want from the returned linq query. -

So the process is, we do the linq query and get resultsObj We then will have a new class call ViewModelOfPage (what we will be returning to the page) which will have all the properties that we want to return.

Our class will look like -

public class ViewModelOfPage
{

public string Name {get;set;}
public string ImageUrl{get;set;}
public string ResortDetails {get;set;}
public string CheckIn {get;set;}
public string Address {get;set;}
public int TotalPrice {get;set;} 

// this may be a double in your example or w/e value you want it set to.                                   
}

we will then create this class object in our controller, initiliaze it with the values from the obj that we returned with the linq query, and then send this object to the page where we can bind its properties in the view.

so in our controller,we have just completed the linq query and have our resultsObj object.

we will then initialize our viewmodel like :

ViewModelOfPage vm = new ViewModelOfPage
{

Name = resultsObj.Name,
ImageUrl = resultsObj.ImageUrl ,
ResortDetails = resultsObj.ResortDetails,
CheckIn = resultsObj.CheckIn,
Address = resultsObj.Address,
TotalPrice resultsObj.TotalPrice

}

return View("ourView",vm);

Hope this helps explain the process and this is how I would try and do this. If there are better ways please explain below :)

Just FYI all this is my first post, so I am writing the code in the incorrect way most likely so I do apologise to everyone for the pain they may be in reading it :D hope you are all having a great tuesday.

If you would like to return all the rows, to the page, you would do :

List<ViewModelOfPage> vm = new ViewModelOfPage;
foreach (var row in resultsObj)
{
    //An example of only selecting certain results
    if(row.Name == "John" && row.TotalPrice > 15){
        var tempVm = new ViewModelOfPage
        {
            Name = row.Name,
            ImageUrl = row.ImageUrl ,
            ResortDetails = row.ResortDetails,
            CheckIn = row.CheckIn,
            Address = row.Address,
            TotalPrice row.TotalPrice
        };
        vm.add(tempVm);
    }
}
view("ourview",vm);
DJW1991
  • 46
  • 1
  • 4
  • 1
    Thanks for answering. This seems like it work if the query results only returned one row since you are directly accessing he attributes of the resultsObj instead of iterating through it.Do you know how the entire ***collection*** of rows returned from the query can be stored? – loremIpsum1771 May 09 '18 at 19:26
  • 1
    You would put it as a list of the ViewModelOfPage and then return it to the page as an ienumerable model in the view so the first line of your view would be @model IEnumerable you can then in your view, loop over, and do foreach model in view create a new div, containing the relevant information and style it etc – DJW1991 May 09 '18 at 19:31
  • 1
    Yeah, its called Model, in the View. You can iterate (in Razor syntax) like so: @foreach (var p in Model.MyModelClass) { } – AussieJoe May 09 '18 at 19:33
  • 1
    So within the controller action, you only need to do the assignment from the resultsObj one time? Why don't you have to iterate through the resultsObj, instantiate ViewModelOfPage with values from resultsObj, insert that instance to a collection, and then pass that collection to the view? – loremIpsum1771 May 09 '18 at 19:35
  • 1
    Once you pass the model to the view, you no longer need to pass it. It's now in your View Model and can be accessed as so. it is a collection WITHIN YOUR VIEW, hence the name VIEW MODEL. – AussieJoe May 09 '18 at 19:36
  • I get how to iterate through the collection once it is in the view, I just had confusion when it came to populating the viewmodel in the controller action. – loremIpsum1771 May 09 '18 at 19:36
  • The method I have added will take a collection of objects through the page, I like this method usually as for example if we only care about records where the Name property equals John we can an if statement before hand (will update my code in the answer). – DJW1991 May 09 '18 at 19:54
3

Since you did not provide any HTML, let me help from another SO answer:

@model IList<MySolution.Models.MyClassEtc>

@{
    ViewBag.Title = "Home Page";
    Layout = "~/Views/Shared/_LayoutNoMenu.cshtml";
}


@foreach (var p in Model)
{ 
<div class="container">
@using (Html.BeginForm("About", "Home", FormMethod.Get, new { @class = "begin-form" }))
{
    <h1>Welcome</h1>
    <div class="required-field-block">
    <textarea rows="1" class="form-control" placeholder="Email" id="filter"></textarea>
    </div>
    <button class="btn btn-primary" type="submit">Login</button>

    @Html.TextBoxFor(model => model.ImageUrl, new { @class = "form-control" })
}
</div>
} 

You need to define a View Model in your page at the top.

@model IList<MySolution.Models.MyClassEtc>

And you need to access and iterate it, like so:

@foreach (var p in Model)
{ 
    Debug.Write(p.Name);
    Debug.Write(p.ImageUrl); //etc
}

It's really great it's this easy, isn't it?

AussieJoe
  • 1,285
  • 1
  • 14
  • 29