Why shouldn't use Linq in View - asp.net-mvc

Somewhere I had to use a linq statement for select a result set from my Model that Controller returned in Index ActionResult.
for doing this, I googled "how to use linq in view razor" and I get the result and my application worked properly, but I see some recommendation that say "Don't use Linq in view".
Why we shouldn't use it however it's possible?
And if I don't want using it what's the solution?
This is my query :
#using System.Linq
#{var lst = (from x in item.Showtimes select x.ShowtimeDate).Distinct();}
#foreach (var showTimeItem in lst)
{
<option value="#showTimeItem">#showTimeItem</option>
}
UPDATE
This is my controller Index
public ActionResult Index()
{
MelliConcert.Models.MelliConcertEntities db = new Models.MelliConcertEntities();
var listOfConcerts = (from x in db.Concert
orderby x.ID ascending
select x).Take(15).ToList();
return View(listOfConcerts);
}
And i use it in the view like this :
#model IEnumerable<MelliConcert.Models.Concert>
#foreach (var item in Model)
{
#if (item.OpenedForFirst == true)
{
//...
}
//Some Codes
#using System.Linq
#{var lst = (from x in item.Showtimes select x.ShowtimeDate).Distinct();}
#foreach (var showTimeItem in lst)
{
<option value="#showTimeItem">#showTimeItem</option>
}
}
My linq statement placed in this loop.
What should i do?

There's nothing inherently wrong with using LINQ in a view per se. The problem isn't that you're using LINQ, the problem is that you're doing this:
#{var lst = (from x in item.Showtimes select x.ShowtimeDate).Distinct();}
Any time you have to declare and set a variable inside your view, that's probably an indication that you need to modify your model. Your view model should have a property on it for this purpose. Something like this:
public IEnumerable<SomeType> DistinctShowtimes
{
get
{
return (from x in item.Showtimes select x.ShowtimeDate).Distinct();
}
}
Then the point about LINQ in the view becomes moot, as all you'd need is this:
#foreach (var showTimeItem in Model.DistinctShowtimes)
{
<option value="#showTimeItem">#showTimeItem</option>
}
UPDATE (in response to your updated question):
Now the problem (albeit slightly less of one) is this:
#model IEnumerable<MelliConcert.Models.Concert>
While this works fine, it's limiting. And what you're experiencing is that limitation. You're asking yourself, "How do I return more than one thing to the view?" The answer is to create a custom view model for that view. Right now your view is binding to an enumeration of Concert objects. Which is fine, if that's all it needs. But it turns out that's not all it needs. It has some custom logic that requires a little more. So, you create a custom view model. Something like this:
public class ConcertsViewModel
{
public IEnumerable<Concert> Concerts { get; set; }
// other properties, methods, anything
}
Then in your controller action, you return one of those instead:
public ActionResult Index()
{
using(var db = new Models.MelliConcertEntities())
{
var concertsModel = new ConcertsModel();
concertsModel.Concerts = (from x in db.Concert
orderby x.ID ascending
select x).Take(15).ToList();
return View(concertsModel);
}
}
(Note also the use of the using statement, which should always be employed when making use of IDisposable resources.)
So now your view is still getting the list of Concert objects, but it's packaged in a custom view model onto which you can add any more functionality you need for that view. Next, in your view, change the model declaration:
#model MelliConcert.Models.ConcertsViewModel
(This assumes you put it in the Models namespace. Depending on the scale of your application, you might want to break out view models into their own namespace. I don't often use the Models namespace in the actual application for core business objects, so our projects are likely structured very differently. This should get you going, but you'll want to make sure you keep your concerns cleanly separated.)
Then in the view code you can reference what you need from that object. So if you need to access the list, instead of just calling something like #foreach (var item in model) you would call #foreach (var item in model.Concerts).

I think in this case the argument would be to do the maximum amount of processing on your model before returning it to the view.
So why not return distinct showtimes to the view and then just loop through them?
The only problem with your current set up is it may undermine the spirit of MVC which was to separate concerns. The view shouldn't be applying any logic that needs to be tested (or as little as possible). By keeping logic in the models and controllers you make unit testing easier and the views are simpler for a developer to read.
EDIT
Hey #samangholami, you can return multiple objects to a view using a class. Create a class called "PAGENAMEViewModel" or something similar and create a property for every value you want to return. For example:
public class MovieViewModel
{
public string MovieName { get; set; }
public IEnumerable<string> Actors { get; set; }
public IEnumerable<ShowTimeRecord> Showtimes { get; set; }
public class ShowTimeRecord
{
public string TheaterName { get; set; }
public string TheaterAddress { get; set; }
public DateTime ShowtimeDate{ get; set; }
}
}
Then return your complex model like so:
public ActionResult Index()
{
MovieViewModel model = myMovieHelper.GetMovieData();
return View(model);
}

