Generating a dropdown list with database first - asp.net

In my mvc3 application i want to populate the dropdownlist for the data which is coming from database here am using entityframework with database first approach so please help me to do this

You did not provide any code of what you have done so far so I know nothing about the data that you have been working with. I will post some code and all that you will have to do is to modify it to fit in with your scenario.
Lets work with a simple solution of loan applications. A client needs to apply for a loan and he needs to supply banking details. He will have to select a bank from a drop down list.
Lets start with the domain model called Bank. This represents your data coming from your database table. Lets call the table Banks.
public class Bank
{
public int Id { get; set; }
public string Name { get; set; }
}
Your table called Banks will look like this:
Id | int | not null | primary key
Name | varchar(50) | not null
Depeneding on you what you need to do I normally have a service layer that calls my bank repository to bring back the data. But seeing that you only need to bring back data and nothing else we can skip the service layer.
public class BankRepository : RepositoryBase<Bank>, IBankRepository
{
public IEnumerable<Bank> FindAll()
{
return DatabaseContext.Banks;
}
}
You database context will look like this:
public class DatabaseContext : DbContext
{
public DbSet<Bank> Banks { get; set; }
}
This is how your data retrieval methods could look like. It might not be the full solution but there are many samples online. Just go and Google.
Lets move onto the web application.
Your view/page will work with a view model and not your domain model. You use view models to represent your data on the view/page. So on your create view you will pass in a create application view model with a list of your banks. An instance of IBankRepository will be supplied through a technique called dependency injection. I use Autofac for this.
public class ApplicationViewModel
{
public int BankId { get; set; }
public IEnumerable<Bank> Banks { get; set; }
}
Your controller's action method will populate the view model and send it to your view.
public class ApplicationController : Controller
{
private readonly IBankRepository bankRepository;
public ApplicationController(IBankRepository bankRepository)
{
this.bankRepository = bankRepository;
}
public ActionResult Create()
{
ApplicationViewModel viewModel = new ApplicationViewModel
{
Banks = bankRepository.FindAll()
};
return View(viewModel);
}
}
Your view will then receive this view model and do with it what it needs to do. In this case it will populate your bank drop down.
#model YourProject.ViewModels.Applications.ApplicationViewModel
#using (Html.BeginForm())
{
<tr>
<td class="edit-label">Bank: <span class="required">**</span></td>
<td>
#Html.DropDownListFor(
x => x.BankId,
new SelectList(Model.Banks, "Id", "Name", Model.BankId),
"-- Select --"
)
#Html.ValidationMessageFor(x => x.BankId)
</td>
</tr>
}
I don't what your experience is but judging from your question it seems like you still need to do a lot of research. There are tonnes of examples online. You will need to work through samples of Entity Framework code first and ASP.NET MVC. Invest some time and you will reap the rewards later.
I hope this works and best of luck. The solution might not be what you want but it can help guide you in the right direction.

Let assume you have a class like so
public class ProductBrand
{
//Eg. Nokia, Hp, Dell
/// <summary>
/// Second Level to Category
/// Has Foreign Key to category table
/// </summary>
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int ProductBrandId { get; set; }
[Display(Name = "Category")]
public int ProductCategoryId { get; set; }
public virtual ProductCategory ProductCategory { get; set; }
[Required(ErrorMessage = "This field is required")]
[StringLength(300, ErrorMessage = "This field must not be greater than 300 xters long")]
[Display(Name="Name")]
public string BrandName { get; set; }
public IList<Product> Products { get; set; }
[Display(Name = "Status")]
public bool ActiveStatus { get; set; }
}
Which means this model has a foreign key name ProductCategoryId then in your create Product brand view you will need a dropdownlist containing All productcategory to choose from.
Just create an actionresult like so
public ActionResult CreateProductBrand() {
ViewBag.ProductCategoryId = new SelectList(context.ProductCategories, "ProductCategoryId", "CategoryName");
return View();
}
Then call the viewbag in you corrensponding view like so:
#using (Html.BeginForm()) {
#Html.AntiForgeryToken()
#Html.ValidationSummary(true)
<table style="width:400px" class="post-form">
<tr>
<td>Category</td>
<td>
<div class="editor-field">
#Html.DropDownList("ProductCategoryId", String.Empty)
#Html.ValidationMessageFor(model => model.ProductCategoryId)
</div>
</td>
</tr>
</table>
<table style="width:400px" class="post-form">
<tr>
<td>Brand</td>
<td>
<div class="editor-field">
#Html.EditorFor(model => model.BrandName)
#Html.ValidationMessageFor(model => model.BrandName)
</div>
</td>
</tr>
<tr>
<td>Status</td>
<td>
<div class="editor-field">
#Html.EditorFor(model => model.ActiveStatus)
#Html.ValidationMessageFor(model => model.ActiveStatus)
</div>
</td>
</tr>
<tr>
<td></td>
<td>
<input type="submit" class="button blue" value="Save" />
<a href="#Url.Action("ProductBrand", "ProductSetup")" class="button">Cancel
</a>
</td>
</tr>
</table>
}

