MVC 3 accept null foreach in the viewModel - asp.net-mvc

I have a page where users can enter their state information and then a list of other users come back within the state. I am using a foreach loop.
Some of the states have 0 users, which then leads me to get an error: Object reference not set to an instance of an object. How can I get past that error? The particular model I'm using is called Profiles.
The Model:
public class homepage
{
public List<profile> profile { get; set; }
public PagedList.IPagedList<Article> article { get; set; }
}
The Controller:
public ActionResult Index()
{
HttpCookie mypreference = Request.Cookies["cook"];
if (mypreference == null)
{
ViewData["mypreference"] = "Enter your zipcode above to get more detailed information";
var tyi = (from s in db.profiles.OrderByDescending(s => s.profileID).Take(5) select s).ToList();
}
else
{
ViewData["mypreference"] = mypreference["name"];
string se = (string)ViewData["mypreference"];
var tyi = (from s in db.profiles.OrderByDescending(s => s.profileID).Take(5) where se==s.state select s).ToList();
}
return View();
}
The View:
#if (Model.profile != null)
{
foreach (var item in Model.profile)
{
#item.city
}
}
When I get the Object reference not set to an instance of an object error, the line #if (Model.profile != null) is highlighted, so I tried to do this:
public List<profile>? profile { get; set; }
But it didn't work. Any ideas of how to accept an empty Model in a foreach or just skip the code at runtime?

Profile is a list. See if the list has any elements.
See if this works:
#if (Model.profile.Any())
{
foreach (var item in Model.profile)
{
#item.city
}
}

Just noticed, you're calling View() but not passing it a model, then in the view you're referencing Model.profile. Inevitably Model is null, and therefore has no profile property to access. Make sure you're passing the model off to the view in the return View(model) call.
Follow-up for collections
I've always found that any time you have variable that implements IEnumerable<T>, it's best to populate it with an empty set over a null value. That is to say:
// no-nos (IMHO)
IEnumerable<String> names = null; // this will break most kinds of
// access reliant on names being populated
// e.g. LINQ extensions
// better options:
IEnumerable<String> names = new String[0];
IEnumerable<String> names = Enumerable.Empty<String>();
IEnumerable<String> names = new List<String>();
Unless you like checking if (variable != null && variables.Count() > 0) every time you want to access it, make it an empty collection and leave it at that.
To come full-circle, as long as the variable is populated with a collection of some sort (empty or populated) a foreach shouldn't break. it will simply skip past the code block and not output anything. If you're getting an object null error, it's most likely because the variable is empty and the enumerator could not be retrieved.

Related

ASP.NET MVC Passing ID from Controller to View

Hi I am trying to pass an ID from the URL from a controller to a view. I am also passing a viewmodel that contains two models that have data from a database. Is there another way of doing this? The routemap has been set to send an ID.
The URL would be http://localhost:55147/Home/ViewProject/8 which obviously 8 is the ID of the project, but it does not seem to get this. I am passing the ID to the ViewProject controller function by URL.Action which has the ID passed through the parameter on another page.
Here is my controller:
public ActionResult ViewProject( int? id )
{
if( id == null )
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
BextecODBEntities DB = new BextecODBEntities();
var mymodel = new Multipledata();
mymodel.projectss = DB.Projects.ToList();
mymodel.projectnotess = DB.ProjectNotes.ToList();
return View("ViewProject", mymodel);
}
Here is my View:
#foreach(var item in Model.projectss)
{
if( item.ID.ToString() == Html.ViewContext.RouteData.Values["id"].ToString() )
{
#Html.DisplayText("You have clicked on Project ID:" + item.ID.ToString())
}
}
My multiple data model contains:
public IEnumerable<Projects> projectss { get; set; }
public IEnumerable<ProjectNotes> projectnotess { get; set; }
Instead of sending a list of Projects I have sent the specific Project data I have defined in the database model using the Project.Find(id) passed through the parameter like you have said

MVC5 - How to know if an action input was completely empty

