I'm trying to get access to multiple models in my view.
I created a ViewModel class and its controller and I imported the model to view but I got this error:
The model item passed into the dictionary is of type
'System.Collections.Generic.List`1[LivrinhosMVC.Models.Category]', but this
dictionary requires a model item of type 'LivrinhosMVC.Models.ViewModel'.**
ViewModel Class:
public class ViewModel
{
public List<Category> Categories { get; set; }
public List<Ad> Ads { get; set; }
}
ViewModel Controller:
// GET: ViewModel
public ActionResult Index()
{
ViewModel mymodel = new ViewModel();
mymodel.Categories = db.Categories.ToList();
mymodel.Ads = db.Ads.ToList();
return View(mymodel);
}
View:
#model LivrinhosMVC.Models.ViewModel
<div class="container">
<div class="row" style="margin:2em 0">
<div class="col-sm-4">
#Html.Label("Categorias")
<table class="table-condensed">
#foreach (var item in Model.Categories)
{
<tr>
<td>#Html.ActionLink(item.Name, "../Category/Books", new { id = item.ID }, null)</td>
</tr>
}
</table>
</div>
<div class="col-sm-8" style="display:inline">
#Html.TextBox("BookTitle", null, new { placeholder = "TÃtulo...", #class = "form-control" })
#Html.DropDownList("Cities", "Portugal")
#Html.ActionLink("Pesquisar", "Books", null, null, new { #class = "btn btn-primary" })
</div>
<div class="row">
#foreach (var item in Model.Ads)
{
<div class="col-sm-3">
#Html.Label(item.Title)
</div>
}
</div>
</div>
</div>
I'm not sure based on your example code why you would be getting that error, it seems to have an issue with #foreach (var item in Model.Categories).
What I can see, and recommend though, is to follow through with the view model approach, but do not attach entities into the view model. View models should be simple POCO classes that only expose enough detail for the view to consume, nothing more. The reason for this is to help boost performance (less data read and transmitted) and boost security. (no data sent to the client you don't expect them to see, such as exposed in debugging tools, and ensure that Entities are not sent back to the server with unintentional edits, again through debugging tools, trusted, and attached and committed to the context.)
I would suggest creating a CategorySummaryViewModel and an AdSummaryViewModel containing just the ID and Name to start. Mark all view models as Serializable. Then when you populate your ViewModel:
[Serializable]
public class AdsPageViewModel
{
public List<CategorySummaryViewModel> Categories { get; set; } = new List<CategorySummaryViewModel>();
public List<AdSummaryViewModel> Ads { get; set; } = new List<AdSummaryViewModel>();
}
public ActionResult Index()
{
var mymodel = new AdsPageViewModel
{
Categories = db.Categories.Select(x=> new CategorySummaryViewModel
{
Id = x.Id,
Name = x.Name // Append other fields if necessary for the view.
}).ToList(),
Ads = db.Ads.Select(x => new AdSummaryViewModel
{
Id = x.Id,
Name = x.Name
}).ToList()
};
return View(mymodel);
}
This will seem like more code/effort than simply passing the entities. You can leverage a tool like Automapper to simplify this, and utilize .ProjectTo<T> to gain the same benefits as above. These benefits include that by using .Select() the EF SQL statement sent to the database, and data coming back from the database only include the fields our view model needs. Less data over the wire = faster, and less memory needed on the app server and client for the request. It also ensures our view can only ever see the fields we want it to see. Someone using F12 in our client can't inspect other FKs and fields that are hidden only because we don't have controls visible on the page. We also ensure that our application does not accept entities back from the client where the enticement will be to attach them to a context and save them. (seems simple, but vulnerable to hackers altering the entity data using those same debugging tools and corrupting the data saved.)
Give that a go with serializable POCO view models to make up the view's data and see if you still get any exception around rendering the child collections. I suspect if there still is an issue there may be a detail from your code that isn't covered by your example here.
Try eagerly load the data you want to send to the view.
Such as:
public ActionResult Index()
{
var model = db.ViewModel
.Include(c => c.Categories);
.Include(c => c.Ads);
return View(model.ToList());
}
And make the view Strogly typed as:
#model IEnumerable<LivrinhosMVC.Models.ViewModel>
Related
I know it would be a basic question but I'm a newbie to ASP.Net MVC. I have fetched data from database using LINQ but there is an issue. I wanna bind that data with input fields of a customized webform. (I'm using MVC). I wanna populate the input fields of webform with fetched data. I'm using EF Database first approach.
My Controller and view is attached.
Controller ActionMethod
public class HomeController : Controller
{
public ActionResult Index()
{
AutoRTGSEntities_1 dc = new AutoRTGSEntities_1();
//dc.policies.Where(cb => cb.Section_Key.Contains("SenderBIC"));
return View(dc.policies.Where(cb => cb.Policy_Section.Contains("RTGS")).ToList()); //get RTGS policy section data
}
}
View
#model IEnumerable<Swift_MT_103.policy>
#{
ViewBag.Title = "Home Page";
}
<div> #Model{ #Html.TextBoxFor(x => x.data_Value)) } </div>
<div> <input type="text" name="ReceiverBIC" id="ReceiverBIC" /> </div>
Rest is HTML and CSS. Snap is attached.
Here's a very basic example of how to this. Let's say you have following class:
public class User
{
public int Id { get; set; }
[Display(Name = "Name")]
public string Name { get; set; }
[Display(Name = "E-mailaddress")]
public string E-mail { get; set; }
}
In the controller you get the user:
public ActionResult Index(int id)
{
var user = Db.Users.FirstOrDefault(x => x.Id == id);
if(user != null)
{
return View(user);
}
//Return to the 'Error' view as no user was found
return View("Error");
}
You also need a View to show everything on screen. Make it a strongly typed view, this way you can pass a Model to it. This class will hold all data you want to pass to the view. Code of the view:
//This line lets the view know which class represents the model
#model User
#Html.LabelFor(m => m.Name)
#Html.TextBoxFor(m => m.Name)
#Html.LabelFor(m => m.Name)
#Html.TextBoxFor(m => m.Name)
Using the Razor syntax instead of plain HTML it is very easy to construct and bind your form elements to the corresponding data. In this case the label will show the value of the Display attribute in the User class and the values of the user will be filled in the textboxes.
More reading:
Getting started with ASP.NET MVC 5
ASP.NET MVC Overview
Update:
In case you have a list of objects, you need to enumerate them in the view:
#model IEnumerable<string>
#foreach (var value in Model)
{
<div>#value</div>
}
And if the model is a class and has a property that is a list:
//Let's say a user has lots of names
public class User
{
public int Id { get; set; }
public List<string> Names { get; set; }
}
//View:
#model User
#Html.TextBoxFor(m => m.Id)
#foreach (var name in Model.Names)
{
<div>#name</div>
}
Try to implement a correct ASP.NET MVC architecture. To get this completed, you'll need to use proper Razor (.cshtml type) Syntax in your Views. Best practice:
Create a dedicated ViewModel class in the Model directory. You might call it CustomerCreditTransferViewModel for example. It should contain all Properties you want to display/edit anywhere on the page.
Once you selected your data from your DBContext in your Action, create an instance of CustomerCreditTransferViewModel and populate all fields from the result.
Update your View to use #model CustomerCreditTransferViewModel instead of Swift_MT_103.policy (believe me, this is going to make your live much easier in future)
Copy-paste your raw HTML Code into the page and start looking for all Fields you want to bind, e.g. Text fields (<input type="text" name="accountno" value="" />) and replace them with the Razor Syntax for Data Binding (#Html.TextBoxFor(x => x.AccountNo)). If done correctly, they should be populated now.
Next step is probably the POST. Follow the base MVC Post technique from the Tutorials. Ensure that the Posted Value is of type CustomerCreditTransferViewModel) again, so you can easily validate values and map back to type of Swift_MT_103.policy.
I have a partial view which is used to filter what needs to be displayed on a View.
Below is the code I have written.
Partial View for Location
#using JobSite.Entities
#model IEnumerable<string>
#Html.ActionLink("All jobs", "List", "Job", null, new { #class = "btn btn-
block btn-default btn-lg" })
#foreach(var link in Model)
{
#Html.RouteLink(link, new
{
controller="Job",
action="List",
location=link},
new
{
#class = "btn btn-block btn-default btn-lg"
+ (link==ViewBag.SelectedLocation?"btn-primary":"")
})
}
Partial View for Company
#using JobSite.Entities
#model IEnumerable<string>
#Html.ActionLink("All jobs", "List", "Job", null, new { #class = "btn btn-
block btn-default btn-lg" })
#foreach(var link in Model)
{
#Html.RouteLink(link, new
{
controller="Job",
action="List",
company=link},
new
{
#class = "btn btn-block btn-default btn-lg"
+ (link==ViewBag.SelectedCompany?"btn-primary":"")
})
}
In the controller,I am filtering it based on location.
public ViewResult List(string location)
{
JobListViewModel model = new JobListViewModel();
if (location == null)
{
model = new JobListViewModel
{
jobDetails = repository.jobDetailsInterface
};
}
else
{
model = new JobListViewModel
{
jobDetails = repository.jobDetailsInterface.Where(p => p.JobLocation.LocationName == location)
};
}
return View(model);
}
How can i display the items in the view based upon the selection in both the partial views? I want to develop something like a filtering criteria in Shopping websites.
Sample filter used in shopping websites
I don't think one partial view per search facet is the right way to go (where a facet is something like a location or a company). I think the right way to go is to introduce one level of abstraction on top of your search facets that can hold the metadata about each facet inside of a data structure, instead of that metadata being held as literal and distinct server-side code.
To accomplish this you would need to generalize everything about each of your search facets so that it can be represented as data.
For example, your models might look like this:
public sealed class SearchFacetLinkViewModel {
public string Name { get; set; }
public string Link { get; set; }
}
public sealed class SearchFacetViewModel {
public string Description { get; set; }
public IEnumerable<SearchFacetViewModel> Links { get; set; }
public string SelectedFacet { get; set; }
}
Then in your view:
#model IEnumerable<SearchFacetViewModel>
#foreach (var searchFacet in Model) {
// Insert your above code for your views, but instead,
// use values from the model instead of hard-coding them
// so each facet can be generated from within the loop.
#foreach (var facetLink in searchFacet.Links) {
// use facetLink.Name and facetLink.Link
}
}
You can use the session to store up the currently selected facets the user has specified, or you could embed all the current facet state into all the links (so for example, if a location is selected, then each company url emitted also has &location= within it as well). You can also maintain the user's selection state in a database.
In your controller you would either accept all facet values on each submission, or if you are maintaining facet state server-side, you would provide just the "delta" action, such as "remove facet location" or "add facet company=xyz". You may even want to switch to an asychronus/AJAX model where the page doesn't get reloaded and instead of returning whole views, your initial page is more of an empty container, and then AJAX calls to fetch search results using the current facets receive JSON payloads of the matching values (this actually makes maintaining the facets wholly client-side somewhat easier, by removing all need to maintain current facet state server-side). There is also room for, short of AJAX, nonetheless manipulating the URL requested to include all current facet values (not allowing a link's default action to occur but running JavaScript instead).
Please understand that faceted search is a fairly complex topic with many nuances. It's hard to lead you to a comprehensive and best solution, especially because your project has to incrementally develop and improve over time as your knowledge and ability improve. There's no real substitute for the pain of encountering problems caused by your coding choices and then learning how to overcome those (so you choose better next time).
I'm new to MVC, please help me out for this.
I have to manage the profile of Employee in my application.So i created 3 model
DetailPersonal(contain field- First Name,Middle Name, Last Name, DOB)
DetailAddress(contain field- AddressLine1,AddressLine2,City,State,etc)
DetailContact(contain field-Primary Mobile no.,Secondary Mobile No, Off. No. etc)
Can i create a single view for these models and do ajax posting.
I want to display these in tabs Like Personal || Address || Contact
The cleanest way would be to create a new class with three properties, and use that as your model.
Or, if you don't want to do that, you could use a list of objects:
#model List<object>
#{
var detailPersonal = Model[0] as DetailPersonal;
var detailAddress = Model[1] as DetailAddress;
var detailContact = Model[2] as DetailContact;
}
And pass all three from the controller:
View(new object[] {MyDetailPersonal, MyDetailAddress, MyDetailContact})
But it could be more prone to error.
This situation is very common where you have defined classes that need to be combined before being presented to the user.
Typically, this is handled by using a View Model to consolidate separate classes into something that can be modified by the end user. Translating between your entity objects and your view models can be done in the constructor for a simple application.
I would look at this:
What is ViewModel in MVC?
Also, if you want to introduce more sophistication, I would read up on the repository pattern, and also look up domain layer. Those two subjects deal with the situation you are facing.
Will the user submit the contents of all three models using a single form (a shared submit button)? I think that makes the most sense, unless there are circumstances that make AJAX necessary. If I'm right about that, here's what you would do.
Create a new model with three model-typed properties. Let's call it CustomerModel.
public class CustomerModel
{
[DisplayName("Address")]
public DetailAddress AddressDetails { get; set; }
[DisplayName("Contact information")]
public DetailContact ContactDetails { get; set; }
[DisplayName("Personal information")]
public DetailPersonal PersonalDetails { get; set; }
}
Then create the editor templates in the framework-defined folder. These contain the editable fields for each model.
~/Views/Shared/EditorTemplates/DetailAddress.cshtml
~/Views/Shared/EditorTemplates/DetailContact.cshtml
~/Views/Shared/EditorTemplates/DetailPersonal.cshtml
Then add the editors to your main form. Something like...
#using (Html.BeginForm())
{
<div id="addressDetailsTab">
#Html.LabelFor(model => model.AddressDetails)
#Html.EditorFor(model => model.AddressDetails)
</div>
<div id="contactDetailsTab">
#Html.LabelFor(model => model.ContactDetails)
#Html.EditorFor(model => model.ContactDetails)
</div>
<div id="personalDetailsTab">
#Html.LabelFor(model => model.PersonalDetails)
#Html.EditorFor(model => model.PersonalDetails)
</div>
<input type="submit" value="Submit" />
}
I have a DropDownListFor control that I am wanting to show a display value that resides in a property within a model/class (this is the Rule class.) The view's model is actually a collection of these model/classes. However, when I select the item from the DropDownList, I want to send back the entire model as a parameter. I have this working perfectly with the following code, but the Name property within the parameter is coming back as null. The other properties all have appropriate values.
View Code:
#model List<StockTrader.Web.Data.Rule>
#{
ViewBag.Title = "Configure Rules";
}
<h2>#ViewBag.Title</h2>
<h4>Choose a rule to edit:</h4>
<form method="post" id="rulesform" action="SaveRules">
#Html.DropDownListFor(m => m.First().RuleID, new SelectList(Model.AsEnumerable(), "RuleID", "Name"))
<div style="margin-bottom: 15px;">
<label>Value:</label><br />
<input type="number" name="Value" style="margin-bottom: 15px;" /><br />
<button>Save Value</button>
</div>
Controller Code:
public ActionResult SaveRules(Rule model)
{
//do something
}
Rule Class:
public class Rule
{
public int RuleID { get; set; }
public string Name { get; set; }
public int Value { get; set; }
public bool IsDeleted { get; set; }
}
We do have Kendo controls, so if another control would be more appropriate, that is an option.
I would be glad to provide anymore code or information you might need.
Any thoughts or ideas?
EDIT:
So it turns out this is what I needed to do, the accepted answer got me to this point so I'm going to leave it checked.
View Code (w/script included):
#Html.DropDownListFor(m => m.First().RuleID, new SelectList(Model.AsEnumerable(), "RuleID", "Name"), new { id = "ruleid", #onchange = "CallChangefunc(this)" })
#Html.HiddenFor(m => m.First().Name, new { id = "rulename" })
function CallChangefunc(e) {
var name = e.options[e.selectedIndex].text;
$("#rulename").val(name);
}
You will need a hidden field for it,and use dropdownlist on change event on client side to update hidden field:
#Html.DropDownListFor(m => m.First().RuleID, new SelectList(Model.AsEnumerable(), "RuleID", "Name"),new { id= "ruleid" })
#Html.HiddenFor(m=>m.First().Name,new { id="rulename" })
and jquery code:
$("#ruleid").change(function(){
$("#rulename").val($(this).text());
});
Second option isif Rule collection is coming from database you can fetch RuleName by using id to by querying db in action.
it can be achieved by using UIHint
On your model class, on the RuleID property, add an annotation for UIHint. It basically lets you render a partial (cshtml) for the property. So, on the partial, you can have the template for generating the dropdwon with required styling. When Page is generated. Now you can use the same Html.DropDownListFor for RuleID and UI generates a dropdown for it.
This will avoid having additional jQuery code to get the dropdown value, and code is more concise and testable.
EDIT: I think I need some sort of view model, but I'm unsure how to handle this relationship.
I'm trying to understand MVC 4 and EF Code First and I'm trying to map many to many relationships.
I have two classes;
public class Asset
{
public int Id { get; set; }
public string Title { get; set; }
public string Description { get; set; }
public virtual ICollection<Category> Categories { get; set; }
}
public class Category
{
public int CategoryId { get; set; }
public string CategoryName { get; set; }
public string Description { get; set; }
public virtual ICollection<Asset> Assets { get; set; }
}
So I'm trying to allow each Asset to have multiple Categories and each Category may have multiple Assets.
On my create method I have;
public ActionResult Create()
{
var model = new Asset();
model.Categories = _db.Categories.ToList();
return View(model);
}
In my view, the only way I can show these categories is to say; (Note the capital M in Model. I can't use the lower case model as used elsewhere in the view)
#model MyProject.Models.Asset
#using (Html.BeginForm("Create", "Assets", FormMethod.Post, new { enctype = "multipart/form-data" }))
{
<div>
#foreach (var item in Model.Categories)
{
<p>#item.CategoryName</p>
}
</div>
<div class="form-group">
#Html.LabelFor(model => model.Title, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.Title, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.Title, "", new { #class = "text-danger" })
</div>
</div>
}
When the initial create is called, I can see my asset and it has categories. On the return create method however, its null. I can't work out why. I understand I'm not doing anything to edit these categories in the View, I can't get that far. What I don't understand though is why my model leaves with categories, but comes back with none.
My create return (here my assets categories is null)
// POST: Assets/Create
[HttpPost]
public ActionResult Create(Asset model)
{
if (!ModelState.IsValid)
{
//error, return to view.
return View();
}
try
{
//do stuff
}
catch
{
return View();
}
}
Ultimately when creating an Asset, I want to be able to list all the categories and allow some selection as to which categories this new asset will belong. If someone could help me work that out, you're my hero. But if I could just understand why what's coming back isn't what I sent out, that would be a start.
In my view, the only way I can show these categories is to say; (Note the capital M in Model. I can't use the lower case model as used elsewhere in the view)
I've always hated that Microsoft uses the model => model.* convention through its generated views and tutorials and articles online; it only leads to confusion. In your view Model is an actual object instance, namely an instance of what you defined as the "model" for the view. The lowercase model you see used in things like Html.EditorFor is actually a parameter to a lambda expression. It can be called anything. For example, Html.EditorFor(x => x.Foo) and even Html.EditorFor(supercalifragilisticexpialidocious => supercalifragilisticexpialidocious.Foo) would work just as well. Although the value that gets passed into this parameter is usually the Model object instance, Model and model are totally different concepts.
When the initial create is called, I can see my asset and it has categories. On the return create method however, its null. I can't work out why. I understand I'm not doing anything to edit these categories in the View, I can't get that far. What I don't understand though is why my model leaves with categories, but comes back with none.
That is why. You're not doing anything to edit these categories in the view. There's no fields for them to be posted along with the form data, and as a result, the class instantiated by the modelbinder in your action does not contain any data for categories. This is key. The class instance that goes into the view is not the same class instance that comes back after something like a post. Each is a unique thing. The post action has no knowledge of anything that came before it; it simply has whatever data was posted. Assuming the action takes a parameter of a particular class, the modelbinder will attempt to new up an instance of that class and bind the posted data to appropriate properties on that class. It doesn't care what was sent to the view originally; it doesn't even care what class it's working with.
Ultimately when creating an Asset, I want to be able to list all the categories and allow some selection as to which categories this new asset will belong. If someone could help me work that out, you're my hero. But if I could just understand why what's coming back isn't what I sent out, that would be a start.
This is the fun part. First, you absolutely must use a view model for this. In case you're not familiar with view models, they're simply classes that are used as a model for a view, hence the name. What you're passing around here, Asset, is technically an "entity", which is a class that is used for data transfer, usually to/from a database. While an entity could be used as a model for a view, as you've done here, it's not really suited for that.
There's a clear conflict of interest, as the needs of a class representing some table schema in a database are vastly different from the needs of a class representing data for a UI layer. That's where view models come in. In the most traditional sense, a view model simply represents the data that will need to be displayed and/or edited in one or more views. It may have many properties in common with a particular entity or it may only have a subset of those properties or even completely different properties. It is the job of your application to "map" from your entity to your view model and vice-versa, so that the logic for saving an entity to a persistence store can be completely abstracted from the logic for how the user interacts with that data.
The reason a view model is so important for your purposes here is that form elements in HTML have certain limitations. They can only work with data that can be represented as a string: things like ints, bools, actual strings, etc. They are particularly unsuited for working with complex objects, like your Category class. In other words, it would be perfectly achievable to post back a list of integer ids, representing Categorys, but it is entirely implausible to post back complete Category instances that have been chosen by a user.
Since your entity expects a list of categories and your view will only feasibly be capable of posting a list of ints, there's a fundamental disconnect. Using a view model provides a way to bridge the gap. Plus, it allows you to have other properties, like a list of category choices to populate your select list with, that would be totally inappropriate to put on your entity class.
For your scenario, you'd need a view model like:
public class AssetViewModel
{
// any other asset properties you need to edit
public List<int> SelectedCategoryIds { get; set; }
public IEnumerable<SelectListItem> CategoryChoices { get; set; }
}
This then allows you to create a multiselect list in your view using:
#Html.ListBoxFor(m => m.SelectedCategoryIds, Model.CategoryChoices)
Now, to populate your view model with data from your entity. In a create view, the entity doesn't exist yet, so you don't need to do any mapping. The only thing you need to do is populate your CategoryChoices property so the select list in the view has some data. However, based on the above discussion about data needing to be posted back or else it will be null, since the actual contents of the select list will never be posted, you'll need to populate this in each of your create and edit actions, both for GET and POST. As a result, it's best to factor this logic out into a private method on your controller that each action can call:
private void PopulateCategoryChoices(AssetViewModel model)
{
model.CategoryChoices = db.Categories.Select(m => new SelectListItem
{
Value = m.Id,
Text = m.Name
};
}
Then, in your create GET action, you'll just new up your view model and populate your category choices:
public ActionResult Create()
{
var model = new AssetViewModel();
PopulateCategoryChoices(model);
return View(model);
}
In the post version, you'll now need to map the posted data onto your Asset entity:
[HttpPost]
public ActionResult Create(AssetViewModel model)
{
if (ModelState.IsValid)
{
var asset = new Asset
{
Title = model.Title,
Description = model.Description,
// etc.
Categories = db.Categories.Where(m => model.SelectedCategoryIds.Contains(m.Id))
}
db.Assets.Add(asset);
db.SaveChanges();
return RedirectToAction("Index");
}
PopulateCategoryChoices(model);
return View(model);
}
The edit GET action is similar to the create version, only this time, you have an existing entity that will need to be mapped onto an instance of your view model:
var asset = db.Assets.Find(id);
if (asset == null)
{
return new HttpNotFoundResult();
}
var model = new AssetViewModel
{
Title = asset.Title,
Description = asset.Description,
// etc.
SelectedCategoryIds = asset.Categories.Select(m => m.Id).ToList()
};
Likewise, the edit POST action is similar to the create version, but you're going to map from your view model on to an existing asset instead of creating a new asset. Additionally, because you have a many to many relationship, you have to take extra care when saving the categories.
// map data
asset.Title = model.Title;
asset.Description = model.Description;
//etc.
// You might be tempted to do the following:
// asset.Categories = db.Categories.Where(m => model.SelectedCategoryIds.Contains(m.Id));
// Instead you must first, remove any categories that the user deselected:
asset.Categories.Where(m => !model.SelectedCategoryIds.Contains(m.Id))
.ToList().ForEach(m => asset.Categories.Remove(m));
// Then you need to add any newly selected categories
var existingCategories = asset.Categories.Select(m => m.Id).ToList();
db.Categories.Where(m => model.SelectedCategoryIds.Except(existingCategories).Contains(m.Id))
.ToList().ForEach(m => asset.Categories.Add(m));
The extra footwork here is necessary to prevent saving the same relationship twice, resulting in an integrity error. By default Entity Framework creates a join table for many to many relationships that consists of a composite primary key composed of the foreign keys to each side of the relationship.
The reason why your categories is null is because you are not binding it on the POST. They are not in fields during the POST.
Try this and see if they are filled out:
#for (int i = 0; i < Model.Categories; i++)
{
#Html.TextBoxFor(model => model.Categories[i].CategoryId)
#Html.TextBoxFor(model => model.Categories[i].CategoryName)
}
1.
return View();
you are not passing back the model in your Create Method that is why you don't see the Model is NULL.
// POST: Assets/Create
[HttpPost]
public ActionResult Create(Asset model)
{
if (!ModelState.IsValid)
{
//error, return to view.
return View(model);
// If you don't pass back the model to you view you will see model is NULL
}
try
{
//do stuff
}
catch
{
return View(model);
}
}
Cateogries Will always be null in your case, as you can't post back a List like you are doing in your case.
Try Displaying them in a loop, Then the MVC model Binder will be able to bind your list to Model:
#for (int i = 0; i < Model.Categories; i++)
{
#Html.HiddenFor(model => model.Categories[i].CategoryId)
}
If you want to save SelectedCategories you will have to use MultiSelect