ViewModel:
public List<Item> _Items { get; set; }
public int_newID { get; set; }
Model Item
public int_id { get; set; }
public string _name { get; set; }
Controller:
Populate _Items with data and send that list to your view
View:
#Html.DropDownListFor(c => c._newID, new SelectList(Model._Items , "_id", "_name"),--Select Item--")

Related

ASP.NET MVC - Entity framework Lazing Loading is not retrieving data from related classes, using fluent API

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;

Binding list of nested viewmodels on post ASP.NET MVC

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 :( )

How to code an auction site with ASP.Net/ MVC 5 / EF

I'm relatively new to ASP.NET so please bear with me.
I'm trying to code a straightforward auction site for a charity, using MVC 5 and Entity Framework with Code-First.
I have created an Item model and controller. The Item model holds fields like the title, description, starting bid, current bid, number of bids, high bidder, etc.
public class Item
{
public int ID { get; set; }
public string Code { get; set; }
public string Title { get; set; }
public string Description { get; set; }
[DisplayName("Starting Bid")] public int StartingBid { get; set; }
public int Increment {get; set;}
public int Bids { get; set; }
[DisplayName("Current Bid")] public int CurrentBid { get; set; }
public string HighBidder { get; set; }
public int CurrentOrStartingBid
{ // The price that is displayed next to an item
get
{
return Bids > 0 ? CurrentBid : StartingBid;
}
}
public int NextBid
{ // The minimum amount that is valid for a new bid
get
{
return Bids > 0 ? CurrentBid + Increment : StartingBid;
}
}
}
(What I have tried to do in the code above is to add these properties CurrentOrStartingBid and NextBid which are not intended to be part of the database record, they are just derivative properties rather than columns in the DB. So hopefully having these as read-only properties will do that...)
I am now making a view for the item detail. This shows the item description, and also features form controls for placing a bid, similar to what you would see on eBay. My question is about how to wire up the logic for the Bid button correctly.
I figure that in the view I should use an HTML form with a submit button for the bidding. This does an HTTP post when the button is hit, and allows me to write a method in the Item controller that gets called at that time.
So my Razor code in the view currently looks like this:
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
#Html.ValidationSummary(true)
<div class="form-group">
<div class="input-group">
<span class="input-group-addon">£</span>
<input type="number" class="form-control" value="#Model.NextBid" id="CurrentBid" name="CurrentBid"/>
</div>
<br />
<button type="submit" class="btn btn-primary btn-lg">Place bid</button>
</div>
}
Using this code allows me to write a controller method with this signature:
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Bid([Bind(Include = "ID,CurrentBid")] Item item)
This kind of works so far. But note that I am having to pass the amount that has been bid in the CurrentBid field of the item, before it has been validated server-side. This doesn't feel quite right to me. Is there a way of writing the method so it just takes a signature like this?
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Bid(int itemID, int bidAmount)
Maybe there's a way to do that with query strings or something?
Anyway, once inside the method, things again seem a little weird. Protecting from over-posting, the only fields in the item variable that are valid are ID and CurrentBid. So I then do a lookup in the DbContext to find the actual item that corresponds with that ID and update it:
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Bid([Bind(Include = "ID,CurrentBid")] Item item)
{
if (ModelState.IsValid)
{
Item i2 = db.Items.Find(item.ID);
if (item.CurrentBid >= i2.NextBid)
{
i2.Bids++;
i2.CurrentBid = item.CurrentBid;
i2.HighBidder = HttpContext.User.Identity.Name;
db.Entry(i2).State = EntityState.Modified;
db.SaveChanges();
}
return View(i2);
}
return RedirectToAction("Auction");
}
This seems to work. But it does not feel right. I think I am missing some important concepts/patterns here. If any experienced MVC folks could sketch how they would wire up the client/server logic for this simple button, that would be great. (You would also be helping a good cause!)
Is there a way of writing the method so it just takes a signature like
this?
[HttpPost] [ValidateAntiForgeryToken]
public ActionResult Bid(int itemID, int bidAmount)
Sure there is a way, lets say you have a simple model like this -
public class Item
{
public int ID { get; set; }
public int NextBib { get; set; }
public int CurrentBid { get; set; }
public string Description { get; set; }
}
Then your Action is creating an Item and sending to View as shown below -
public ActionResult BidForm()
{
Item i = new Item();
i.ID = 100;
i.CurrentBid = 10;
return View(i);
}
And your view is as follows -
#model MVC.Controllers.Item
#{
ViewBag.Title = "BidForm";
}
<h2>BidForm</h2>
#using (Html.BeginForm("Bid", "sample", FormMethod.Post))
{
#Html.AntiForgeryToken()
<input type="hidden" name="itemId" value="#Model.ID" />
<input type="number" class="form-control" value="#Model.NextBib" id="CurrentBid" name="bidAmount" />
<input type="submit" value="Create" />
}
And your NextBid action would be -
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Bid(int itemID, int bidAmount)
{
return View();
}
As you see we have an HiddenField with name = itemID and a input type=number field with name = `bidAmount. Those are mapped to parameters of the action as shown below -
Some recommendations which you can consider -
Instead of using regular HTml.BeginForm(), you can go for Ajax.BeginForm() to give more intuitive user experience. Alternatively you can use JQuery POST operation too.
To prevent OVER posting, you need to create specific ViewModels for specific activities like increasing a bid, displaying a product etc. And you post only required viewModels for required activities, so that you can prevent over posting of form.

asp net mvc3 post a list of objects to action

I created a page with aspnet mvc3. It show all users info as a list. I want to do something with this list. There are some checkboxes that belong to each items. When I click some checkboxes and press submit button, I want to post the whole list as a collection and save each items of this collection to database. There are several notes on internet but there is no exact solution. I have a UserDto. and want to use this to transfer users data in all sections.
Does anyone have any full solution about this or can they give any idea?
Thanks in advance.
Kerem
I added some of my codes. You can see the lead sentences what they are about.
this is my index view detail:
#model List<DomainModel.UserApprovalDto>
#{
ViewBag.Title = "Manage Users";
Layout = "~/Views/Shared/_Layout.cshtml";
}
<h2>
Manage Users</h2>
<div>#Html.Partial("_PartialManageUsers", (List<DomainModel.UserApprovalDto>)Model) </div>
this is my partial view detail:
#model List<DomainModel.UserApprovalDto>
#using (Html.BeginForm("ConfirmUsers", "ManageUsers", FormMethod.Post))
{
<table>
<tr>
<th>
Name
</th>
<th>
Is Reported
</th>
</tr>
#for (int i = 0; i < Model.Count(); i++)
{
<tr>
<td>
#Html.DisplayFor(modelItem => Model[i].FirstName)
</td>
<td>
#Html.CheckBox("IsReported", Model[i].IsReported.HasValue ? Model[i].IsReported.Value : false)
#*#Html.CheckBoxFor(modelItem => Model[i].IsReported.Value);*# #* #if (Model[i].IsReported != null)
{
#Html.CheckBoxFor(modelItem => Model[i].IsReported.Value);
}
else
{
#Html.CheckBoxFor(modelItem => Model[i].IsReported.Value);
}*#
</td>
<td>
</td>
</tr>
}
</table>
<div>
<input name="submitUsers" type="submit" value="Save" />
</div>
}
this is my controller submit method
[HttpPost]
public ActionResult ConfirmUsers(List<DomainModel.UserApprovalDto> collection)
{
if (ModelState.IsValid)
{
//TO-DO
}
return RedirectToAction("Index");
}
this last one is my DTO class detail:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace DomainModel
{
public class UserApprovalDto
{
public long UserId { get; set; }
public Guid CarUserId { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string PhotoPath { get; set; }
public string PhotoSmallPath { get; set; }
public string PhotoSquarePath { get; set; }
public string PhotoBigPath { get; set; }
public bool IsBlocked { get; set; }
public bool IsDeleted { get; set; }
}
}
when I submit this code my list return null collection to my controller method.
thanks for your comments.
Assuming you are creating a screen which adds/ remove users to a course. So let's create some viewmodels
public class CourseVM
{
public string Name { set;get;}
public int CourseID { set;get;}
public List<UserVM> Users { set;get;}
public CourseVM()
{
Users=new List<UserVM>();
}
}
public class UserVM
{
public string Name { set;get;}
public int UserID{ set;get;}
public bool IsSelected { set;get;}
}
Now in your GET Action, you will fill the values of the ViewModel and sent it to the view.
public ActionResult Add()
{
var vm = new CourseVM();
//The below code is hardcoded for demo. you may replace with DB data.
vm.Users.Add(new UseVM { Name = "Jon" , UserID=1});
vm.Users.Add(new UseVM { Name = "Scott", UserID=2 });
return View(vm);
}
Now Let's create an EditorTemplate. Go to Views/YourControllerName and Crete a Folder called "EditorTemplates" and Create a new View there with the same name as of the Property Name(UserVM.cshtml)
Add this code to your new editor template.
#model ChannelViewModel
<p>
<b>#Model.Name</b> :
#Html.CheckBoxFor(x => x.IsSelected) <br />
#Html.HiddenFor(x=>x.Id)
</p>
Now in your Main View, Call your Editor template using the EditorFor Html Helper method.
#model CourseVM
#using (Html.BeginForm())
{
<div>
#Html.EditorFor(m=>m.Users)
</div>
<input type="submit" value="Submit" />
}
Now when you post the form, Your Model will have the Users Collection where the Selected Checkboxes will be having a True value for the IsSelected Property.
[HttpPost]
public ActionResult Add(CourseVM model)
{
if(ModelState.IsValid)
{
//Check for model.Users collection and Each items
// IsSelected property value.
//Save and Redirect(PRG pattern)
}
return View(model);
}

Dynamic link issue in an MVC 3 website

I'm creating my first ASP.NET MVC 3 website for my company's intranet. It's a pretty cool, I play audio recorded by our phone system and saved in our db. That's working good, but I'm having a hard time figuring out how to do something that should be simple. Please forgive any syntax errors I most likely have, this is a rough draft.
I have a table in the Index View /Apps that list all the AppName's, and next to each AppName I want to display a link to another view, with the text of the link being a Count() of all CallDetails associated with that App.
I have two classes:
public class Apps
{
public int AppId { get; set; }
public string AppName { get; set; }
}
public class CallDetail
{
public int Id { get; set; }
public int AppID { get; set; }
public byte[] FirstName { get; set; }
public byte[] LastName { get; set; }
....etc
}
a context for each:
public class AppsContext : DbContext
{
public DbSet<Apps> Apps { get; set; }
}
public class CallContext : DbContext
{
public DbSet<CallDetail> CallDetails { get; set; }
}
a controller method for each:
// AppsController
private AppsContext db = new AppsContext();
public ViewResult Index()
{
return View(db.Apps.ToList());
}
// CallController method (from my current attempt)
public ActionResult CallCheck(int id)
{
bool? enabled = null;
var appcalls = from s in db.CallDetails
where s.AppID == id
&& s.Enabled.Equals(enabled)
select s;
string callnum = appcalls.Count().ToString();
return View(callnum);
}
It displays the AppName just fine in this portion of the View below, and I can create a link to a View for each associated CallDetail just fine. But I don't know how to display info I'd get from the CallDetail Controller since the View's Model is Apps and its Controller, AppsController.
#model IEnumerable<myMessagePlayer.Models.Apps>
...
#foreach (var item in Model)
{
<tr>
<td>
#Html.DisplayFor(modelItem => item.AppName)
</td>
<td class="appLink">
...
</td>
</tr>
}
I've tried many different methods, some that I might have gotten to work, but they seemed semantically un-MVC. So I figured I'd just ask a general "whats the standard practice?" type of question.
The path you are currently going down would end up hitting the database for each app you have in your database. There is a way to display all the information with only one hit to the database.
Your context needs to change to this:
public class ApplicationContext : DbContext
{
public DbSet<Apps> Apps { get; set; }
public DbSet<CallDetail> CallDetails { get; set; }
}
You could create a view model object called AppCallInfo that has three properties:
public class AppCallInfo
{
public int AppID { get; set; }
public string AppName { get; set; }
public int CallCount { get; set; }
}
In your Index action you need to do something like this:
public ViewResult Index()
{
var model = from a in db.Apps
join c in db.CallDetails on a.AppID equals c.AppID
where c.Enabled == enabled
group a by new { AppName = a.AppName, AppID = a.AppID } into g
select new AppCallInfo {
AppName = g.Key.AppName,
AppID = g.Key.AppID,
CallCount = g.Count()
};
return View(model.ToList());
}
Now you have everything you need for each row in your table in one object.
#model List<myMessagePlayer.ViewModels.AppCallInfo>
...
#foreach (var item in Model)
{
<tr>
<td>
#Html.DisplayFor(modelItem => item.AppName)
</td>
<td class="appLink">
#Html.ActionLink(item.CallCount, "ViewCalls", "Call", new { Id = item.AppID }, null)
</td>
</tr>
}
Using this method avoids hitting the database for each app you have in your table.
Is the view CallCheck a partial view?
In your index view you could use
#Html.RenderAction("CallCheck", "AppsController", new { Id = #Model.AppId } )
The syntax may not be 100% correct, but it should get you going in the right direction.

Resources