ASP.NET MVC 3 nested lists - asp.net-mvc-3

I'm new to the MVC world and need some guidance. I'm trying to create a vertical multi level menu for a website. The data for the menu is stored in a database table with the following structure:
ID, categoryName, parentID
How do I go about this with regular ADO.NET and MVC? As far as I can tell I need to make a MenuItem class with int ID, string name and List<MenuItem> Children properties. I then need to make and instance of this class representing the vertical menu and then I need to genereate the HTML for the view.
I have searched the net for an easy to follow example but I'm not finding anything I can understand. If there is anyone out there that can guide me through this it would be very much apprediated!

You should edit your question or split it up into more concrete questions.
I will answer the concrete question in your comment on how to load your MenuItems recursively.
You wrote you use regular ADO.NET. I was free to use dapper in my sample code which makes life a bit easier:
So here is you MenuItem class:
public class MenuItem
{
public int Id { get; set; }
public int? ParentId { get; set; }
public List<MenuItem> Children { get; set; }
}
Now we open a connection and load the whole MenuItems table into a single collection:
SqlConnection conn = new SqlConnection("...");
conn.Open();
IEnumerable<MenuItem> allItems = conn.Query<MenuItem>("SELECT * FROM MenuItems");
I pretend you will have a single MenuItem as the root item with ParentId = null. Lets find it:
MenuItem rootMenu = allItems.Single(m => m.ParentId == null);
The last thing we have to do is to rearrange the items in a hierachical tree. We do this with a function which works recursively:
loadChildren(rootMenu, allItems);
Here is the function. It simply looks up the children of the passed item and calls itself on all found childnodes:
private static void loadChildren(MenuItem currentItem,
IEnumerable<MenuItem> allItems)
{
currentItem.Children = allItems
.Where(m => m.ParentId == currentItem.Id).ToList();
foreach (var childItem in currentItem.Children)
{
loadChildren(childItem, allItems);
}
}

Related

implementing dropdownlist in asp.net mvc 3

I am teaching myself asp .net mvc3. I have researched a lot but the more I read the more confused I become. I want to create a page where users can register their property for sale or rent.
I have created a database which looks like this:
public class Property
{
public int PropertyId { get; set; }
public int PropertyType { get; set; }
ยทยทยท
public int Furnished { get; set; }
...
}
Now, I want dropdownlistfor = PropertyType and Furnished.
Property type would be
1 Flat
2 House
3 Detached House
...
Furnished would be:
1 Furnished
2 UnFurnished
3 PartFurnished
...
Now, I am really not sure where to keep this information in my code. Should I have 2 tables in my database which store this lookup? Or should I have 1 table which has all lookups? Or should I just keep this information in the model?
How will the model bind to PropertyType and Furnished in the Property entity?
Thanks!
By storing property types and furnished types in the database, you could enforce data integrity with a foreign key, rather than just storing an integer id, so I would definitely recommend this.
It also means it is future proofed for if you want to add new types. I know the values don't change often/will never change but if you wanted to add bungalow/maisonette in the future you don't have to rebuild and deploy your project, you can simply add a new row in the database.
In terms of how this would work, I'd recommend using a ViewModel that gets passed to the view, rather than passing the database model directly. That way you separate your database model from the view, and the view only sees what it needs to. It also means your drop down lists etc are strongly typed and are directly in your view model rather than just thrown into the ViewBag. Your view model could look like:
public class PropertyViewModel
{
public int PropertyId { get; set; }
public int PropertyType { get; set; }
public IEnumerable<SelectListItem> PropertyTypes { get; set; }
public int Furnished { get; set; }
public IEnumerable<SelectListItem> FurnishedTypes { get; set; }
}
So then your controller action would look like:
public class PropertiesController : Controller
{
[HttpGet]
public ViewResult Edit(int id)
{
Property property = db.Properties.Single(p => p.Id == id);
PropertyViewModel viewModel = new PropertyViewModel
{
PropertyId = property.Id,
PropertyType = property.PropertyType,
PropertyTypes = from p in db.PropertyTypes
orderby p.TypeName
select new SelectListItem
{
Text = p.TypeName,
Value = g.PropertyTypeId.ToString()
}
Furnished = property.Furnished,
FurnishedTypes = from p in db.FurnishedTypes
orderby p.TypeName
select new SelectListItem
{
Text = p.TypeName,
Value = g.FurnishedTypeId.ToString()
}
};
return View();
}
[HttpGet]
public ViewResult Edit(int id, PropertyViewModel propertyViewModel)
{
if(ModelState.IsValid)
{
// TODO: Store stuff in the database here
}
// TODO: Repopulate the view model drop lists here e.g.:
propertyViewModel.FurnishedTypes = from p in db.FurnishedTypes
orderby p.TypeName
select new SelectListItem
{
Text = p.TypeName,
Value = g.FurnishedTypeId.ToString()
};
return View(propertyViewModel);
}
}
And your view would have things like:
#Html.LabelFor(m => m.PropertyType)
#Html.DropDownListFor(m => m.PropertyType, Model.PropertyTypes)
I usually handle this sort of situation by using an enumeration in code:
public enum PropertyType {
Flat = 1,
House = 2,
Detached House = 3
}
Then in your view:
<select>
#foreach(var val in Enum.GetNames(typeof(PropertyType)){
<option>val</option>
}
</select>
You can set the id of the option equal to the value of each item in the enum, and pass it to the controller.
EDIT: To directly answer your questions:
You can store them as lookups in the db, but for small unlikely to change things, I usually just use an enum, and save a round trip.
Also look at this approach, as it looks better than mine:
Converting HTML.EditorFor into a drop down (html.dropdownfor?)

