How to post IEnumerable within a list to controller - asp.net-mvc-3

I have a MVC3 app with the following model
public class Class1
{
public List<Class2> Class2Data { get; set; }
}
public class Class2
{
public int Id { get; set; }
public IEnumerable<Class3> Class3Data { get; set; }
}
public class Class3
{
public int Id { get set; }
public bool Selected { get; set; }
}
In my razor model
for (var i = 0; i < Model.Class2Data.Count();i++)
{
#Html.HiddenFor(c => c.Class2Data[i].Id)
foreach (var n in Model.Class2Data[i].Class3Data.ToList())
{
#Html.CheckBoxFor(x=> n.Selected);
#Html.HiddenFor(x=> n.Id);
}
}
However, when iam posting this to my controller, the Class3Data count is always 0 when I tick the checkboxes. Any ideas? Thanks

Resolved by changing IEnumerable to List as wasn't binding properly

Related

How do I custom validate collections withing MVC5

I am building an MVC5 application and I have the following viewmodels:
public class UserPartyViewModel
{
public UserPartyViewModel()
{
Entitlements = new Collection<AssignedClaims>();
}
public Guid PartyID { get; set; }
public string PartyName { get; set; }
public ICollection<AssignedClaim> AssignedClaims{ get; set; }
}
public class AssignedClaims
{
public AssignedClaims()
{
ClaimValues = new Collection<AssignedClaimValue>();
}
public string Name { get; set; }
public int Max { get; set; }
public int Min { get; set; }
public ICollection<AssignedClaimValue> ClaimValues { get; set; }
}
public class AssignedClaimValue
{
public Guid ClaimValueID { get; set; }
public string ClaimValue { get; set; }
public bool Assigned { get; set; }
}
Contained in the UserPartyViewModel will always be an assignedclaim with a name of "Security" and the assignedclaimvalue with a claimvalue of "User"
If the ClaimValue of user is Assigned then I need to validate the rest of the model. If it is not then no further validation should take place.
Within AssignedClaims there is a min and max, these are the minimum and maximum number of assignedclaimvalues that should be Assigned.
I have tried to use AttributeValidate cannot stop it validating the rest of the model.
I have also looked at the IValidatableObject interface but also can't work out how to control the validation of the child collections depending on the User claim.
What's the best way to achieve this?
Found a solution which appears to do what I want:
public class UserPartyViewModel : IValidatableObject
{
public UserPartyViewModel()
{
Entitlements = new Collection<AssignedClaims>();
}
public string AccessLevel { get; set; }
public Guid PartyID { get; set; }
public string PartyName { get; set; }
public ICollection<AssignedClaims> Entitlements { get; set; }
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
var isUser = Entitlements.Any(c => c.Name == "Security" && c.ClaimValues.Any(v => v.Assigned == true && v.ClaimValue == "User"));
if (isUser)
{
int i = 0;
foreach (var result in Entitlements)
{
yield return result.Validate(i++);
}
}
else
{
yield return ValidationResult.Success;
}
}
}
public class AssignedClaims
{
public AssignedClaims()
{
ClaimValues = new Collection<AssignedClaimValue>();
}
public string Name { get; set; }
public string Description { get; set; }
public int Max { get; set; }
public int Min { get; set; }
public ICollection<AssignedClaimValue> ClaimValues { get; set; }
public ValidationResult Validate(int item)
{
int min = Min;
int max = (ClaimValues.Count() < Max) ? ClaimValues.Count() : Max;
int assignedCount = ClaimValues.Where(i => i.Assigned == true).Count();
if (!(min <= assignedCount && assignedCount <= max))
{
string errMessage = String.Format("{2} should have between {0} and {1} Security Claims checked.", min, max, Name);
return new ValidationResult(errMessage, new[] { string.Format("Entitlements[{0}]", item) });
}
else
{
return ValidationResult.Success;
}
}
}
The only issue I had was trying to get the error messages appearing in the correct place. In my view for assignedclaims I added:
#Html.ValidationMessageFor(model => model, "", new { #class = "text-danger" })
and passed the iteration through to the validate function on assignedclaim to ensure it was added to the correct member.

how to bind a dropdownlist to model properly to be able to pass values back from view to controller