I have an action that takes a complex object as an input. I want to be able to populate any of the values with either POST data, or from the query string in a GET request. This works fine.
I also want to provide a default value if no user input was provided, however, this is not working because filter is never null, even if there were no querystring params from a GET request. What happens instead is, MVC just calls the model's default constructor without setting any properties instead of giving me a null.
public ActionResult Index(DataFilterInput filter = null)
{
if (filter == null)
filter = new DataFilterInput { Top = 100 };
var model = new IndexModel();
return View(model);
}
How can I know whether I should be defaulting the values in the absence of user input (I do not want to go into the Request query string or form collections)?
to provide a default value for your object this sample would work
public class SearchModel
{
public bool IsMarried{ get; set; }
public SearchModel()
{
IsMarried= true;
}
}
and if you want to validate the model
public ActionResult Index(DataFilterInput filter = null)
{
if (!ModelState.Isvalied)
filter = new DataFilterInput { Top = 100 };
var model = new IndexModel();
return View(model);
}
You can decare top nullable
public int? Top {get;set;}
So when no top value is provided it will be null by default and you can check it by using ==null or HasValue like this
public ActionResult Index(DataFilterInput filter )
{
if (!filter.Top.HasValue )
filter = new DataFilterInput { Top = 100 };
var model = new IndexModel();
return View(model);
}

Items not being added to multiple lists when saved in database.

I have the following code on a controller in my MVC application:
public void BulkUpdateGroupResident(List<TherapyGroup> therapyGroups)
{
foreach(var group in therapyGroups)
{
using (var context = new ResidentFirstDBContext())
{
var currentGroup = context.TherapyGroups.Where(g => g.ID == group.ID).FirstOrDefault();
if (group == null)
{
throw new HttpResponseException(HttpStatusCode.NotFound);
}
if (currentGroup.Residents == null)
{
currentGroup.Residents = new List<Resident>();
}
currentGroup.Residents.Clear();
foreach (var id in group.ResidentsId)
{
var resident = context.Residents.Where(r => r.ID.ToString() == id.ToString()).FirstOrDefault();
if (resident == null)
{
throw new HttpResponseException(HttpStatusCode.NotFound);
}
currentGroup.Residents.Add(resident);
}
context.Entry(currentGroup).State = EntityState.Modified;
context.SaveChanges();
}
}
}
The intended functionality is for it to fetch the therapy groups object (which is currently storing residentids and the groupid) from the Post to the controller, and then it should take those values and add the residents to the list of resident for the particular group; however, I have a problem because it seems the code will not push the residents into all of the correct groups when it is saved to the database. In fact, I have determined it only pushes them into the last selected group; however, this ONLY happens if the resident being added to two subsequent group is the same. For example, I want to add John to two different groups at the same time will not work. I know it's something to do with pushing the same object into different lists, but I am not sure how I go about fixing this problem. Any help is appreciated. Also, my Model code for the TherapyGroup Model is below. It may help you understand. Essentially, we are interested in the List on the model which is pointing to another object.
public class TherapyGroup
{
public Guid ID { get; set; }
public List<Resident> Residents { get; set; }
}

ASP.NET MVC sever-side validation of select (dropdown) list selected item