Besides the possible separation of concerns problem you also might have a performance problem that can be more difficult to diagnose.
If your LINQ query is somehow connected to the database via some ORM or something like that by looping through the results you might create a N+1 problematic scenario.
Moving it off the view might not prevent it, but at least puts it into somewhere that is more visible.

I believe that's because it's not respecting the separation of concerns principle, which is a fundamental concept of MVC. By performing that query in the view, you're taking business logic to it.
It has nothing to do with Linq itself. You could, for example, have a Linq statement to perform an OrderBy. That's ok because it's not business logic, it's a proper view operation (to order data into a table, let's say).

Related

pass dynamic summary queries to an MVC view from an EF model

Translate code in Laravel to asp.net mvc. I am passing a subset of the model and summarizing the fields by year. My EF model has 50+ fields.
This is my Laravel route:
Route::get('/metrics', 'MetricsController#index');
My Laravel Controller
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use App\Http\Controllers\Controller;
class MetricsController extends Controller
{
public function index()
{
$metrics = DB::select('select year(date_id) as Year, Sum(Revenue) from metrics Group By year(date_id)');
return ($metrics);
}
}
I would like to display the model data in a #foreach loop in the view? The dump in the Laravel view displays the data in the following way.
[{"Year":2009,"Sum(Revenue)":"61302670.65"},
{"Year":2011,"Sum(Revenue)":"68397989.00"},
{"Year":2012,"Sum(Revenue)":"69245803.00"},
{"Year":2013,"Sum(Revenue)":"67184051.00"},
{"Year":2014,"Sum(Revenue)":"33445434.00"}]
I will keep it simple, sticking to the fact that you need to show the data in the View. For that, I will assume that you already have your DbContext, Models and data loading parts ready.
Model
I will create a sample model just for the sake of example. But it could, obviously, be more advanced.
public class Result
{
public string Year { get; set; }
public decimal SumRevenue { get; set; }
}
Action Method (in the Controller)
The most important here is the fact that you are loading a "List of" model as result of your query. As you are using EF, you could load it directly from your DbContext or probably use a repository pattern. Since we don't know, I am just making a simple method call so just imagine the DB logic is in there :)
public ActionResult Index()
{
// Here is where you load your data. I am using this line just as example.
List<Result> resultList = RandomLibrary.LoadData();
return View(resultList)
}
View
Remember to prepare your View to receive the List you got in the Action Method.
#model List<Result>
#foreach(var item in Model)
{
<p>
<strong>Year: </strong>#item.Year
<br />
<strong>Sum of revenue: </strong>#item.SumRevenue
</p>
}
This is an oversimplification of the process. You can see it in more details in the official documentation. Or just let us know if you will need a more detailed explanation as your case might be a bit different.

MVC ViewModel Binding Construction vs. Flattening

In my ViewModel (also in my Domain model), I have kinda dynamic Property Structure where the Profile Elements are a List of the base class ProfileVM and refer to a ProfileDefinitionElement (just to explain the ViewModel without pasting the full thing).
public class OwnProfileVM
{
public OwnProfileVM() {}
public ProfileDefinitionVM ProfileDefinitionVM { get; set; }
public ProfileVM ProfileVM { get; set; }
}
So I bind my Properties using a Linq Single statement:
#Model.ProfileDefinitionVM.ProfileElementDefinitions.Single(p => p.Key == ProfileElementKey.CompanyName.ToString()).Title
This works for showing data. But when posting back like this:
#Html.TextBoxFor(model => ((ProfileElementTextVM)model.ProfileVM.ProfileElements
.Single(p=> p.ProfileElementDefinition.Key == ProfileElementKey.CompanyName.ToString()))
.Text
..the model properties are null.
This is because of the parameterless constructor which builds the OwnProfileVM object without any properties filled in.
After some research I found out that there are two ways to solve this:
"Flatten" the ViewModel. So I would have a fixed Property for every Profile Element. This would work, but the disadvantage would be that I couldn't map the data with the Automapper. I would have to fill the ViewModel to the Model "manually". This would result in more Code in the Controller and a "bigger", but simpler ViewModel. Seen in this article
Find a way to pass the Definition data into the ViewModel Constructor to build the list of Properties before posting back.
Now my questions:
Is the second way even possible and if yes, how would this be done? I havent found a way to do this.
If the first question can be answered with yes, which way would you prefer?
Looks complicated. It may be best to simplify it a bit.
In my experience, model properties are null in the controller because the binder cannot understand how to link the form element name with the associated property. For example, I've seen it with lists where foreach has been used:
(model has a) List<Something> Somethings.....
foreach (Something thing in Model.Somethings)
{
#Html.EditorFor(m => thing)
}
This is rendered in the resulting html as <input name="thing"..... which is useless. The solution here is to use a for loop and access the model's properties via their path rather than copying pointers to instances, such as:
for (int i = 0; i < Model.Somethings.Count; i++)
{
#Html.EditorFor(m => Model.Somethings[i])
}
This is then rendered with the correct <input name="Model.Somethings[i]"..... and will be understood by the model binder.
I expect this issue you're facing here is similar. You need to add the necessary accessors to your properties so that the correct names and ids can be rendered in your view and picked up by the binder.
I'm not sure of the exact definition of your class so this example is not likely to be completely right.
This class includes a this[string index] method which will get and set the element using your property key as the index:
public class ProfileElements : List<ProfileElement>
{
public ProfileElement this[string index]
{
get
{
return base.First(p => p.ProfileElementDefinition.Key == index);
}
set
{
base[index] = value;
}
}
}
And in your view, you could use this like:
#Html.TextBoxFor(model => model.ProfileVM.ProfileElements[ProfileElementKey.CompanyName.ToString()].Text)
Hopefully, this will do what you need.