One to many, one view do I need a custom model?

I have a database base first application so I have no homemade modals as such, just the ones created automatically. I want to display in a single view the one and many parts from a select in single row in an html table/list, here is the SQL that works great in a re-created MS Access Query, - some brackets:
SELECT Facilitiy.FacilityName, Facilitiy.FacilityImage, FacilityDetail.FacilityDetailDescription
FROM FacilityDetail INNER JOIN Facilitiy ON FacilityDetail.FacilityDetailID = Facilitiy.FacilityFK
WHERE FacilityFK = id
And the LINQ that gives me the correct many part of the list:
from fd in db.FacilityDetails
join f in db.Facilities on fd.FacilityID equals f.FacilityFK
where f.B FacilityFK == id
select f
I had kind of hoped that would do it as the SQL does but I need to create a modal that I can put in a view to display:
FacilityImage(one side) | FacilityName(one side) | FacilityDetailDescription(many side)
I have heard I can simply do it by doing something like this???
from fd in db.FacilityDetails.Include("Facilities")
but it seems to make no difference!
Which leads me to believe I need to create a new custom model? If so how would i write it?
I tried something like this but I cant get it to work.
public class FacilitiyDetails
{
public string FacilitiesDescription { get; set; }
public string FacilitiesImage { get; set; }
public string BeachFacilitiesDescription { get; set; }
{
I'm not quite sure what i'm supposed to do here!
Yes you will need a custom model because you're wanting to display multiple types. You'll need to have something like:
public class FacilityModel
{
public Facility Facility { get; set; }
public List<FacilityDetails> FacilityDetails { get; set; }
}
Based on your class above and assuming you have a Facility class already. Then you'll need to change your LINQ to first get the Facility details:
var facility = (from f in db.Facilities
where f.FacilityID == id
select f)
.FirstOrDefault(); //just being safe here,
//if it's null, you'll want to escape out of
//the code below
Then you'll need to get all equivalent FacilityDetails for that facility by doing:
var facilityDetails = (from fd in db.FacilityDetails
where fd.FacilityFK == id
select fd).ToList()
Then you can create a new instance of your Model by doing:
var model = new FacilityModel
{
Facility = facility,
FacilityDetails = facilityDetails
};

MVC View Model Organization

I'm new to MVC and I'm trying to understand how to organize my ModelViews. The first problem is Drop Down data. I have an Equipment Model and an EquipmentViewModel that looks like this:
public class EquipmentViewModel
{
public Equipment Equipment { get; private set; }
public SelectList EquipmentCategories { get; private set; }
private MyEntities db = new MyEntities();
public EquipmentViewModel(Equipment equipment)
{
Equipment = equipment;
EquipmentCategories = new SelectList(db.EquipmentCategories.Where(c => c.IsActive),
"EquipmentCategoryID", "Description");
}
Please note the SelectList for my category dropdown. This is all well and good. However I have another Model called Inventory. Inventory has an EquipmentID property (corresponding to a piece of equipment you see). For the creation of an inventory item it would be useful to have a drop down for the equipment category. I already have this select list in my EquipmentViewModel and it feels wrong some how to duplicate that code for the InventoryViewModel.
I have considered something like this:
public class InventoryViewModel
{
MyEntities db = new MyEntities();
public Inventory Inventory { get; set; }
public EquipmentViewModel EquipmentViewModel { get; set; }
}
This seems okay to me except I am going to have an Index page for the InventoryViewModel. Basically I would be returning a List of InventoryViewModels which each has an EquipmentViewModel where in each of those has the exact same list of categories. This, too, feels wrong and I think I am misunderstanding some crucial piece of the MVC puzzle.
It also leads me to my second question: How would I return such a monstrosity from the controller? I imagine it looking something like this:
var list = db.Inventories
.Select(i => new InventoryViewModel
{
Inventory = i,
EquipmentViewModel = new EquipmentViewModel(i.EquipmentID)
});
Which means that I'd basically be making separate trips to the database (inside the EquipmentViewModel constructor) for each EquipmentID rather than being able to join on the id. For example if I just needed the description I could do this:
var list = from i in db.Inventories
join e in db.Equipments
on i.EquipmentID equals e.EquipmentID
select new InventoryViewModel
{
Inventory = i,
EquipmentName = e.Description
};
Which I believe would have much better performance. I greatly appreciate any wisdom that anyone could offer. Thanks!
I have and idea for you,
there is no point of ducplicating your views if they are doing same thing. however if they are not doing exactly the same is better to exted or create new view than have one big one that is shared for all parts.
You can have model such as this
public class InventoryViewModel{
// contains all categories for every inventory
public SelectListItem EquipmentSelect {get;set;}
//this will contain all the items you want to assign
public List<Inventories> ListOfInventories{get;set;}
}
after in view you can create partial view for every item with the select list item in to it

Populate a DropdownList with composite key records in MVC3.net?

I don't know if I'm missing something obvious, but I really want to grab names of clients associated with a composite key.
Controller Code:
Job job = db.Jobs.Find(id);
ViewBag.jobClientsList = new SelectList(job.JobClients.ToList(), "ClientNumber", "ClientNumber");
View Code:
<%: Html.DropDownList("ClientNumber", ViewData["JobClientsList"] as SelectList)%>
Model:
namespace Sample.CustomerService.Domain {
using System.ComponentModel.DataAnnotations;
public class JobClient {
public JobClient() { }
[Key]
[Column(Order = 1)]
public virtual int JobNumber { get; set; }
[Key]
[Column(Order = 1)]
public virtual int ClientNumber { get; set; }
public virtual Client Client { get; set; }
public virtual Job Job { get; set; }
}
}
This code works, but all I get in the dropdownlist is a bunch of numbers. What I would really like is the client names associated with the numbers but I'm really not sure how to do it! I've been looking around for ages!
After re-reading your question your answer seems simpler then expected.
Check out the Select list class http://msdn.microsoft.com/en-us/library/system.web.mvc.selectlist.aspx
The constructor your using in your controller is wrong, it should be:
ViewBag.jobClientsList = new SelectList(job.JobClients.ToList(), "ClientNumber", "Client");
You were setting the text value of the selectList to be "ClientNumber" which is why you had a list of numbers and not names!
By default the select list is showing you the property that is marked [Key]
<%: Html.DropDownList("ClientNumber",
ViewData["JobClientsList"].Client as SelectList)%>
Should print the client name (assuming the primary Key on the Client object is their name, otherwise You'd need something like ViewData["JobClientsList"].Client.FullName
The best solution would be to use a ViewModel instead of using ViewBag or ViewData for this, it'll help avoid a lot of headaches both now and in the future.
What I have done in the past to get DropDownLists working is save the List to the Session Variable, and then create my SelectList in the actual DropDownList.
Controller:
Job job = db.Jobs.Find(id).ToList();
ViewBag.jobClientList = job;
View:
<%: Html.DropDownList("ClientNumber", new SelectList((If you view is strongly typed, put that here) ViewData["JobClientsList"],"ClientNumber","ClientNumber")%>
This may be poorly worded, so I think I can clarify if need be
Anyone looking for a solution, try the following:
In your controller:
1) Get the list:
var allCountries = countryRepository.GetAllCountries();
2) define a variable:
var items = new List<SelectListItem>();
3)loop each item:
foreach(var country in allCountries)
{
items.Add(new SelectListItem() {
Text = coutry.Name,
Value = Country.Id.ToString(),
// Put all sorts of business logic in here
Selected = country.Id == 13 ? true : false
});
}
model.Countries = items;
In your view:
#Html.ActionLink("StringToDisplay", "actionName", "controllerName", new SelectList(Model.Countries, "Value","Text"),"--Please Select--", new{#class="form-control"})
Not to mention Model should have a property with
public IEnumerable<Country> Countries {get; set;}

How does one do MVC2/EF4 EntityCollection validation w/ data annotations?

I have finally gotten over one hurdle and can now successfully create new model data. Now there's another catch - validation. Most of the validation seems easy enough as a lot of my model data are scalar values. There is a many-to-many relationship I link to, however, so I'm not sure how to go about validating that. My model is (once again):
Game (only listing the relevant columns):
GameID - int (primary key, auto-incr)
Platform:
PlatformID - int (primary key, auto-incr)
Name - string
GamePlatform (not a visible entity):
GameID - int (foreign key from Games)
PlatformID - int (foreign key from Platforms)
And my Create method (yes, I know it's sloppy and amateurish - I am an amateur and trying to learn. I'll definitely add error checking to it. I'm just trying to get the big picture of the view->controller->validation->persist-in-db/show errors process down):
public ActionResult CreateReview([Bind(prefix = "GameData")]Game newGame, int[] PlatformIDs)
{
try
{
foreach(int i in PlatformIDs)
{
Platform plat = _siteDB.Platforms.Single(p => p.PlatformID == i);
newGame.Platforms.Add(plat);
}
newGame.LastModified = Datetime.Now;
_siteDB.Games.AddObject(newGame);
_siteDB.SaveChanges();
return RedirectToAction("Index");
}
catch
{
return View();
}
}
The array of PlatformIDs are supplied by a group of checkboxes within my view. For my Game to be valid, it must be associated with at least one Platform. I'm just not sure how to validate that with data annotations, or if it's even possible to do so. Any help would be greatly appreciated.
If I understand your question correctly, your int[] potentially contains ints associated with the ID of a Platform in your DB and you want to make sure your int[] contains at least one valid PlatformID, correct?
Immediately you could do just a simple check prior to going into your logic:
// If there aren't any IDs in Platform that are in PlatformIDs...
if (!_siteDB.Platforms.Any(p => PlatformIDs.Contains(p.PlatformID)))
Return RedirectToAction("Index");
// And probably tell the user to check a box, if they did,
// One of your checkboxes isn't matching up with your PlatformIDs
Ideally what you'd want to do is add the int[] to your model so you can check model validation. Since databased don't typically store int[], add it to your Game model. The EF probably put your DB Entities in your Models folder and if you look at them, you'll see they're partial classes. So add this in your Models folder:
public partial class Game
{
public Dictionary<int, bool> SupportedPlatforms { get; set; }// Edited
}
// Also add this which you'll see why below
public partial class Platform
{
public static bool IsValidPlatformID(int PlatformID)
{
using (SiteDBEntities _siteDB = new SiteDBEntities())
return _siteDB.Platforms.Any(p => p.PlatformID.Equals(PlatformID));
}
}
Then add a custom ValidationAttribute class:
public ContainsValidPlatformIDAttribute : ValidationAttribute
{
public ContainsValidPlatformIDAttribute() { }
public override bool IsValid(object value)
{
Dictionary<int, bool> supportedPlatforms = (Dictionary<int, bool>)value;
if (value == null)
return true;
foreach (int i in values)
{
if (supportedPlatforms.Values.Any(b => b.Equals(true)))// Edited
return false;
}
return true;
}
Now decorate your Property with it in the Game class:
[ContainsValidPlatformID(Error = "You did not select a valid Platform.")]
public Dictionary<int, bool> SupportedPlatforms { get; set; }// Edited
(Edited)Now instead of hard coding a checkbox for each platform, add this instead:
<%: Html.CheckboxFor(model => model.SupportedPlatforms[0]) %>
<%: Html.ValidationMessageFor(model => model.SupportedPlatforms[0]) %>
(Edited)Now your checkboxes are tied to the Model, you can validate the model in the controller, and you can remove the int[] argument from your Action method. This has all been coded from my head into this editor so you may need to tweak some things here and there but this is the direction you should be heading in when working with Models in Views.
Also, check out what Scott Guthrie has written on the topic of MVC Model Validation in his blog. Hopefully with my sample and Scott's blog, you'll be pointed in the right direction.

Resources