Which Model Statement to use in MVC 3 - c#

I am very new to MVC 3 framework. I have added an Entity with the EntityFramework 6.0, and in my controller, I would like to query the entity for all "DeviceInterfaces" which belong to a particular device. My view is throwing this error:
The model item passed into the dictionary is of type System.Data.Entity.Infrastructure.DbQuery`1[<>f__AnonymousType1`2[System.String,System.String]], but this dictionary requires a model item of type System.Collections.Generic.IEnumerable`1[FindDevice.DeviceInterfaces].
My Controller:
public ActionResult DeviceName(string name)
{
SampleEntities sampleEntities = new SampleEntities();
try
{
var Model = (from dev in sampleEntities.NetworkDevices
where dev.Name == name
from inter in sampleEntities.DeviceInterfaces
where inter.NetworkDevice.Id == dev.Id
//select inter);
select new { DeviceName = dev.Name, InterfaceName = inter.Name});
return View(Model);
}
catch
{
}
return View();
}
My View:
#model IEnumerable<FindDevice.DeviceInterfaces>
#{
ViewBag.Title = "DeviceName";
Layout = "~/Views/Shared/_Layout.cshtml";
}
<h2>Displaying Device with name "#ViewBag.MyName"</h2>
<table>
<tr>
<th>Device</th>
<th>Interface</th>
<th>IPv4 Address</th>
<th>Subnet Mask</th>
<th>CIDR</th>
<th>Network</th>
</tr>
#foreach (var item in Model)
{
<tr>
<td>item.NetworkDevice.Name</td>
<td>#item.Name</td>
<td>#item.IPv4Address</td>
<td>#item.IPv4SubnetMask</td>
<td>#item.CIDR</td>
<td>#item.Subnet</td>
</tr>
}
I understand why the error is thrown, what I don't understand is what I can change my #model statement to so that I can display the Device Name, as well as the Interface Name and all other interface properties in this view.

I'd suggest using a strongly typed model. Add the below class in Models folder
public class DeviceInterfaceModel
{
public string DeviceName { get; set; }
public string InterfaceName { get; set; }
public string IPv4Address { get; set; }
public string IPv4SubnetMask { get; set; }
public string CIDR { get; set; }
public string Subnet { get; set; }
}
Then map the query results to a List<DeviceInterfaceModel> in your controller
public ActionResult DeviceName(string name)
{
SampleEntities sampleEntities = new SampleEntities();
try
{
var model = (from dev in sampleEntities.NetworkDevices
where dev.Name == name
from inter in sampleEntities.DeviceInterfaces
where inter.NetworkDevice.Id == dev.Id
select new DeviceInterfaceModel
{
DeviceName = dev.Name,
InterfaceName = inter.Name,
IPv4Address = inter.IPv4Address,
IPv4SubnetMask = inter.IPv4SubnetMask,
CIDR = inter.CIDR,
Subnet = inter.Subnet
}).ToList();
return View(model);
}
catch
{
}
return View();
}
and change the model type to IEnumerable<DeviceInterfaceModel> in your view
#model IEnumerable<DeviceInterfaceModel>
#{
ViewBag.Title = "DeviceName";
Layout = "~/Views/Shared/_Layout.cshtml";
}
<h2>Displaying Device with name "#ViewBag.MyName"</h2>
<table>
<tr>
<th>Device</th>
<th>Interface</th>
<th>IPv4 Address</th>
<th>Subnet Mask</th>
<th>CIDR</th>
<th>Network</th>
</tr>
#foreach (var item in Model)
{
<tr>
<td>#item.DeviceName</td>
<td>#item.InterfaceName</td>
<td>#item.IPv4Address</td>
<td>#item.IPv4SubnetMask</td>
<td>#item.CIDR</td>
<td>#item.Subnet</td>
</tr>
}

Related

Using if statement in MVC view and controller

I am having a problem displaying different text based on the result I get from my controller.
Column command_status_codeis returning value between 0 to 12 from the table. However, I would like to display different text based on the values I get from the controller.
i.e if I get 0 I would like to display Verify and if I get 1 I would like to display Active and so on.
I am not sure if I add the check in the view or do the conversion in the controller itself.
Here is the relevant code:
View
#model List<Models.AuditLogs>
<table>
<tr>
<th>User</th>
<th>Command Status Code</th>
</tr>
#foreach (var AuditLogsDetail in Model)
{
<tr>
<td>#AuditLogsDetail.user_id</td>
<td>#AuditLogsDetail.command_status_code</td>
</tr>
}
</table>
Controller
public ActionResult AuditLogs() {
string connectionstring = "MY_CONNECTION_STRING";
string sql = "select * from table_name";
SqlConnection conn = new SqlConnection(connectionstring);
SqlCommand cmd = new SqlCommand(sql, conn);
var Details = new List < AuditLogs > (); {
conn.Open();
SqlDataReader rdr = cmd.ExecuteReader();
while (rdr.Read()) {
var AuditLogsDetail = new AuditLogs {
user_id = rdr["user_id"].ToString(),
command_status_code = rdr["command_status_code"].ToString(),
};
Details.Add(AuditLogsDetail);
}
}
return View(Details);
}
Model
public class AuditLogs
{
public string user_id { get; set; }
public string command_status_code { get; set; }
}
}
I would leave the Controller for routing or controlling which view is called (which should be it's job, you should put as few presentation or application logic in the controller as possible).
Since this conversion is logic related to the model itself, I would put it in the Model class where it belongs and can be tested easily (if it becomes more complicated).
I would add a new property in the AuditLogsDetail model class which would return a string using a switch statement (since there are many possible values):
public class AuditLogsDetail
{
public int CommandStatusCode { get; set; }
public string CommandStatus
{
get
{
switch (CommandStatusCode)
{
case 0:
return "Verify";
case 1:
return "Active";
// and so on for the other 12 cases
default:
// you could throw an exception here or return a specific string like "unknown"
throw new Exception("Invalid Command Status Code");
}
}
}
}
In the Razor view you then simply have to call this property likewise:
<tr>
<td>#AuditLogsDetail.user_id</td>
<td>#AuditLogsDetail.CommandStatus</td>
</tr>
You could put a switch statement or if statement in the view but then you would clutter it. If you have several of these statements the view would be hard to read.
This sounds like a good candidate for an enum.
enum CommandStatus
{
def = 0,
success = 1,
A = 2,
B = 3,
...
}
public class AuditLogs
{
public string user_id { get; set; }
public CommandStatus command_status_code { get; set; }
}
Then when you fetch the value, just cast the int to the enum:
var AuditLogsDetail = new AuditLogs
{
user_id = rdr["user_id"].ToString(),
command_status_code = (CommandStatus)rdr["command_status_code"],
};
This doesn't provide the robustness of a switch, however.
in your view you can do something like this:
#model List<Models.AuditLogs>
<table>
<tr>
<th>User</th>
<th>Command Status Code</th>
</tr>
#foreach (var AuditLogsDetail in Model)
{
#if (#AuditLogsDetail.command_status_code == 0)
{
// what i need to do
}
#else if (#AuditLogsDetail.command_status_code == 1)
{
// what i need to do
}
<tr>
<td>#AuditLogsDetail.user_id</td>
<td>#AuditLogsDetail.command_status_code</td>
</tr>
}
</table>

Display IEnumerable<dynamic> in a view

I have a model as shown below. The MenuItem model, which has different SQL queries (MenuItem.MenuSQLQuery).
I am executing these query agaist a progress DB and I want to display the result on the view. The return of the SQL query is IEnumerable dynamic (using dapper ORM).
Thanks for your time reading and appreciate any help.
Model
namespace Models.Menu
{
public class MenuItem
{
public int MenuItemId { get; set; }
public string MenuName { get; set; }
public string MenuCategory { get; set; }
public string MenuParent { get; set; }
public string MenuAction { get; set; }
public string MenuController { get; set; }
public string MenuSQLQuery { get; set; }
public string ResultColumnHeading { get; set; }
public string MenuRole { get; set;}
}
}
Controller
using Dapper;
using Microsoft.AspNet.Identity;
using System.Collections.Generic;
using System.Data;
using System.Data.Odbc;
using System.Linq;
using System.Web.Mvc;
using DataLayer;
using Models.Menu;
namespace Controllers
{
public class BrowseController : Controller
{
public ActionResult Index()
{
return View();
}
public ActionResult BrowseResult(int menuId)
{
ApplicationDbContext context = new ApplicationDbContext();
var odbcConnection = new OdbcConnection("DRIVER=Progress OpenEdge 10.2B Driver;****DIL=READ UNCOMMITTED");
// Open your connection
//ApplicationUser currentUser = context.Users.Where(u => u.UserName.Equals(UserName, StringComparison.CurrentCultureIgnoreCase)).FirstOrDefault();
var userid = User.Identity.GetUserId();
List<MenuUserAccess> menuAccess = context.MenuUserAccess.Where(m => m.MenuItemId == menuId).ToList();
var access = menuAccess.FirstOrDefault(o => o.MenuUserId == userid);
if (access != null)
{
odbcConnection.Open();
var menuitem = context.MenuItem.First(m => m.MenuItemId == menuId);
var browseResult = odbcConnection.Query(menuitem.MenuSQLQuery);
odbcConnection.Close();
odbcConnection.Dispose();
return View(browseResult);
}
else
{
return View();
}
}
}
}
This is the view I am using, but not getting any output.
#model IEnumerable<dynamic>
<div>
<h2>BrowseResult</h2>
#if (Model.Count() > 0)
{
ViewBag.Title = "BrowseResult";
WebGrid grid = new WebGrid(source: Model);
#grid.GetHtml()
}
else
{
<p> No Data Found</p>
}
<h2>End</h2>
</div>
You need something like this
#model IEnumerable<dynamic>
<div>
<h2>BrowseResult</h2>
#if (Model.Count() > 0)
{
ViewBag.Title = "BrowseResult";
foreach(dynamic item in Model)
{
<span>#item.MenuName</span>
}
}
else
{
<p> No Data Found</p>
}
<h2>End</h2>
</div>
Returns
name1 name2
There's an example with WebGrid in this link: https://www.w3schools.com/asp/webpages_webgrid.asp
I made it working.
This is my view code.
#model IEnumerable<dynamic>
<div>
#{
int i = 0;
}
<table class="table-striped">
<tr>
#foreach (KeyValuePair<string, object> kvp in Model.ElementAt(1))
{
// This will be the column heading
<td> <b> #kvp.Key </b></td>
}
</tr>
#foreach (var d in Model)
{
<tr>
#foreach (KeyValuePair<string, object> kvp in Model.ElementAt(i))
{
//This will be the row data
<td> #kvp.Value </td>
}
</tr>
i++;
}
</table>
</div>

mvc Dropdown list with model of type list

I am pretty new to MVC however I have been trying to use a dropdownlist in a view which has a model of type List<>. The reason for this is to create multiple records on a single page.
I have been able to successfully create a view that is not of type List<> and a single record at a time, but in spite of a lot of googling can not implement the solution when I implement the view with a model of type List<>.
Model
public class Product
{
public int ProductID { get; set; }
public string Description { get; set; }
public int VatRateID { get; set; }
[ForeignKey("VatRateID")]
public virtual VatRate VatRate { get; set; }
}
Controller
// GET: /Product/Bulkdata
public ActionResult BulkData()
{
PopulateVatRateDropDownList();
List<Product> model = new List<Product>();
model.Add(new Product
{
ProductID = 0,
Description = "",
VatRateID = 1
}
);
return View(model);
}
//
// POST: /Product/Bulkdata
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult BulkData(
[Bind(Include = "Description,VatRateID")]
List<Product> products)
{
try
{
if (ModelState.IsValid)
{
foreach (var p in products)
{
db.Product.Add(p);
}
db.SaveChanges();
return RedirectToAction("Index");
}
}
catch (DataException)
{
ModelState.AddModelError("", "Bulk - Unable to save changes, Try again, and if the probelm persists contact your administrator");
}
PopulateVatRateDropDownList();
return View(products);
}
private void PopulateVatRateDropDownList(object selectedVatRate = null)
{
var vatRateQuery = from v in db.VatRate
select v;
ViewBag.VatRateID = new SelectList(vatRateQuery, "VatRateID", "VatRateDescription", selectedVatRate);
}
View
#if (Model != null && Model.Count > 0)
{
int j = 0;
foreach (var i in Model)
{
<tr style="border:1px solid black">
<td>
#Html.EditorFor(m => m[j].Description)
#Html.ValidationMessageFor(m => m[j].Description)
</td>
<td>
#Html.DropDownList("VatRateID", String.Empty)
#Html.ValidationMessageFor(m => m[j].VatRateID)
</td>
<td>
#if (j > 0)
{
Remove
}
</td>
</tr>
j++;
}
}
When I run the application with this code then 0 is always past back to vatRateID. I have tried numerous other solution other than the above including
ASP.NET MVC DropDownListFor with model of type List<string>. As I have mentioned I am new to mvc so I know there is probably something simple that I am missing. Any help would be hugely appreciated.
Thanks
Answered by Stephen in comments:
In the view, changed the foreach to a for loop as per the article in Stephen's comment and used DropdDownListFor
#Html.DropDownListFor(m => m[i].VatRateID, (IEnumerable<SelectListItem>)ViewBag.VatRateList)
Also amended the controller to rename the ViewBag property to VatRateList
'
private void PopulateVatRateDropDownList(object selectedVatRate = null)
{
var vatRateQuery = from v in db.VatRate
select v;
ViewBag.VatRateID = new SelectList(vatRateQuery, "VatRateID", "VatRateDescription", selectedVatRate);
}
'

How can I display a model property name that is coming from the database?

I currently am pulling a list of url's from a view using Entity Framework 5 and MVC 5. I have the view populating all the links but I need each link to display their 'LinkState' names like in my model so it will output:
Alabama
Georgia
etc.
with the link attached to the LinkState. Instead of the view foreach loop saying State Link. I cant get my model/controlled to pull the correct information.
Repository:
public class LinkRepository
{
private readonly LinkLibrary _entities = new LinkLibrary ();
public LinkRepository()
{
_entities = new LinkLibrary ();
}
public List<LinkModels> RetrieveStateLink(string year)
{
return
_entities.vw_URLLibrary.Where(s => s.YEAR.Equals(year) && s.URL_TYPE.Equals("United States")).Select(m => new LinkModels()
{
UrlLink = m.LinkLocation
}).ToList();
}
}
Model
public class LinkModels
{
public string LinkYear { get; set; }
public string LinkState { get; set; }
public string UrlLink { get; set; }
public string LinkType { get; set; }
public List<string> ListOfUrls{ get; set; }
}
Controller
public ActionResult GetStateLinks()
{
var stateLink = new List<string>();
var model = rr.RetrieveStateLinks("2014").Select(m=> m.UrlLink).ToList();
foreach (var s in model)
{
stateLink.Add(s);
}
var rm = new LinkModels();
rm.ListOfUrls = stateLink;
return View(rm);
}
View
#foreach (var item in Model.StateLinkList)
{
<td>
State Link
</td>
}
Your issue is that you are returning a List of strings as opposed to a list of LinkModels. I updated the repository to return the url and link name
removed some unneccessary code in your controller and updated it to work with a list of LinkObjects. Then updated the view to display the info.
You will have to update your view #model List<LinkModels> instead of #model List<string>
public class LinkRepository
{
private readonly LinkLibrary _entities = new LinkLibrary ();
public LinkRepository()
{
_entities = new LinkLibrary ();
}
public List<LinkModels> RetrieveStateLink(string year)
{
return
_entities.vw_URLLibrary.Where(s => s.YEAR.Equals(year) && s.URL_TYPE.Equals("United States")).Select(m => new LinkModels()
{
LinkState = m.LinkState,
UrlLink = m.LinkLocation
}).ToList();
}
}
public ActionResult GetStateLinks()
{
var stateLink = new List<LinkModels>();
var model = rr.RetrieveStateLinks("2014");
return View(model);
}
#foreach (var item in Model)
{
<td>
#item.LinkState
</td>
}
Controller
public ActionResult GetStateLinks()
{
var model = rr.RetrieveStateLinks("2014");
return View(model);
}
View (change your view model to list of LinkModels)
#foreach (var item in Model)
{
<td>
#item.LinkState
</td>
}

Html table not populating from ViewModel

I am trying to populate an HTML table with data from a table in my database. The issue is simply that the HTML table is not getting populated with any data.
Here is the ViewModel:
public class TestViewModel
{
public string MatchedId { get; set; }
public string UnmatchedId { get; set; }
public string Auth { get; set; }
public DateTime CreditDate { get; set; }
public string CreditNumber { get; set; }
public decimal CreditAmount { get; set; }
public DateTime DeniedDate { get; set; }
public int DeniedReasonId { get; set; }
public string DeniedNotes { get; set; }
}
Controller Action:
[HttpPost]
public ActionResult UploadValidationTable(HttpPostedFileBase csvFile)
{
var inputFileDescription = new CsvFileDescription
{
SeparatorChar = ',',
FirstLineHasColumnNames = true
};
var cc = new CsvContext();
var filePath = uploadFile(csvFile.InputStream);
var model = cc.Read<Credit>(filePath, inputFileDescription);
try
{
var entity = new Entities();
//model here is the .csv, doesn't have anything to do with this issue
foreach (var item in model)
{
var tc = new TemporaryCsvUpload
{
Id = item.Id,
CreditAmount = item.CreditAmount,
CreditDate = item.CreditDate,
CreditNumber = item.CreditNumber,
DeniedDate = item.DeniedDate,
DeniedReasonId = item.DeniedReasonId,
DeniedNotes = item.DeniedNotes
};
entity.TemporaryCsvUploads.Add(tc);
}
entity.SaveChanges();
System.IO.File.Delete(filePath);
//This is where the database table is getting filled
entity.Database.ExecuteSqlCommand("Insert into CsvReport Select p.Id as MatchedId, case when p.Id is null then t.Id end as UnmatchedId, p.Auth,p.CreditDate, p.CreditNumber,p.CreditAmount, p.DeniedDate,p.DeniedReasonId, p.DeniedNotes from TemporaryCsvUpload t left join PermanentTable p on p.Id = t.Id;");
TempData["Success"] = "Updated Successfully";
}
catch (LINQtoCSVException)
{
TempData["Error"] = "Upload Error: Ensure you have the correct header fields and that the file is of .csv format.";
}
return View("Upload");
}
View:
#model IEnumerable<TestProject.TestViewModel>
#if (Model != null)
{
foreach (var item in Model.Where(x => x.IdMatched != null))
{
<tr>
<td>
#item.MatchedId
</td>
<td>
#item.Auth
</td>
<td>
#item.CreditDate
</td>
<td>
#item.CreditNumber
</td>
<td>
#item.CreditAmount
</td>
<td>
#item.DeniedDate
</td>
<td>
#item.DeniedReasonId
</td>
<td>
#item.DeniedNotes
</td>
</tr>
}
}
It's a little weird because I am populating the database with an SQL command. What am I missing here? Do I need to try and pass it through the controller action? Let me know if you need more information. Thanks!
Edit
I tried to pass the instance through, but I may still be doing it incorrectly:
var testModel = new TestViewModel();
return View("Upload", testModel);
Here is what its padding through:
public class TestViewModel
{
public IEnumerable<Test> Test { get; set; }
}
Made an answer so essentially the view doesn't know what to render you need to pass an actual filled model (in your case an IEnumerable to the view). This can be done using the method:
View("Upload", viewModelList);
Controller.View docs on MSDN
It looks like you are not adding any data to your view model.
If your view model is a collection of Test objects, you need to add some
Test objects to the collection.
var model = new TestViewModel()
{
Test = new List<Test>() { new Test(), new Test(), ... }
}
return View("Upload", model);

Categories

Resources