Doesn't nhibernates suggestion of putting everything in a transaction work against the idea of lazy loading?

I have an asp.net-mvc site and i am trying to understand the recommended practice around transactions and lazy loading.
When doing research around implementating nhibernate second level cache, one rule of thumb is around wrapping everything in a transaction (see here and here). I am trying to reconcile this point with how lazy loading works because they seems to be at odds given deferred query execution.
Lets say i have a query that looks like this . .
I have two entities:
Project
Owner
Here is a query in a transaction
public CacheTestViewModel GetCacheTestViewModel()
{
var vm = new CacheTestViewModel();
var session = Repository.Session;
using (var tx = session.BeginTransaction())
{
vm.Projects = Repository.Session.Query<Project>().Cacheable()
tx.Commit();
}
return vm;
}
but in my view I have the following code:
<% foreach (var project in Model.Projects {%>
<% = project.Owner %>
<%} %>
so that queries the Owner object when required due to lazy loading but when reviewing nhibernate profiler, it appears that these queries are happening AFTER the transaction commit is done so wanted to see if that breaks the principle in the first place. It seems like this will always be an issue with any lazy loading (either Fetch().Select() or Fetch().Select().Batch()
What is the recommended practice here?
You should create ViewModels that represent everything that the View needs to be rendered. You should not be issuing database queries from the view if at all possible. To summarize the link above here:
It increase the time that the connection to the database have to be
open. The recommendation is to keep that open only for the duration of
the action, not throughout the lifetime of the request.
It make it
that much harder to understand what are the data requirements for a
particular action is.
When writing views, you shouldn't be bothered
with thinking about persistence, or the number of queries that you
views are generating.
The views are often the most changeable parts in
the application, and having the application issue queries from the
views may result in significant changes to the way the application
data access pattern between revisions.
Most often, queries from the
views result from lazy loading, Select N+1 or similar bad practices.
We strongly recommend that you'll avoid generating queries in the
view, instead, perform all your queries in the action, and provide in
memory access only to the view for them to render themselves.
(Emphasis on the last point mine).
The practical implications of this are that you should not be lazy loading anything in a view. So what should you do instead? This is where the ViewModel layer comes in. You should be fully populating a thin ViewModel with the information you need and then rendering the view with that.
Furthermore, ViewModels shouldn't even contain classes mapped with NHibernate (this appears to be what you're doing in your example).
With all this in mind I would change your current code:
public class CacheTestViewModel
{
public List<ProjectViewModel> Projects { get; set; }
}
public class ProjectViewModel
{
public string Owner { get; set; }
/* etc. */
}
.. And then your code to populate those ViewModels:
public CacheTestViewModel GetCacheTestViewModel()
{
var vm = new CacheTestViewModel();
var session = Repository.Session;
using (var tx = session.BeginTransaction())
{
var projects = Repository.Session.Query<Project>().Cacheable();
foreach (var project in project)
{
vm.Projects.Add(new ProjectViewModel { Owner = project.Owner.Name });
}
tx.Commit();
}
return vm;
}
Now you might be thinking "gee, that's a lot of code to map domain entities to ViewModels. I can see that getting out of hand." You'd be right about that. There's a library called AutoMapper that you can use to semi-automate your mappings.
The recommended practice is to have a view model that is separate from your domain model, and to have one view model per view. For a Projects view, there would be a corresponding ProjectsViewModel class that might look like:
public class ProjectsViewModel
{
public IList<ProjectViewModel> Projects { get; set; }
public class ProjectViewModel
{
public int ProjectId { get; set; }
public string Title { get; set; }
public string OwnerName { get; set; }
}
}
The ProjectsViewModel class would be fully populated in the transaction scope so that no lazy loading is necessary.

Is there a more efficient way to query data and pass as a View Model List to a View?

I have an Item model mapping to the DB like so:
public class Item
{
public int ItemId { get; set; }
public DateTime Created { get; set; }
public string Title { get; set; }
}
To display lists of these items, I have created a ItemSummaryViewModel like so:
public class ItemSummaryViewModel
{
public int ItemId { get; set; }
public string Title { get; set; }
public ItemSummaryViewModel(Item item)
{
this.ItemId = item.ItemId;
this.Title = item.JobTitle + " " + item.Created.ToString("ddd d MMM HH:mm");
}
}
I have also created a class to take a List< Item > and return a List< ItemSummaryViewModels > like so:
public class ItemSummaryViewModelList : List<ItemSummaryViewModel>
{
public ItemSummaryViewModelList(List<Item> items)
{
foreach (Item i in items)
{
ItemSummaryViewModel itemSummary = new ItemSummaryViewModel(i);
this.Add(itemSummary);
}
}
}
Finally, we use the controller to pass the list into the View like so:
public ActionResult Index()
{
//IEnumerable<ItemSummaryViewModel> itemsummaries = new IEnumerable<ItemSummaryViewModel>();
List<Item> ListOfItems = db.Items.ToList();
ItemSummaryViewModelList ListOfItemViewModels = new ItemSummaryViewModelList(ListOfItems);
return View(ListOfItemViewModels);
}
My Questions Are:
Is there a more efficient or "best practice" way of doing this?
To transform the list of DB models (Item) into a list of displayable View Models (ItemSummaryViewModels), we currently iterate through each item in the list and transform them individually. Is there a more efficient way of doing this ?
Essentially we're querying the DB and assigning the data to a ViewModel for display as a list. I can't help feeling that I'm "going round the houses" a bit and that there might be a more efficient or "best practice way of doing this.
Is there a better way?
Thanks
Try using LINQ select:
List<ItemSummaryViewModel> results = items.Select(
x =>
new ItemSummaryViewModel
{
ItemId = x.ItemId,
Title = x.Title + " " + x.Created.ToString("ddd d MMM HH:mm")
}).ToList();
Put that list in your view model.
Regarding efficiency, I would not worry until you have found that the simplest to implement solution was overly slow in practice. Get it working first and then only optimise when actually necessary. Obviously in the example you give, there are opportunities to only query and convert the subset of Items that the view requires (may it is all, but maybe you are paging?)
Structurally, I think the academic and professionally correct answer would be to have one set of objects to represent your database entities, a second set to represent the "domain" or business objects, and a third set to represent the all of the MVC models. However, depending the exact scenario this could be simplified:
If there is a really close mapping between the business objects and the database entities, and it is very unlikely that the database is going to change significantly, then you could have a single class for both.
If you have a very simple set of views that map very cleanly onto your business objects, then perhaps you can use business objects as your models. Unless your views do nothing but splat raw business objects onto a web page, I think your models will normally need to be more complicated than your current example though.
For that specific case, I would agree with #CorrugatedAir and say you could just use a plain List rather than create your own List class, and if want to be simpler, you could just use List and skip creating the ItemSummaryViewModel class too.
But try and be consistent throughout the application - so if you find a situation where your database entities can't be used as business objects, then it is best to have a separate set in all instances and have mappers between them.
To answer the "best practice" part of your question:
More efficient way (architecturally) will be to use Unit of Work and the repository patterns. That way you decouple your views from your data source, making it more reusable, more testable, more readable, hence more maintainable along with other "more"s.
The article is very graphical and gives you real feel of why do you need to tear apart database access from the controller.
To answer the technical part of how to transform it in a less verbose way,
I'd use something called AutoMapper. Using it, your complex transformation instead of the loop you presented will look as something like this:
public ActionResult Index()
{
var dbList = db.Items.ToList();
var vmList = Mapper.Map<List<Item>, List<ItemSummaryViewModel>>(dbList);
return View(vmList);
}
You will also have to put this initialization somewhere in your App_Start configuration (if MVC 4) or in Global.asax.cs file:
Mapper.CreateMap<ListOfItems , ItemSummaryViewModelList>();
Mapper.AssertConfigurationIsValid();
You can read more about why use AutoMapper and how to use it AutoMapper: Getting Started
Hope this helps!

Can you remove the HTML Field Prefix from strongly typed models in MVC 3?

I have a view model like this:
public class EditVM
{
public Media.Domain.Entities.Movie Movie { get; set; }
public IEnumerable<Genre> Genres { get; set; }
}
Movie is the real entity I wish to edit. Genres is simply present to populate a drop down. I would prefer that when I call:
#Html.TextBoxFor(m => m.Movie.Title)
inside my strongly typed view that the input control have a name = "Title" instead of "Movie.Title"
I do not wish to split my view into partial views or lose my strongly typed view by using ViewData or the like.
Is there a way to express to the View that I do not wish to have the Movie. prefix? I noticed that you can set:
ViewData.TemplateInfo.HtmlFieldPrefix = "x";
in the controller, but unfortunately it seems only to allow adding an additional prefix. Setting it to "" does nothing.
Is there any work around for this? Or am I stuck with the unfortunate prefix that isn't really necessary in this case if I wish to keep strongly typed views and lambdas?
Thanks for any help.
Update:
Here's the controller actions to maybe make things a bit clearer.
public ActionResult Edit(int? id)
{
var vm = new EditVM
{
Movie = id.HasValue ? _movieSvc.Find(id.Value) : new Movie(),
Genres = AppData.ListGenres()
};
return View(vm);
}
[HttpPost]
public void Edit([Bind(Prefix = "Movie")]Movie m)
{
_movieSvc.AddOrUpdateMovie(m); //Exceptions handled elsewhere
}
No, in order to do what you want you would have to rewrite the Html helpers, and then you would have to write your own model binder. Seems like a lot of work for minimal gain.
The only choice is a Partial view in which you pass the Movie object as the model. However, this would require you to write your own model binder to have it be recognized.
The reason you have to do m.Movie.Title is so that the ID has the correct name, so the model binder can recognize it as a member of your model.
Based on your update:
Your options are:
Use non-strongly typed helpers.
Use a partial view.
Rewrite the stronly typed helpers
Don't use the helpers at all, and write the values to the HTML
Personally, i'd just use 1 or 2, probably 2.
EDIT:
Based on your update above. Change your code to this (note, Genres does not get posted back to the server, so m.Genres will just be null on postback):
[HttpPost]
public void Edit(EditVM m)
{
_movieSvc.AddOrUpdateMovie(m.Movie); //Exceptions handled elsewhere
}
EDIT:
I did just think of an alternative to this. You could simply do this:
#{ var Movie = Model.Movie; }
#Html.TextBoxFor(m => Movie.Title)
However, if there was a validation error, you would have to recreate your EditVM.
I have a view model like this
I think that you might have some misunderstanding about what a view model is. A view model shouldn't contain any reference to your domain models which is what those Movie and Genre classes seem to be. I mean creating a new class that you suffix with VM and in which you stuff all your domain models as properties is not really a view model. A view model is a class that is specifically designed to meet the requirements of your view.
A much more correct view model would looks like this:
public class EditVM
{
public string MovieTitle { get; set; }
public IEnumerable<GenreViewModel> Genres { get; set; }
}
and in your view you would have:
#Html.EditorFor(x => x.MovieTitle)
#Html.EditorFor(x => x.Genres)
Another option is to either use the TextBox(string name, object value) overload instead of the TextBoxFor:
#Html.TextBox("Title", Model.Movie.Title)
You could also specify the input tag HTML instead of using a helper.
Another option is to take EditVM as your postback parameter. This is what I would do. My post action parameter is always the same type of the .cshtml model. Yes there will be properties like lists that are null, but you just ignore those. It also allows you to gracefully handle post errors as well because if there is an error you'll need to return an instance of that view model anyhow, and have the values they submitted included. I usually have private methods or DB layer that handles retrieving the various lists that go into the ViewModel, since those will be empty on postback and will need to be repopulated, while not touching the properties that were in the post.
With your post method as it is now, if you need to return the same view, you've gotta create a new EditVM and then copy any posted values into it, and still populate the lists. With my method, you eliminate one of those mapping steps. If you are posting more than one thing, are you going to have umpteen different parameters on your post action? Just let them all come naturally into a single parameter typed to the EditVM of the View. While maybe having those null properties in the VM during the postback feels icky, you get a nice predictable consistency between View and postback IMO. You don't have to spend alot of time thinking about what combination of parameters on your post method will get you all the pieces of data from the form.

Resources