When handling form posts in MVC, I find myself writing some, somewhat, tedious code to ensure that the posted drop down list selections are for valid items in the list. The idea is, there's nothing preventing a post that contains a selected ID that was not originally presented in the drop down list. A user could insert their own item into the drop down list (or otherwise post whatever they want) or maybe the form has been sitting in the window for so long that the items that are now available have changed. Regardless of why it could happen, the fact is, you can't control the data that is posted. Here's some example code of how I deal with:
VM:
public class MyViewModel
{
public string SelectedItemID {get; set;}
public List<Items> AvailableItems {get; set;}
}
View:
#using (Html.BeginForm())
{
#Html.DropDownListFor(m => m.SelectedItemID,
Model.AvailableItems.Select(i => new SelectListItem()
{
Value = i.ID.ToString(),
Text = i.Name,
Selected = (Model.SelectedItemID == i.ID)
}), "Select One")
}
Controller:
[HttpPost]
public ActionResult Index(MyViewModel myVM)
{
bool isValid = true;
try
{
//Reload the available items
myVM.AvailableItems = Repository.GetAvailableItems();
if(!ModelState.IsValid)
{
isValid = false;
}
else
{
//Make sure the SelectedItemID is a real item
if(!myVM.AvailableItems.Any(i => i.ID == myVM.SelectedItemID))
{
isValid = false;
myVM.SelectedItemID = null;
ModelState.AddModelError("SelectedItemID", "Required"); //This gets interesting when the selected ID belongs to a nested VM in a collection.
}
}
if(isValid)
{
//Finally I can process the form
}
}
catch(Exception)
{
ModelState.AddModelError("", "Unable to process your submission. Please try again.");
}
//return an ActionResult
}
Setting the error in ModelState gets especially ugly if the SelectedItemID belongs to a nested view model that is inside a collection. This seems like it should be a standard type of validation but, relative to the ease of performing other validation in asp.net MVC, this is pretty ugly and tedious. Is there an easier way to take care of this?
I think you should look at Tim Coker's response here:
Is it possible to update ModelState.IsValid manually?
Basically, you want to make your viewmodel class inherit from IValidatableObject, and then put some validate logic into it. Subsequent validation calls should fail if your criteria isn't met (ie SelectedItemId not in a fresh db query)

MVC pass viewmodel view with viewmodel containing parent values and child list

My application has multiple areas within one facility. I am trying to pass a single model to the view that contains facility values (facility_id, facility_name), and a list of areas. I currently have the list of areas as a type of the entity model for the table (area_list).
My viewmodel is as follows:
public class AreaView
{
public string facility_name { get; set; }
public int facility_id { get; set; }
public int group_id { get; set; }
public IList<area_list> areas { get; set; }
}
As an aside, I had originally tried setup the list of areas as a separate viewmodel (AreaS) instead of the model area_list, but I had other issues there so went back to directly referencing the for simplicity. I am assuming this would be more appropriate...
My Controller:
public ActionResult List(int id = 0)
{
var model = (from f in areaDB.facility_list
where f.facility_id == id
select new AreaView
{
facility_id = f.facility_id,
facility_name = f.facility_name,
areas = (from a in areaDB.area_list
orderby a.area_name
where a.facility_id == id
select a).ToList()
});
return View(model);
}
My View (abbreviated):
#model SkyeEnergy.Models.AreaView
Facility: #Model.facility_name
#foreach (var item in Model.areas) {
<tr>
<td>
#Html.ActionLink(item.vendor_name,"Details","Area",new {id = item.vendor_id},null)
</td>
</tr>
}
I have tried numerous variations to accomplish what's below, which has given me numerous errors, but the most recent is below:
The model item passed into the dictionary is of type
'System.Data.Entity.Infrastructure.DbQuery`1[MyApp.Models.AreaView]',
but this dictionary requires a model item of type
'MyApp.Models.AreaView'.
I understand that I am not passing the correct type for what the view is expecting, but I cannot seem to figure out:
Is the viewmodel setup correctly in the firstplace (how to mix values and a list of children
How to structure my linq query to get one AreaView object with
all my data
Pass it appropriately (in the correct type) to my
view
I have read about 45 posts on Stackoverflow, but can't seem to piece them together to accomplish what's above. If anyone has a correct solution (or even a direction), I would be very appreciative.
Thanks for any help.
I think you should add FirstOrDefault() at the end of your query to return the AreaView
public ActionResult List(int id = 0)
{
var model = (from f in areaDB.facility_list
where f.facility_id == id
select new AreaView
{
facility_id = f.facility_id,
facility_name = f.facility_name,
areas = (from a in areaDB.area_list
orderby a.area_name
where a.facility_id == id
select a).ToList()
}).FirstOrDefault();
return View(model);
}
I would not combine both object in the same query. I would do
1) Select AreaView where id = xxxx
2) Select Areas where id = xxxx
3) Assign areas to my AreaView
Example
AreaView model = GetAreaView(id);
model.Areas = GetAreas(id);
return View(model);
Also, try the following for your current code
return View(model.FirstOrDefault());

Resources