I am trying to update a compound page model which as one of its properties has a list of objects.
My Model looks like this:
public class PageViewModel
{
public ProgramListVM ProgramsDDL { get; set; }
public PageViewModel()
{
this.ProgramsDDL = new ProgramListVM();
}
}
The ProgramListVM class is:
public class ProgramListVM
{
public List<ProgramVM> Program_List { get; set; }
public int SelectedValue { get; set; }
public ProgramListVM()
{
this.Program_List = new List<ProgramVM>();
this.SelectedValue = 0;
}
}
and ProgramVM is:
public class ProgramVM
{
public int ProgramID { get; set; }
public string ProgramDesc { get; set; }
public ProgramVM(int id, string code)
{
this.ProgramID = id;
this.ProgramDesc = code;
}
}
I try to render this dropdownlist by the following two:
1-
<%: Html.DropDownList("ProgramsDDL", new SelectList(Model.Page6VM.ProgramsDDL.Program_List, "ProgramID", "ProgramDesc", Model.Page6VM.ProgramsDDL.SelectedValue))%>
2-
<%: Html.DropDownListFor(m => m.Page6VM.ProgramsDDL.Program_List, new SelectList(Model.Page6VM.ProgramsDDL.Program_List, "ProgramID", "ProgramDesc"), Model.Page6VM.ProgramsDDL.SelectedValue)%>
But when I try to update my model through a controller action
[HttpPost]
public ActionResult UpdateUser(PageViewModel model)
{
}
model.ProgramsDDL.count is zero.
What is the best way to render this dropdownlist and be able to set the selected index, and also be able to send the selected index back to the controller?
You mixed up the parameters for Html.DropDownListFor(). Code sample below should work.
<%: Html.DropDownListFor(m => m.SelectedValue,
new SelectList(Model.Page6VM.ProgramsDDL.Program_List, "ProgramID", "ProgramDesc"),
null) %>
You also should have a SelectedValue in your model that's posted back.
public class PageViewModel
{
public ProgramListVM ProgramsDDL { get; set; }
public int SelectedValue { get; set; }
public PageViewModel()
{
this.ProgramsDDL = new ProgramListVM();
}
}
Also default model binder can't map complex collections to your model. You probably don't need them in your post action anyway.

cant insert complex object to database using entity frame work

