model size check - asp.net

I've had some success passing query results to my view.
Unfortunately the ASP/Razor code balks in the foreach when the resulting model list has 0 entries.
#foreach (var action in (List<LemonTrader.Models.Lemon>)ViewData["lemons"]) {
<tr>
<td>
#Html.Encode( action.acidity)
If there are no entries it says something about a null exception.
What is the best way to handle the case where the list is empty?
I guess I could put a code block in and have it do an if/then branch. This seems to deviate a bit from the elegant razor one-liner of #foreach.
I guess I could put blank stuff in the controller and then just display something blank.
Those don't seem like very elegant approaches.
Any better ideas?

Try do next:
Create additional model (viewmodel) in Models folder (for example LemonsView.cs) and put there:
public class LemonList
{
public IQueryable<Lemon> AllLemons { get; set; }
}
Create a controller (LemonController.cs)
public ActionResult Lemons
{
var model = new LemonList();
var lemons = db.Lemon;
model.AllLemons = lemons;
return View(model);
}`
In View:
#using LemonTrader.Models.AllLemons
foreach(var item in Model.LemonList){
#item.Some
}
If in result you will have null, it will be a blank page
Have fun!

Related

Too many select lists polluting my controller actions

I have an ASP.Net MVC application and some of the controller actions have about 10 select lists (others have more). Currently my code looks like this:
public ActionResult Edit(int carId)
{
CreateCar model = new CreateCar();
model.Makes = Helper.Makes();
model.Models = Helper.Models();
model.Colors = Helper.Colors();
model.EngineSizes = Helper.EngineSizes();
model.Materials = Helper.Materials();
model.FuelTypes = Helper.FuelTypes();
model.WheelSizes = Helper.WheelSizes();
model.BodyTypes = Helper.BodyTypes();
//more select lists below this
return View(model)
}
In my views i setup select lists like this:
#Html.DropDownListFor(x => x, Model.Makes)
I have code that looks like this in a number of actions and i feel there is a better way of doing this so my actions are not polluted with these select lists.
The only option i can think of to get around this is to actullly call the Helper class in the views e.g.
#Html.DropDownListFor(x => x, Helper.Makes())
Is this approach considered bad practice and are there any other approaches to deal wi this issue?
Generally, yes, it's bad practice to do things like that in your view. The controller is responsible for wiring everything up, so your code should go there. Now, it depends a lot on what your Helper class is doing. If the select lists are just generated via some bit of code, it's probably not that bad for that to happen in the view, but what you don't want to be doing is issuing database queries while the view is being rendered. If your helper is interacting with a database, then keep it in the controller.
That said, what is the real issue here? Sure that's a lot of select lists, but I wouldn't go so far as to say it's "polluting" your action. It's very clear what your doing. The action is responsible for creating the model for your view, and that's what it's doing. Just because there may be a lot of lines in your code, doesn't necessarily mean it's "bad" or "wrong".
However, if you're repeating this in a lot of places, I would recommend factoring it out into a private or protected method on your controller. For example:
public ActionResult Edit(int carId)
{
...
PopulateSelectLists(model);
return View(model);
}
[HttpPost]
public ActionResult Edit(Foo model, int carId)
{
...
PopulateSelectLists(model);
return View(model);
}
private void PopulateSelectLists(Foo model)
{
model.Makes = Helper.Makes();
model.Models = Helper.Models();
model.Colors = Helper.Colors();
model.EngineSizes = Helper.EngineSizes();
model.Materials = Helper.Materials();
model.FuelTypes = Helper.FuelTypes();
model.WheelSizes = Helper.WheelSizes();
model.BodyTypes = Helper.BodyTypes();
//more select lists below this
}
Then, everything is clean and tidy.

2 Different Models Need to Utilize Same View

Let me premise this by saying I am transitioning from Classic ASP to .net so my .net knowledge is all through books, not use. I am working on a shopping cart platform and have been tasked with creating 3 different display types (horizontal slider, vertical slider and grid) that will be able to be utilized by 2 different models.
I got the new views to work with my first model and thought I was in the clear and plugged in the second, but then I started getting an error about the models not being the same. I am trying to figure out what the best way to handle this. The solutions I have come up with are
Using A View Model - this seems like it is supposed to only be used when you need to combine 2 models into one and that is really the opposite of what I need, I need to make 1 view work for 2 models.
Make separate views for each type. This seems logical but is going to result in 5 new view files that are almost identical to those I have already created, it seemed redundant.
Use renderPartial and convert the models - not really sure how I would even accomplish this, but my though was to renderpartial out the creation of the actual displays (horizontal slider, vertical slider and grid view)
I am having a hard time determining what the best course of action is. I know that with MVC and .net you are supposed to never code the same thing twice and I think making the separate views is doing exactly that. Any thoughts on how to best approach this would be greatly appreciated.
This is the way I'm doing it right now on my project, I don't know if it's the best way or not but for me works fine.
Basically on my website's homepage I need to have to 2 different models to handle 2 forms posting to the same Method in the controller
namespace Website.Models.ViewModels
{
public class HomePageModels
{
public SearchFlyModel SearchFly { get; set; }
public CarRequestModel CarRequest { get; set; }
public int form { get; set; }
public HomePageModels()
{
SearchFly = new SearchFlyModel();
CarRequest = new CarRequestModel();
}
}
}
Then on the controller's method I need to know which form was posted to know which form I need to validate, because Asp.net automatically validate the model , it will ,by default validate both models in your ViewModel , and this affects the ModelState and when you check if your model is valid doing ModelState.isValid , this will return false because you only posted one form and not the other which is completely empty.
To manage that, I do the following
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Index(HomePageModels model)
{
ModelState.Clear();
if (model.form == 0)
{
try
{
ValidateModel(model.SearchFly);
return this.SearchFly(model.SearchFly);
}
catch (Exception e)
{
}
}
else
{
try
{
ValidateModel(model.CarRequest);
return this.SearchCar(model.CarRequest);
}
catch (Exception e)
{
}
}
var modelHomePage = new HomePageModels()
{
SearchFly = model.SearchFly,
CarRequest = model.CarRequest,
form=model.form
};
return View(modelHomePage);
}
Both forms are posting to the same action because I need to return the form's validations error so the user can fix it.

MVC4 Incorrect model error

Hi I getting this error:
The model item passed into the dictionary is of type 'System.Collections.Generic.List`1[DBModel.Telemarketing]', but this dictionary requires a model item of type 'TWeb.Models.LoginModel'
In _Layout.cshtml file i have
#Html.Partial("_LoginPartial")
this partial login view is rendered in div on _layout page (it`s hides/shows with javaScripts )
#model TWeb.Models.LoginModel
Then I have "Telemarketings" controller having view:
public class TelemarketingController : Controller
{
private Entities db = new Entities();
//
// GET: /Telemarketing/
public ActionResult Index()
{
return View(db.Telemarketings.ToList());
}
When I click link in _Layout page
#Html.ActionLink("Telemarketingas", "Index", "Telemarketing", new{area="" },new{ })
It throws an error written in top of the post.
I am new in MVC, please help me.
problem 1) Your Partial requires a model, and you're not passing one.
proper syntax: #Html.Partial("_LoginPartial", Model.LoginModel)
problem 2) _layout, as far as I know, can't have a Model passed
Solution 1:
Use an ActionPartial. AcionPartials are called similarly,
#Html.Action("/Tools/_LoginPartial").
The difference is they have an ActionMethod Associated which can return a Model
public ActionResult _LoginPartial()
{
LoginModel Model= new LoginModel();
//populate Model from whatever
return View(Model);
}
Option 2:
Pass a LoginModel object to a Viewbag
Viewbag.LoginModel = new LoginModel();
and reference the Viewbag in your _layout's Partial
#Html.Partial("_LoginPartial", Viewbag.LoginModel)
Your "_LoginPartial" expects "LoginModel" model, but since you're not giving it any, Razor engine sets its model to the current view model ("db.Telemarketings.ToList()").
All you have to do is somehow set its model, probably like so:
#Html.Partial("_LoginPartial", new LoginModel())
Simplest way was to remove model declaration from Login Div :).
You can use this code
#Html.Partial("Partial page", new ModelFroLogin())

Basic ASP.NET MVC query with parameter

I am new in ASP.Net MVC and MVC architecture in general. I'm building a simple application using the Database Code First method.
I have a Recipe Model with a property called cookId which is the id of the user who created the recipe.
Now I want to be able to pass a querystring to my page and get ONLY the recipes where the cookId is the same as the parameter and list i on my View.
How can I achieve this? Where should i put this logic? In my controller or in my view?
Well, asp.net mvc works with routes, or TableRoutes. The default routes is created with this format: {controller}/{action}/{id}.
So, when you get a request on your action, you could retrive this id from id parameter on your Action (at controller) and use this value to hit on your database and get all records you need to show on the View. You could try something liek this:
public ActionResult Recipes(string id)
{
IEnumerable<Recipe> list = _repository.GetRecipeByCookId(id); // this method should return list of Recipes
return View(list); // return your View called "Recipes" passing your list
}
You also could use Request.QueryString["Id"] to get the Id, but it is not a good pratice in asp.net mvc. You can use parameters on your action and use it.
On your View, you could type it with the IEnumerable<Recipe> and show it on a table, something like:
#model IEnumerable<Recipe>
<table>
#foreach(var recipe in Model)
{
<tr>
<td>#recipe.Name</td>
<td>#recipe.CookId</td>
<td>#recipe.OtherProperties</td>
</tr>
}
</table>
To create an link passing this id for the request, you could just use Html.ActionLink, something like on your View:
#Html.ActionLink("Text of You Link", "Action", "Controller", new { id = 5, another = 10 }, new { #class = "css class for you link" });
and asp.net mvc will render an a tag with a apropriated route following routetable setted on global.asax. If you have other parameters to pass in querystring, you also could add it like I did on sample with another parameter.
NEVER put logic in the view. The view should simply display the information provided in the model. Put the logic in the controller.
Controller:
[HttpGet]
public ActionResult Recipes(int cookId)
{
var recipes = /* get recipes based on cook */;
List<RecipeModel> model = recipes
.Select(r => new RecipeModel
{
Id = r.Id,
CookId = r.CookId,
...
})
.ToList();
return View(model);
}
View:
#model List<RecipeModel>
#foreach (RecipeModel item in Model)
{
<div>
<span>Id:</span>
<span>#item.Id</span>
</div>
}
Controller:
[HttpGet]
public ActionResult GetRecipes(int cookId)
{
// model can view a List<Recipe>, logic goes here
var model = SomeQueryThatReturnsRecipesFrom(cookId);
return View(model)
}
View (for example views\yourController\GetRecipes.cshtml), only use this file to show data, its not recommendend to put logic here:
#model List<Namespace.Recipe>
<h2>Recipes</h2>
#foreach(var r in Model)
{
<p>r.Name</p>
}
This will be called with the following querystring:
/Recipes/GetRecipes?cookId=SomeId
You might have a CooksController. That controller would return a list of cooks. That list might include a link for the cook's recipes. Your RecipesController could handle the request for all recipes for a given cookId.
#Html.ActionLink("Recipes", "RecipesByCook", "Recipes", new { cookId = model.cookId }, null};
The above code is used in the view Cooks/Index.shtml. It creates a link that uses the query string to identify the cookId like you want.
The RecipesController would then have a method RecipiesByCook which takes a parameter for the cookId. This method will handle requests for URLs like this, Home/Recipies/RecipeByCook?cookId=4.
Your RecipesController can then return an ActionResult with the correct set of recipes to show. Very simply (as in you may want to add more for the view to show, like information about the cook):
public ActionResult RecipesByCook(int cookId)
{
var recipes = repository.Recipes.Where(r => r.cookId == cookId);
return View(recipes);
}

ASP.NET MVC: How to transfer more than one object to View method?

I finished NerdDinner tutorial and now I'm playing a bit with project.
Index page shows all upcoming dinners:
public ActionResult Index()
{
var dinners = dinnerRepository.FindUpComingDinners().ToList();
return View(dinners);
}
In DinnerRepository class I have method FindAllDinners and I would like to add to above Index method number of all dinners, something like this:
public ActionResult Index()
{
var dinners = dinnerRepository.FindUpComingDinners().ToList();
var numberOfAllDinners = dinnerRepository.FindAllDinners().Count();
return View(dinners, numberOfAllDinners);
}
Of course, this doesn't work. As I'm pretty new to OOP I would need help with this one.
Thanks,
Ile
Create view model:
public class DinnerViewModel
{
public List<Dinner> Dinners { get; set; }
public int NumberOfAllDinners { get; set; }
}
public ActionResult Index()
{
var dinners = dinnerRepository.FindUpComingDinners().ToList();
var numberOfAllDinners = dinnerRepository.FindAllDinners().Count();
return View(new DinnerViewModel { Dinners = dinners, NumberOfAllDinners = numberOfAllDinners } );
}
You need to create a "wrapper" object that contains the objects you wish to pass as public properties of it. For instance, create an object called DinnerViewModel and give it two properties and set these with two properties, one a List called Dinners and one an int called DinnerCount. Then pass the DinnerViewModel to the view and you can then access Model.Dinners and Model.DinnerCount
In your case I would prefer the solution mentioned by LukLed.
In general you could of course also transfer multiple values from your controller to your view using ViewData:
ViewData["dinners"] = dinners;
ViewData["numberOfAllDinners"] = 150;
...
For more information also take a look at this link.
Just simply use dinners.Count property instead.
Remember, you start off using the ViewData inherts in you .aspx filesand returning the same in you return statements. Because of that, I figure that it was an issue with the Inherits attribute on the top of the ASP.NET files. But, if you are getting the error when trying to create or edit a new Dinner when you are on the 'Upcoming Dinners' page (generated from the Details.aspx and the LINQ file that gets all Dinners that are after todays date), go into your 'Controllers' directory, specifically the DinnerController.cs. Then look at the Edit and or Create methods. the answer lies right here. If you put breakpoints on these methods, you should be able to figure it out. If not, continue reading:
Look where it fails, the 'return...' line. Maybe I am the only person who forgot to change this, but my error is the same as people are getting in this page and this os how I fixed it.....the 'return(dinner)' line, in Create and Edit (and any others that you are having issues with), they are using the NerDinner.Model.Dinner / ViewData method. However, if you change it to the ViewModel return method instead, it should fix it, For example: 'return(new DinnerFormViewModel(dinner));', it should work for you. I hope this helps, as it was what my issue was. Just a simple overlook.

Resources