I am new to ASP mvc and entity framework
I have a model created by entity framework as
public partial class Privilege
{
public Privilege()
{
this.Role_Privilege_Map = new HashSet<Role_Privilege_Map>();
}
public int PrivilegeId { get; set; }
[Required(ErrorMessage="*")]
[Display(Name = "Privilege Name")]
public string PrivilegeName { get; set; }
[Required(ErrorMessage = "*")]
public Nullable<int> ModuleId { get; set; }
public virtual module module { get; set; }
public virtual ICollection<Role_Privilege_Map> Role_Privilege_Map { get; set; }
}
As you can see module is a navigational property.
I have binded this model to a view as
#for (var i = 0; i < Model.Count(); i++)
{
<tr>
<td>
#Html.DisplayFor(modelItem => modelItem[i].PrivilegeName)
#Html.HiddenFor(modelItem => modelItem[i].PrivilegeId)
#Html.HiddenFor(modelItem => modelItem[i].PrivilegeName)
</td>
<td>
#Html.DisplayFor(modelItem => modelItem[i].module.ModuleName)
#Html.HiddenFor(modelItem => modelItem[i].ModuleId)
#Html.HiddenFor(modelItem => modelItem[i].module.ModuleName)
#Html.HiddenFor(modelItem => modelItem[i].module)
</td>
<td>
#Html.CheckBoxFor(modelItem => modelItem[i].Checked)
#Html.HiddenFor(modelItem => modelItem[i].Checked)
</td>
<td>
#Html.ActionLink("Edit", "OpenEditPrivilegeDialog", "RolePrivilegeMapping",
new { id = Model[i].PrivilegeId },
new { #class = "actionHyperLink edit_Privilege_Link" }) |
#Html.ActionLink("Delete", "DeletePrivilege","RolePrivilegeMapping",
new { id = Model[i].PrivilegeId },
new { #class = "actionHyperLink Delete_Privilege_Link" })
</td>
</tr>
}
I have an Update button for updating this model say Privilege name. Now in my action
public ActionResult UpdateRolePrivilege(IList<One_Track.Models.Privilege> updatedPrivilege)
{
if (ModelState.IsValid)
{
}
else
{
ViewBag.PrivilegeMessage = "Privileges updation Failed.";
}
}
return PartialView("PrivilegePartial", sample.GetPrivilegeNames());
}
Is returning false. I put a breakpoint and come to know that the navigational property is null that could be a reason for model not been valid. How can I surpass this. As you can see in code I have added a hidden field for binding navigational property
Why is this happening? Any help will be appreciated
As per the post ModelState.IsValid == false, why? provided by wiz kid
I come to know that tan exception is occuring there as
The parameter conversion from type 'System.String' to type 'sample.Models.module' failed because no type converter can convert between these types.
at System.Web.Mvc.ValueProviderResult.ConvertSimpleType(CultureInfo culture, Object value, Type destinationType)
at System.Web.Mvc.ValueProviderResult.UnwrapPossibleArrayType(CultureInfo culture, Object value, Type destinationType)
at System.Web.Mvc.ValueProviderResult.ConvertTo(Type type, CultureInfo culture)
at System.Web.Mvc.DefaultModelBinder.ConvertProviderResult(ModelStateDictionary modelState, String modelStateKey, ValueProviderResult valueProviderResult, Type destinationType)}
So I removed the line from view
#Html.HiddenFor(modelItem => modelItem[i].module)
This solved by problem
Related
I have two classes, with 1 to many relationship, but when trying to retrieve the list of objects that is related to the Support class, that list comes empty.
public class Support
{
public int SupportId { get; set; }
public string Name { get; set; }
public string Summary { get; set; }
public virtual ICollection<FileDetail> FileDetails { get; set; }
}
public class FileDetail
{
public Guid Id { get; set; }
public string FileName { get; set; }
public string Extension { get; set; }
public int SupportId { get; set; }
public virtual Support Support { get; set; }
}
My Repository:
public abstract class Repository<TEntity> : IRepositoryRead<TEntity>, IRepositoryWrite<TEntity> where
TEntity : Entity, new()
{
public virtual IEnumerable<TEntity> GetAll()
{
return Dbset.ToList();
}
}
My Fluent API config
public class FileDetailConfig : EntityTypeConfiguration<FileDetail>
{
public FileDetailConfig()
{
HasKey(c => c.Id);
Property(c => c.FileName);
Property(c => c.Extension);
HasRequired(c =>c.Support )
.WithMany(c => c.FileDetails )
.HasForeignKey(c => c.SupportId );
ToTable("Support");
}
}
My Controller
public ActionResult Index()
{
return View(__supportRepository.GetAll());
}
My View:
#foreach (var item in Model)
{
<tr>
<td>
#Html.DisplayFor(modelItem => item.Name)
</td>
<td>
#Html.DisplayFor(modelItem => item.Summary)
</td>
<td>
#if (item.FileDetails.Count() == 0)
{
<span>No File</span>
}
else
{
<span>#item.FileDetails.Count() File(s)</span>
}
</td>
<td>
#Html.ActionLink("Edit", "Edit", new { id = item.Id }) |
Delete
</td>
</tr>
}
also tried to configure the fluent api in the FileDetail class, but the same thing remains. The list of filedetains returns empty in view
EDIT:
Thats my Support table
Support Table
Ands my FileDetail table:
FileDetain Table
EDIT:
I think the problem is the query, it looks like the query is not looking for the related data in the other table. I just can not understand why
{SELECT [Extent1].[Id] AS [Id], [Extent1].[Name] AS [Name], [Extent1].[Summary] AS [Summary] FROM [dbo].[Support] AS [Extent1]}
Since FileDetails is virtual, I'm pretty sure it will be lazy loaded. So it wont be in the initial query.
I don't understand this mapping:
HasRequired(c =>c.Support)
.WithMany(c => c.FileDetails)
.HasForeignKey(c => c.SupportId)
I don't see a Support property on your model, can you change it to just:
HasMany(c => c.FileDetails).HasForiegnKey(c => c.SupportId);
And see what you get?
You also can try Include in your query (assuming your not calling ToList(), see below):
return DBset.Include(x => x.FileDetails);
Another thing, this will load the entire table into memory:
return Dbset.ToList();
I'd highly recommend just returning the DBSet instead, further filtering will get passed to the DB:
return DBset;
I have the following viewmodel structure:
ItemViewModel containing a property Model of type BaseViewModel.
In this concrete example, Model is of type WeekManagerWorkScheduleViewModel. This viewmodel in turns consists of an IEnumerable of type ManagerWorkScheduleViewModel.
Inside the ItemView.cshtml the following is used:
<form class="form-horizontal" id="#Model.FormName" name="#Model.FormName" onsubmit="#Model.OnSubmitFunction">
<div class="form-group form-group-sm">
#Html.EditorFor(model => model.Model, Model.ItemViewName + "View")
</div>
<button class="btn pull-left" type="submit" style="margin: 2px;">
<span class="glyphicon glyphicon-save"></span>
#Model.ViewConfiguration.SaveText #Model.ItemTitle
</button>
</form>
I have omitted some details (basically a bunch of if's determining whether or not to add CRUD buttons. The Model.ItemViewName is the typename (in this case its WeekManagerWorkScheduleViewModel).
public class WeekManagerWorkScheduleViewModel : BaseViewModel
{
[HiddenInput]
public int RegionId { get; set; }
[HiddenInput]
public IEnumerable<DateTime> Dates { get; set; }
public IEnumerable<ManagerWorkScheduleViewModel> WorkSchedules { get; set; }
}
The WeekManagerWorkScheduleView.cshtml looks like this:
#using DRSTransportPortal.ViewModels
#model WeekManagerWorkScheduleViewModel
#{
ViewBag.Title = "Ugentlig arbejdsplan - ledere";
}
#Html.HiddenFor(m => m.RegionId)
<table class="table">
<thead>
<tr>
<th><b>Leder</b></th>
<th><b>Uge</b></th>
<th><b>Mandag</b></th>
<th><b>Tirsdag</b></th>
<th><b>Onsdag</b></th>
<th><b>Torsdag</b></th>
<th><b>Fredag</b></th>
<th><b>Lørdag</b></th>
<th><b>Søndag</b></th>
</tr>
<tr>
<th></th>
<th></th>
#foreach (var date in Model.Dates)
{
<th><i><small>#date.Date.ToString("dd-MM-yyyy")</small></i></th>
}
</tr>
</thead>
<tbody>
#Html.EditorFor(m => m.WorkSchedules)
</tbody>
</table>
I have a view called ManagerWorkScheduleViewModel.cshtml residing in Views/imalazysod/EditorTemplates (MVC knows this location, as I am using a custom view engine, derived from Razor):
#using DRSTransportPortal.ViewModels
#model ManagerWorkScheduleViewModel
<tr id="#Model.Id">
<td>#Html.HiddenFor(m => m.Id)</td>
<td>#Html.DisplayFor(m => m.ManagerName, new { htmlAttributes = new { #class = "form-control editoritem" } })</td>
<td>#Html.DisplayFor(m => m.DateWeekText, new { htmlAttributes = new { #class = "form-control editoritem" } })</td>
<td>#Html.EditorFor(m => m.MondayCodeChoice, new { #class = "form-control editoritem" })</td>
<td>#Html.EditorFor(m => m.TuesdayCodeChoice, new { #class = "form-control editoritem" })</td>
<td>#Html.EditorFor(m => m.WednesdayCodeChoice, new { #class = "form-control editoritem" })</td>
<td>#Html.EditorFor(m => m.ThursdayCodeChoice, new { #class = "form-control editoritem" })</td>
<td>#Html.EditorFor(m => m.FridayCodeChoice, new { #class = "form-control editoritem" })</td>
<td>#Html.EditorFor(m => m.SaturdayCodeChoice, new { #class = "form-control editoritem" })</td>
<td>#Html.EditorFor(m => m.SundayCodeChoice, new { #class = "form-control editoritem" })</td>
</tr>
The "Choice" properties are all of type ChoiceViewModel using a ChoiceViewModel.cshtml.
Now, everything renders fine:
Screenshot (names omitted). Red box indicates 1 (one) nested viewmodel
The generated HTML looks like this (only the first row and first few cells are shown here):
<tr id="134">
<td><input name="Model.WorkSchedules[0].Id" id="Model_WorkSchedules_0__Id" type="hidden" value="134" data-val-required="Feltet Id skal udfyldes." data-val="true" data-val-number="The field Id must be a number."></td>
<td>OMITTED</td>
<td>2016 - 36</td>
<td>
<select name="Model.WorkSchedules[0].MondayCodeChoice.SelectedValue" class="form-control editoritem dropdown" id="SelectChoices" onchange="">
<option value="1">06:00 - 14:00</option>
<option value="19">06:00 - 18:00</option>
<option value="31">08:00 - 16:00</option>
<option value="2">10:00 - 18:00</option>
<option value="32">10:00 - 18:00</option>
<option value="23">Bagvagt</option>
<option value="22">Ferie</option>
<option value="8">Fri</option>
<option value="3">Kontor</option>
<option value="15">Kussus</option>
<option value="16">Syg</option>
</select>
</td>
REST OF HTML IS OMMITTED (CONTINUES FOR 12 ROWS WITH 10 CELLS EACH)
</tr>
However, when I post back (using jQuery, ajax btw) this is what I get:
Controller breakpoint, after modelbinding
I have tried putting in a custom modelbinder on the BaseViewModel, and debugging that haven't seen anything that doesn't look alright. It resolves the correct types (WeekManagerWorkScheduleViewModel, etc.).
Using that, my Forms element from ControllerContext->...->Request is:
{Model.Id=141&ModelType=DRSTransportPortal.ViewModels.WeekManagerWorkScheduleViewModel%2c+DRSTransportPortal%2c+Version%3d1.0.0.0%2c+Culture%3dneutral%2c+PublicKeyToken%3dnull&Model.RegionId=1&Model.WorkSchedules%5b0%5d.Id=134&Model.WorkSchedules%5b0%5d.MondayCodeChoice.SelectedValue=22&Model.WorkSchedules%5b0%5d.TuesdayCodeChoice.SelectedValue=1&Model.WorkSchedules%5b0%5d.WednesdayCodeChoice.SelectedValue=1&Model.WorkSchedules%5b0%5d.ThursdayCodeChoice.SelectedValue=1&Model.WorkSchedules%5b0%5d.FridayCodeChoice.SelectedValue=1&Model.WorkSchedules%5b0%5d.SaturdayCodeChoice.SelectedValue=1&Model.WorkSchedules%5b0%5d.SundayCodeChoice.SelectedValue=1&..... CONTINUES FOR ALL 12 NESTED VIEWMODELS}
I have made sure the following is true:
All bindings are made to properties (default ie. {get;set;})
No viewmodels, including BaseViewModel, have constructors (ie. default constructors should be created)
All properties and classes are public
So...my question is, why is the modelbinder unable to create the viewmodels of the list? I have no problem using the template elsewhere, editing pr. person, ie. one row at a time).
Yes, I know there are MANY questions and MANY answers related to MVC modelbinding, but none seem to quite fall into this category (same goes for books). What really puzzles me, is that it is recognized that a list of 12 items is needed, but it just isn't populated.
EDIT:
public class BaseViewModelBinder : DefaultModelBinder
{
protected override object CreateModel(ControllerContext controllerContext, ModelBindingContext bindingContext, Type modelType)
{
var typeValue = bindingContext.ValueProvider.GetValue("ModelType");
var type = Type.GetType((string) typeValue.ConvertTo(typeof (string)), true);
if(!typeof(BaseViewModel).IsAssignableFrom(type))
throw new InvalidOperationException("NOT A BASEVIEWMODEL");
var model = Activator.CreateInstance(type);
bindingContext.ModelMetadata = ModelMetadataProviders.Current.GetMetadataForType(() => model, type);
return model;
}
}
[ModelBinder(typeof(BaseViewModelBinder))]
public class BaseViewModel
{
[HiddenInput]
public int Id { get; set; }
[HiddenInput]
public DateTime Created { get; set; }
[HiddenInput]
public DateTime Edited { get; set; }
public virtual string SelectionName { get; set; }
}
EDIT 2:
[HttpPost]
public override async Task<JsonResult> Save(ItemViewModel item, string parentName, int? parentId)
{
if (item?.Model == null)
{
const int result = 0;
return Json(new { result });
}
var model = item.Model as WeekManagerWorkScheduleViewModel;
if (model == null)
{
const int result = 0;
return Json(new { result });
}
foreach (var inner in model.WorkSchedules)
{
}
return await base.Save(item, parentName, parentId);
}
public class ManagerWorkScheduleViewModel : BaseViewModel
{
[HiddenInput]
public int? ManagerId { get; set; }
[DisplayName("Leder")]
public string ManagerName { get; set; }
[HiddenInput]
public int? DateWeekId { get; set; }
[DisplayName("År / Uge")]
public string DateWeekText { get; set; }
[HiddenInput]
public int? MondayCodeId { get; set; }
[UIHint("ChoiceViewModel")]
[DisplayName("Mandag")]
public ChoiceViewModel MondayCodeChoice { get; set; }
[HiddenInput]
public int? TuesdayCodeId { get; set; }
[UIHint("ChoiceViewModel")]
[DisplayName("Tirsdag")]
public ChoiceViewModel TuesdayCodeChoice { get; set; }
[HiddenInput]
public int? WednesdayCodeId { get; set; }
[UIHint("ChoiceViewModel")]
[DisplayName("Onsdag")]
public ChoiceViewModel WednesdayCodeChoice { get; set; }
[HiddenInput]
public int? ThursdayCodeId { get; set; }
[UIHint("ChoiceViewModel")]
[DisplayName("Torsdag")]
public ChoiceViewModel ThursdayCodeChoice { get; set; }
[HiddenInput]
public int? FridayCodeId { get; set; }
[UIHint("ChoiceViewModel")]
[DisplayName("Fredag")]
public ChoiceViewModel FridayCodeChoice { get; set; }
[HiddenInput]
public int? SaturdayCodeId { get; set; }
[UIHint("ChoiceViewModel")]
[DisplayName("Lørdag")]
public ChoiceViewModel SaturdayCodeChoice { get; set; }
[HiddenInput]
public int? SundayCodeId { get; set; }
[UIHint("ChoiceViewModel")]
[DisplayName("Søndag")]
public ChoiceViewModel SundayCodeChoice { get; set; }
}
Ah, stupid me.
I forgot how I actually made this framework to begin with.
I created this mainly for CRUD operations, so I only need to define viewmodel (derived from BaseViewModel), view, repository and dto types for each business entity as well as an empty controller. Then everything else is using a standard set of controllers (three levels of derivation, depending on functionality needed) and all I need to do is create a config entry for the business entity, defining the vm, view, repo types as well as some CRUD options for buttons and labels.
All top level viewmodels and view are the same, like ItemView, ItemViewModel, ItemListView, SingleSelector, MultipleSelector etc etc.
What I had FORGOTTEN was that I limited myself to one business entity at a time, hence outputting the class (see the modelbinder), so I COULD use derived viewmodels...this works great, except obviously in this case.
What happens is, as the binding tries to populate the list, it keeps trying to populate it using the first viewmodel type (as this is the only one known after POST) and not the derived. DAMMIT! So back to the drawing table (no nesting :( )
I have this model class:
public class UserProfile
{
[Key]
[DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
public int UserId { get; set; }
public string UserName { get; set; }
public DbSet<ForeignExpression> learnedexpressions { get; set; }
}
I create the dbset in the controller:
int userID = (int)Membership.GetUser().ProviderUserKey;
model.ForeignExpressions=db.UserProfiles.Find(userID).learnedexpressions;
Then in the view, I try to write them out:
#foreach (var exp in Model.ForeignExpressions.OrderByDescending(m => m.dateAdded).Take(#numOfExpressions).ToList().OrderBy(m => m.dateAdded))
{
<tr>
<td> #exp.expression</td>
<td> #exp.meaning</td>
<td> "#exp.context"</td>
<td> #exp.dateAdded</td>
</tr>
}
However, Model.ForeignExpressions is null, so I get a runtime error for the foreach (Value cannot be null). I suspect that it's because I don't have any ForeignExpression yet for the current user, so the value of learnedexpressions is null. Am I correct, and what is the best way to handle this? Should I fix it in the view, the controller, or the model?
Make the learnedespressions property virtual so as to allow lazy loading of the collection and change the type of the property from DbSet<T> to ICollection<T>
Test whether ForeignExpressions is null prior to attempting to use it:
#if (Model.ForeignExpressions != null)
{
// foreach
}
I have an MVC app that I am trying to put together that requires some select lists and drop down lists.
So, I have the following models....
public class Task
{
public int ID { get; set; }
public string Title { get; set; }
......
public virtual ICollection<Monitor> Monitors { get; set; }
public virtual ICollection<Resource> Resources { get; set; }
}
public class Monitor
{
public int ID { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public IList<Task> Tasks { get; set; }
}
public class Resource
{
public int ID { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
.....
public IList<Task> Tasks { get; set; }
The interesting part is that when I display a list of tasks, among the other properties that display just fine, I need to display a list of 'Monitors' and a list of 'Resources' that are assigned to the task in the Index view shown below.
#model IEnumerable<ResourceManager.Models.Task>
#{
ViewBag.Title = "Index";
}
<h2>Index</h2>
<p>
#Html.ActionLink("Create New", "Create")
</p>
<table>
<tr>
.....
<th>
#Html.DisplayNameFor(model => model.Title)
</th>
.....
<th>
#Html.DisplayNameFor(model => model.Monitors)
</th>
<th>
#Html.DisplayNameFor(model => model.Resources)
</th>
<th></th>
</tr>
#foreach (var item in Model) {
<tr>
.....
<td>
#Html.DisplayFor(modelItem => item.Title)
</td>
.....
<td>
#if (item.Monitors == null || item.Monitors.Count == 0)
{<span>No Monitors Assigned</span>}
else
{ string.Join(", ", item.Monitors.Select(m => string.Format("{0} {1}", m.FirstName, m.LastName))); }
</td>
<td>
#Html.DisplayFor(modelItem => item.Resources)
</td>
<td>
#Html.ActionLink("Edit", "Edit", new { id=item.ID }) |
#Html.ActionLink("Details", "Details", new { id=item.ID }) |
#Html.ActionLink("Delete", "Delete", new { id=item.ID })
</td>
</tr>
}
</table>
And here is the controller....
public ActionResult Index()
{
var tasks = from t in db.Tasks where t.IsActive == true select t;
return View(tasks);
}
I would like for the list of Monitors and the list of Resources to display as a string on the Index, Delete and Details Views i.e. 'Monitor 1, Monitor 2, Monitor 3' and 'Resource 1, Resource 2, Resource 3'.
However on the other views (Create and Edit), I want them to appear as a selectable list.
First Create Select list in your controller,
var monitors = //Your linq query
ViewData["ddlList"] = monitors .Select(x => new SelectListItem {
Text = x.FirstName,
Value = x.Id.ToString()
}).ToList();
And then you can use it in your view as follows,
<%=Html.DropDownList("myList") %>
For the display of Monitors/Resources (and since you want them displayed as a comma-delimited list), you can just use string.Join:
<td>
#string.Join(",", Model.Monitors.Select(m => string.Format("{0} {1}", m.FirstName, m.LastName)))
</td>
To be able to actually use Html.DisplayFor, you'd have to create a custom display template so Razor will actually know how to respond. To do so, in your Views folder, create new folder called "DisplayTemplates", and in that, create a new partial view called "Monitors.cshtml" and "Resources.cshtml", strongly-typed to IEnumerable<Monitor> and IEnumerable<Resource>, respectively. Then inside that file, you'll just add roughly the same code as above:
#model IEnumerable<Monitor>
#string.Join(",", Model.Select(m => string.Format("{0} {1}", m.FirstName, m.LastName)))
Then in your view, you can call:
#Html.DisplayFor(m => m.Monitors, "Monitors")
Unfortunately, in this example, you'd have to feed the template name because the default behavior of DisplayFor for a list, is to render the display template multiple times, once for each member of the list. You could do something like:
# Monitor.cshtml
#model Monitor
#Model.FirstName #Model.LastName,
And then:
#Html.DisplayFor(m => m.Monitors)
But your last item would have a comma at the end.
For editing the lists, all you have to do is pass the select lists to your view. Optimally, you'd do this with a view model, but for simplicity's sake, I'll just use ViewBag here. In your controller action:
ViewBag.MonitorChoices = db.Monitors.Select(m => new SelectListItem
{
Value = m.ID.ToString(),
Text = string.Format("{0} {1}", m.FirstName, m.LastName)
});
Then, in your create/edit view:
#Html.ListBoxFor(m => m.Monitors, ViewBag.MonitorChoices)
Try as follows,
var monitors = //Your linq query
List monitorList = new List();
foreach (var monitor in monitors ){
SelectListItem item = new SelectListItem();
item.Text = monitor.FirstName;
item.Value = monitor.Id.ToString();
monitorList .Add(item);
}
ViewData["ddlList"] = monitorList;
I have generated a strongly-type view from a model class that I have created from scratch. I have used the List as the scaffold template. The error appears at the foreach line with the model object.
View:
#model IEnumerable<HockeyPoolStats.Models.Player>
#{
ViewBag.Title = "Index";
}
#foreach (var item in Model) {
<tr>
<td>
#Html.DisplayFor(modelItem => item.Team_Id)
</td>
<td>
#Html.DisplayFor(modelItem => item.Rank)
</td>
<td>
#Html.DisplayFor(modelItem => item.PlayerName)
</td>
<td>
#Html.DisplayFor(modelItem => item.PlayerTeam)
</td>
[...]
Controller:
public class PlayerController : Controller
{
private HockeyPoolStatsDB _db = new HockeyPoolStatsDB();
//
// GET: /Player/
public ActionResult Index()
{
Player _player = new Player();
_db.Players.Add(_player);
var model = _db.Players;
return View(model);
}
}
DbContext
public class HockeyPoolStatsDB : DbContext
{
public DbSet<Player> Players { get; set; }
public DbSet<Team> Teams { get; set; }
public DbSet<Goalie> Goalies { get; set; }
}
I don't understand why model is null. This means that the controller isn't passing the model to the view correctly?
It more likely means that _db.Players is returning null.
Set a breakpoint and step through it and verify that _db.Players is actually returning a list of players.