I am developing an asp.net mvc application, which has these enity classes:
public class Person
{
public int PersonID { get; set; }
public string PersonPicAddress { get; set; }
public virtual List<Person_Local> PersonLocal { get; set; }
}
public class Person_Local
{
public int PersonID { get; set; }
public int CultureID { get; set; }
public string PersonName { get; set; }
public string PersonFamily { get; set; }
public string PersonAbout { get; set; }
public virtual Culture Culture { get; set; }
public virtual Person Person { get; set; }
}
public class Culture
{
public int CultureID { get; set; }
[Required()]
public string CultureName { get; set; }
[Required()]
public string CultureDisplay { get; set; }
public virtual List<HomePage> HomePage { get; set; }
public virtual List<Person_Local> PersonLocak { get; set; }
}
I defined an action with [Httppost] attribute, which accepts complex object from a view.
Here is the action :
[HttpPost]
public ActionResult CreatePerson([Bind(Prefix = "Person")]Person obj)
{
AppDbContext da = new AppDbContext();
//Only getting first PersonLocal from list of PersonLocals
obj.PersonLocal[0].Person = obj;
da.Persons.Add(obj);
da.SaveChanges();
return Jsono(...);
}
But when it throws error as below :
Exception:Thrown: "Invalid column name 'Culture_CultureID'." (System.Data.SqlClient.SqlException)
A System.Data.SqlClient.SqlException was thrown: "Invalid column name 'Culture_CultureID'."
And the insert statement :
ADO.NET:Execute Reader "insert [dbo].[Person_Local]([PersonID], [PersonName], [PersonFamily], [PersonAbout], [Culture_CultureID])
values (#0, #1, #2, #3, null)
select [CultureID]
from [dbo].[Person_Local]
where ##ROWCOUNT > 0 and [CultureID] = scope_identity()"
The command text "insert [dbo].[Person_Local]([PersonID], [PersonName], [PersonFamily], [PersonAbout], [Culture_CultureID])
values (#0, #1, #2, #3, null)
select [CultureID]
from [dbo].[Person_Local]
where ##ROWCOUNT > 0 and [CultureID] = scope_identity()" was executed on connection "Data Source=bab-pc;Initial Catalog=MainDB;Integrated Security=True;Application Name=EntityFrameworkMUE", building a SqlDataReader.
Where is the problem?
Edited:
Included EntityConfigurations Code:
public class CultureConfig : EntityTypeConfiguration<Culture>
{
public CultureConfig()
{
HasKey(x => x.CultureID);
Property(x => x.CultureName);
Property(x => x.CultureDisplay);
ToTable("Culture");
}
}
public class PersonConfig : EntityTypeConfiguration<Person>
{
public PersonConfig()
{
HasKey(x => x.PersonID);
Property(x=>x.PersonPicAddress);
ToTable("Person");
}
}
public class Person_LocalConfig : EntityTypeConfiguration<Person_Local>
{
public Person_LocalConfig()
{
HasKey(x => x.PersonID);
HasKey(x => x.CultureID);
Property(x=>x.PersonName);
Property(x => x.PersonFamily);
Property(x => x.PersonAbout);
ToTable("Person_Local");
}
}
Try to remove fields CultureID and PersonID from Person_Local class. Because you already has field Person and Culture
It looks like your schema is out of sync with your model. Make sure you understand EF Code first schema update features, which are described in this blog. If you need more sophisticated schema migration, there are some other approaches in answers to this question.

ASP.NET MVC3 - Entity Framework: Many-to-many relation (news and categories)

I want to create categories for news. It will be many-to-many relation. How do that properly? I have created two classes:
public class News
{
public News()
{
this.NewsCategories = new List<NewsCategory>();
}
public int ID { get; set; }
public DateTime Date { get; set; }
public string Title { get; set; }
public string Text { get; set; }
public IEnumerable<NewsCategory> NewsCategories { get; set; }
}
public class NewsCategory
{
public NewsCategory()
{
this.News = new List<News>();
}
public int ID { get; set; }
public string Name { get; set; }
public IEnumerable<News> News { get; set; }
}
But EF create just two tables...without Join table. I have created also custom DbInitializer:
public class TouristGuideDBInitializer : DropCreateDatabaseAlways<TouristGuideDB>
{
protected override void Seed(TouristGuideDB context)
{
base.Seed(context);
context.NewsCategories.Add(new NewsCategory { Name = "Default" });
context.NewsCategories.Add(new NewsCategory { Name = "Second" });
context.News.Add(new News { Date = DateTime.Now, Text = "asasdfas fasdfa sdf asf asf", Title = "Hello world" });
context.SaveChanges();
var news = context.News.First();
var cat = context.NewsCategories.Where(r => r.Name == "Default").Single();
news.NewsCategories.ToList().Add(cat);
context.SaveChanges();
}
}
But it just add one news and two categories...without relationships...
How it should be done properly (the relations)?
You need to use ICollection<T> for navigation properties.

Silverlight RIA request only returns 1

I have the following code...
internal sealed class Menu_Metadata
{
private Menu_Metadata() { }
[Key]
public int MenuHeaderID { get; set; }
public string MenuHeaderName { get; set; }
[Include]
[Association("MenuHeader_MenuItem", "MenuHeaderID", "MenuHeaderID")]
public IEnumerable<MenuItem> MenuItems { get; set; }
}
public class EmployeeMenuItem
{
[Key]
public int MenuItemID { get; set; }
public int MenuHeaderID { get; set; }
public string MenuItemName { get; set; }
}
[MetadataType(typeof(Menu_Metadata))]
public class EmployeeMenu
{
public int MenuHeaderID { get; set; }
public string MenuHeaderName { get; set; }
public IEnumerable<EmployeeMenuItem> MenuItems { get; set; }
}
[EnableClientAccess()]
public class EmployeeMenuService : DomainService
{
public IQueryable<EmployeeMenu> GetEmployeeMenu()
{
BusinessLogic.Employee blEmployee = new BusinessLogic.Employee();
int employeeId = blEmployee.GetEmployeeIdFromUserName(HttpContext.Current.User.Identity.Name);
var menuHeaders = blEmployee.GetEmployeeMenuHeaders(employeeId);
// This works here!
IQueryable<EmployeeMenu> retValue = from mh in menuHeaders
select new EmployeeMenu
{
MenuHeaderID = mh.ID,
MenuHeaderName = mh.HeaderName,
MenuItems = from mhi in mh.MenuHeaderItems
select new EmployeeMenuItem
{
MenuItemID = mhi.MenuItemID,
MenuHeaderID = mhi.MenuHeaderID,
MenuItemName = mhi.MenuItem.MenuItemName
}
};
return retValue;
}
}
which is consumed by a Silverlight Accordion control
EmployeeMenuContext employeeMenuContext = new EmployeeMenuContext();
accordion2.ItemsSource = employeeMenuContext.EmployeeMenus;
employeeMenuContext.Load(employeeMenuContext.GetEmployeeMenuQuery());
The MenuHeaderName's are coming through just fine, and the MenuItems is populated for the 1st MenuHeader, but the other 3 MenuItems are empty.
Any ideas why?
At what point is it easier to use EF4 and RIA??? This seems so incredibly and needlessly complex to get a simple Entity with a sub-class in it!
I'm not entirely sure, but it appears that the problem may have been that I was trying to databind in the xaml constructor. I created a Loaded event and moved the code there and it seems to work now.

Resources