I have a Web API in ASP.NET with OData and I can consume from the service, but the view doesn't print a table. I'm starting with .NET and with MVC.
ERROR:
In View: #foreach (var item in Model)
System.NullReferenceException: Object reference not set to an instance of an object.
I have been seeing other similar doubts, which were solved by passing the model in the ActionResult, but I don't know how to do this with OData. Thanks!
MODEL
namespace exchange_rates
{
using System;
using System.Collections.Generic;
public partial class CurrentValue
{
public int Id { get; set; }
public System.DateTime Date { get; set; }
public string Currency { get; set; }
public double Rate { get; set; }
}
}
CONTROLLER
namespace exchange_rates.Controllers
{
public class CurrentValuesController : ODataController
{
private db_test_bce_Entities db = new db_test_bce_Entities();
// GET: odata/CurrentValues
[EnableQuery]
public IQueryable<CurrentValue> GetCurrentValues()
{
return db.CurrentValues;
}
// GET: odata/CurrentValues(5)
[EnableQuery]
public SingleResult<CurrentValue> GetCurrentValue([FromODataUri] int key)
{
return SingleResult.Create(db.CurrentValues.Where(currentValue => currentValue.Id == key));
}
}
}
WebApiConfig.cs
namespace exchange_rates
{
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
ODataConventionModelBuilder builder = new ODataConventionModelBuilder();
builder.EntitySet<CurrentValue>("CurrentValues");
config.Routes.MapODataServiceRoute("odata", "odata", builder.GetEdmModel());
}
}
}
VIEW
#model IEnumerable<exchange_rates.CurrentValue>
#{
Layout = null;
}
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width" />
<title>CurrenciesTable</title>
</head>
<body>
<table class="table">
<tr>
<th>
#Html.DisplayNameFor(model => model.Date)
</th>
<th>
#Html.DisplayNameFor(model => model.Currency)
</th>
<th>
#Html.DisplayNameFor(model => model.Rate)
</th>
<th></th>
</tr>
#foreach (var item in Model)
{
<tr>
<td>
#Html.DisplayFor(modelItem => item.Date)
</td>
<td>
#Html.DisplayFor(modelItem => item.Currency)
</td>
<td>
#Html.DisplayFor(modelItem => item.Rate)
</td>
</tr>
}
</table>
</body>
</html>
EDIT
I was trying return my list to the view from the OData Controller and this didn't work for me. Finally, I solved by returning the list from the MVC Controller:
HomeController
public class HomeController : Controller
{
private db_test_bce_Entitiesdb = new db_test_bce_Entities();
// GET: Home
public ActionResult Index()
{
var currencies = db.CurrentValues.OrderBy(field => field.Acronym).ToList();
return View(currencies);
}
}
namespace exchange_rates
{
using System;
using System.Collections.Generic;
public class MyViewModel
{
public List<CurrentValue> MyListOfCurrentValues {get;set;}
}
public class CurrentValue
{
public int Id { get; set; }
public System.DateTime Date { get; set; }
public string Currency { get; set; }
public double Rate { get; set; }
}
}
then in the view
#foreach (var item in Model.MyListOfCurrentValues)
{
<tr>
<td>
#Html.DisplayFor(modelItem => item.Date)
</td>
<td>
#Html.DisplayFor(modelItem => item.Currency)
</td>
<td>
#Html.DisplayFor(modelItem => item.Rate)
</td>
</tr>
}
I'm not familiar with OData... but my controller would look like the below.
public ActionResult MyViewName()
{
var model = new MyViewModel
{
MyListOfCurrentValues = //Value from data source
}
return View("MyViewName", model);
}
Related
I am new to MVC and Entity Framework and have decided to use the Code First approach using ViewModels.
I have tried many different tutorials but no one that i've tried has used Code First in EF Core with ViewModels.
My Models
public class Intermediary
{
public int IntermediaryID { get; set; }
public string RegisteredName { get; set; }
public string TradingName { get; set; }
public int Registration { get; set; }
public int VATNumber { get; set; }
public int FSPNumber { get; set; }
public DateTime CreatedDate { get; set; }
public string CreatedBy { get; set; }
public ICollection<Branch> Branches { get; set; }
}
public class Branch
{
public int BranchID { get; set; }
public string Name { get; set; }
public DateTime CreatedDate { get; set; }
public string CreatedBy { get; set; }
public Intermediary Intermediary { get; set; }
}
My ViewModel
public class IntermediariesViewModel
{
public int ID { get; set; }
public Intermediary Intermediary { get; set; }
public virtual ICollection<Branch> Branches { get; set; }
}
My Data Context Class
public class BizDevHubContext : DbContext
{
public BizDevHubContext(DbContextOptions<BizDevHubContext> options) : base(options)
{
}
public DbSet<Intermediary> Intermediaries { get; set; }
public DbSet<Branch> Branches { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Intermediary>().ToTable("Intermediary");
modelBuilder.Entity<Branch>().ToTable("Branch");
}
}
My Initializer Class
public static class DbInitializer
{
public static void Initialize(BizDevHubContext context)
{
context.Database.EnsureCreated();
if (context.Intermediaries.Any())
{
return; // DB has been seeded
}
var intermediaries = new Intermediary[]
{
new Intermediary{RegisteredName="Carson",TradingName="Alexander",CreatedDate=DateTime.Now,CreatedBy="System"},
new Intermediary{RegisteredName="Meredith",TradingName="Alonso",CreatedDate=DateTime.Now,CreatedBy="System"},
new Intermediary{RegisteredName="Arturo",TradingName="Anand",CreatedDate=DateTime.Now,CreatedBy="System"},
new Intermediary{RegisteredName="Gytis",TradingName="Barzdukas",CreatedDate=DateTime.Now,CreatedBy="System"},
new Intermediary{RegisteredName="Yan",TradingName="Li",CreatedDate=DateTime.Now,CreatedBy="System"},
new Intermediary{RegisteredName="Peggy",TradingName="Justice",CreatedDate=DateTime.Now,CreatedBy="System"},
new Intermediary{RegisteredName="Laura",TradingName="Norman",CreatedDate=DateTime.Now,CreatedBy="System"},
new Intermediary{RegisteredName="Nino",TradingName="Olivetto",CreatedDate=DateTime.Now}
};
foreach (Intermediary i in intermediaries)
{
context.Intermediaries.Add(i);
}
context.SaveChanges();
var branches = new Branch[]
{
new Branch{Name="Bloemfontein",CreatedDate=DateTime.Now,CreatedBy="System"},
new Branch{Name="Cape Town",CreatedDate=DateTime.Now,CreatedBy="System"},
new Branch{Name="Durban",CreatedDate=DateTime.Now,CreatedBy="System"},
new Branch{Name="Nelspruit",CreatedDate=DateTime.Now,CreatedBy="System"},
new Branch{Name="Johannesburg",CreatedDate=DateTime.Now,CreatedBy="System"},
new Branch{Name="Port Shepstone",CreatedDate=DateTime.Now,CreatedBy="System"},
new Branch{Name="Pretoria",CreatedDate=DateTime.Now,CreatedBy="System"}
};
foreach (Branch b in branches)
{
context.Branches.Add(b);
}
context.SaveChanges();
}
}
This all seems to work fine and the database gets created ok, but I am stuck now trying to create the Controller and Views
I managed to do it with a single model but cannot seem to get it right putting the entire viewmodel.
My Controller
using BizDevHub.ViewModels;
public async Task<IActionResult> Index()
{
return View(await _context.Intermediaries.ToListAsync());
}
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Create([Bind("ID")] Intermediaries Intermediaries)
{
if (ModelState.IsValid)
{
_context.Add(Intermediaries);
await _context.SaveChangesAsync();
return RedirectToAction(nameof(Index));
}
return View(Intermediaries);
}
The same goes for my views
My View
#model IEnumerable<BizDevHub.Models.Intermediary>
#{
ViewData["Title"] = "Index";
Layout = "~/Views/Shared/_Layout.cshtml";
}
<h2>Index</h2>
<p>
<a asp-action="Create">Create New</a>
</p>
<table class="table">
<thead>
<tr>
<th>
#Html.DisplayNameFor(model => model.RegisteredName)
</th>
<th>
#Html.DisplayNameFor(model => model.TradingName)
</th>
<th>
#Html.DisplayNameFor(model => model.Registration)
</th>
<th>
#Html.DisplayNameFor(model => model.VATNumber)
</th>
<th>
#Html.DisplayNameFor(model => model.FSPNumber)
</th>
<th>
#Html.DisplayNameFor(model => model.CreatedDate)
</th>
<th>
#Html.DisplayNameFor(model => model.CreatedBy)
</th>
<th></th>
</tr>
</thead>
<tbody>
#foreach (var item in Model) {
<tr>
<td>
#Html.DisplayFor(modelItem => item.RegisteredName)
</td>
<td>
#Html.DisplayFor(modelItem => item.TradingName)
</td>
<td>
#Html.DisplayFor(modelItem => item.Registration)
</td>
<td>
#Html.DisplayFor(modelItem => item.VATNumber)
</td>
<td>
#Html.DisplayFor(modelItem => item.FSPNumber)
</td>
<td>
#Html.DisplayFor(modelItem => item.CreatedDate)
</td>
<td>
#Html.DisplayFor(modelItem => item.CreatedBy)
</td>
<td>
<a asp-action="Edit" asp-route-id="#item.IntermediaryID">Edit</a> |
<a asp-action="Details" asp-route-id="#item.IntermediaryID">Details</a> |
<a asp-action="Delete" asp-route-id="#item.IntermediaryID">Delete</a>
</td>
</tr>
}
</tbody>
</table>
What am I doing wrong?
Thanks in advance.
#Anonymous is correct that you need to redesign the View model. There is no point having a view model if you are just assigning entities as the properties, the purpose of the view model is to separate you controller and view from your datacontext
public class IntermediariesViewModel
{
public int Id { get; set; }
public string RegisteredName { get; set; }
public string TradingName { get; set; }
//other properties
IEnumerable<BranchViewModel> Branches { get; set; }
}
public class BranchViewModel
{
public int Id { get; set; }
public string Name { get; set; }
}
View:
#model IEnumerable<BizDevHub.Models.IntermediariesViewModel>
EDIT : but how would you combine both view models in a single controller and view with methods like Index, Edit and Delete?
Controller:
public IActionResult Update([FromBody]IntermediariesViewModel model)
{
var intermediary = _context.Intermediaries.Where(x=> x.IntermediaryID == model.Id).FirstOrDefault();
if(intermediary != null)
{
intermediary.RegisteredName = model.RegisteredName ;
//rest of updates
_context.SaveChanges();
}
}
public IActionResult Delete([FromBody]int IntermediariesId)
{
var intermediary = _context.Intermediaries.Where(x=> x.IntermediaryID == IntermediariesId).FirstOrDefault();
if(intermediary != null)
{
_context.Intermediaries.Remove(intermediary);
_context.SaveChanges();
}
}
View:
#Html.DisplayNameFor(model => model.RegisteredName)
#Html.DisplayNameFor(model => model.BranchViewModel[0].Name)// first branch name, you may want to iterate
Question: How and where the extension method "Include", used in the Index() action method, in the following PostController used in the Inxex.cshtml view shown below? As I understand _context.Posts.Include(p => p.Blog) means include all posts that relate to blogs table. But I don't see use of blog class or blogId property in the Index.cshtml view below?
Background: In an ASP.NET MVC Core - Code First project I'm following this ASP.NET official site tutorial where they have following Model classes for Blog (parent) and Post (child). I then created a controller (shown below) using MVC Controller with Views, using Entity Framework wizard where I selected Post model in the Model dialogbox.:
Model:
using Microsoft.EntityFrameworkCore;
using System.Collections.Generic;
namespace EFGetStarted.AspNetCore.NewDb.Models
{
public class BloggingContext : DbContext
{
public BloggingContext(DbContextOptions<BloggingContext> options)
: base(options)
{ }
public DbSet<Blog> Blogs { get; set; }
public DbSet<Post> Posts { get; set; }
}
public class Blog
{
public int BlogId { get; set; }
public string Url { get; set; }
public List<Post> Posts { get; set; }
}
public class Post
{
public int PostId { get; set; }
public string Title { get; set; }
public string Content { get; set; }
public int BlogId { get; set; }
public Blog Blog { get; set; }
}
}
PostController:
public class PostsController : Controller
{
private readonly BloggingContext _context;
public PostsController(BloggingContext context)
{
_context = context;
}
// GET: Posts
public async Task<IActionResult> Index()
{
var bloggingContext = _context.Posts.Include(p => p.Blog);
return View(await bloggingContext.ToListAsync());
}
}
Index.cshtml view for Index() action in PostController:
#model IEnumerable<ASP_Core_Blogs.Models.Post>
#{
ViewData["Title"] = "Index";
}
<h2>Index</h2>
<p>
<a asp-action="Create">Create New</a>
</p>
<table class="table">
<thead>
<tr>
<th>
#Html.DisplayNameFor(model => model.Content)
</th>
<th>
#Html.DisplayNameFor(model => model.Title)
</th>
<th></th>
</tr>
</thead>
<tbody>
#foreach (var item in Model) {
<tr>
<td>
#Html.DisplayFor(modelItem => item.Content)
</td>
<td>
#Html.DisplayFor(modelItem => item.Title)
</td>
<td>
<a asp-action="Edit" asp-route-id="#item.PostId">Edit</a> |
<a asp-action="Details" asp-route-id="#item.PostId">Details</a> |
<a asp-action="Delete" asp-route-id="#item.PostId">Delete</a>
</td>
</tr>
}
</tbody>
</table>
Since you're not accessing the Blog property, it's better not to use Include(p => p.Blog). This will add an extra join that isn't required.
However, if you'll reference it in each table row, then it's preferred to include it to avoid lazy loading issues.
I am using model 1st approach.
My all controllers actions and views works fine But the last requirement of my project is to show 2 tables in view using same databaseTable.So i created a new view Model in my dataModel section. Now if I create a new controller for that particular class it shows me error Can not Retrieve metadata for model class. so finally I decided to create empty controller but when i navigate to that view it shows error Resource not found 404. Here is the code.
Model class
namespace BOL1
{
public class ADetailsVm
{
public List<BOL1.tbl_Transiction> Payables { get; set; }
public List<BOL1.tbl_Transiction> Reciveables { get; set; }
}
}
DbContext
public partial class bankingEntities : DbContext
{
public bankingEntities()
: base("name=bankingEntities")
{
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
throw new UnintentionalCodeFirstException();
}
public virtual DbSet<tbl_Accounts> tbl_Accounts { get; set; }
public virtual DbSet<tbl_Transiction> tbl_Transiction { get; set; }
public virtual DbSet<tbl_TransictionType> tbl_TransictionType { get; set; }
public DbSet<ADetailsVm> ADetailsVm { get; set; }
}
}
Controller
public class Details : Controller
{
private TransictionBs objbs;
public Details()
{
objbs = new TransictionBs();
}
// GET: Shinwari/AccountDetails
[HttpGet]
public ActionResult Index(int accountid)
{
ADetailsVm v = new ADetailsVm();
//Load both the collection properties
v.Payables = objbs.GetALL().Where(p => p.AId == accountid && p.tbl_TransictionType.Type.Contains("Payable")).ToList();
v.Reciveables = objbs.GetALL().Where(r => r.AId==accountid && r.tbl_TransictionType.Type.Contains("Reciveable")).ToList();
return View(v);
}
View
#model BOL1.ADetailsVm
#{
ViewBag.Title = "Index";
}
<h2>AccountDetails</h2>
<p>
#Html.ActionLink("Create New", "Create")
</p>
<table id="Payables" class="table">
<tr>
<th>
Date
</th>
<th>
Discription
</th>
<th>
Amount
</th>
</tr>
#foreach (var item in Model.Payables)
{
<tr>
<td>
#item.Date
</td>
<td>
#item.TDiscription
</td>
<td>
#item.Amount
</td>
</tr>
}
</table>
<table id="Reciveables" class="table">
<tr>
<th>
Date
</th>
<th>
Discription
</th>
<th>
Amount
</th>
</tr>
#foreach (var item in Model.Reciveables)
{
<tr>
<td>
#item.Date
</td>
<td>
#item.TDiscription
</td>
<td>
#item.Amount
</td>
</tr>
}
</table>
You need to have Controller suffix in your class to be considered as a controller class in the MVC pattern, which handles your incoming requests and return something.
public class DetailsController : Controller
{
public ActionResult Index(int accountid)
{
return View();
}
}
I'm creating banking application in MVC 5.
Load table by bank and Load table rows by Bank,
But I cannot figure out do this exactly like above image,
This what I'm getting , it has 10 tables, instead just 2 tables
this is cshtml code for above view
#model IEnumerable<albaraka.Models.ProductApprovals>
#{
ViewBag.Title = "Product_Approvals";
Layout = "~/Views/Shared/_Layout.cshtml";
}
<h2>sadfasf</h2>
<h2>Product Approvals</h2>
#using (Html.BeginForm("Index", "Student", FormMethod.Get))
{
<div> Filter by : Date #Html.TextBox("SearchString", ViewBag.CurrentFilter as string) Filter by : Country #Html.TextBox("SearchString", ViewBag.CurrentFilter as string) <input type="submit" value="Search" /> </div>
}
#foreach (var item in Model)
{
#Html.DisplayNameFor(model => model.SubsidaryName); <p   /> #Html.DisplayFor(modelItem => item.SubsidaryName);
<table class="table">
<tr>
<th>
#Html.DisplayNameFor(model => model.ProductNameEn)
</th>
<th>
#Html.DisplayNameFor(model => model.ProdcutNameAr)
</th>
<th>
#Html.DisplayNameFor(model => model.CurrentStatus)
</th>
<th>
Actions
</th>
</tr>
#foreach (var inside in Model)
{
if (inside.Subsidary_ID == item.Subsidary_ID)
{
<tr>
<td>
#Html.DisplayFor(modelItem => inside.ProductNameEn)
</td>
<td>
#Html.DisplayFor(modelItem => inside.ProdcutNameAr)
</td>
<td>
#Html.DisplayFor(modelItem => inside.CurrentStatus)
</td>
<td>
#Html.ActionLink("Edit", "Edit", new { /* id=item.PrimaryKey */ }) |
#Html.ActionLink("Details", "Details", new { /* id=item.PrimaryKey */ }) |
#Html.ActionLink("Delete", "Delete", new { /* id=item.PrimaryKey */ })
</td>
</tr>
}
}
</table>
}
This is Model class
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
namespace albaraka.Models
{
public class ProductApprovals
{
public DateTime SearchDate { get; set; }
public string Country { get; set; }
public string Product_ID { get; set; }
public string Subsidary_ID { get; set; }
public string SubsidaryName { get; set; }
public string ProductNameEn { get; set; }
public string ProdcutNameAr { get; set; }
public string CurrentStatus { get; set; }
}
}
This is Controller class
public ActionResult Product_Approvals()
{
var productApproveResult =(from p in db.AB_Product
join s in db.AB_Subsidary on p.Subsidary_ID equals s.SubsidaryID
select new ProductApprovals
{
Product_ID = p.ProductID,
Subsidary_ID = s.SubsidaryID,
SubsidaryName = s.SubsidaryNameEn,
ProductNameEn = p.ProductTitleEn,
ProdcutNameAr = p.ProductTitleAr,
CurrentStatus = p.Status
}).ToList();
return View(productApproveResult);
}
How to stop this repeating ,appricate if can show a way to this in view page
You need a view model(s) to represent what you want to display in the view. It should be something like
public class BankVM
{
public string SubsidaryName { get; set; }
public IEnumerable<ProductVM> Products { get; set; }
}
public class ProductVM
{
public string ProductNameEn { get; set; }
public string ProdcutNameAr { get; set; }
public string CurrentStatus { get; set; }
}
and pass a collection of BankVM to the view
#model IEnumerable<yourAssembly.BankVM>
#foreach(var bank in Model)
{
<h3>#Html.DisplayFor(m => bank.SubsidaryName)</h3>
<table>
foreach(var product in bank.Products)
{
<tr>
<td>#Html.DisplayFor(m => product.ProductNameEn)</td>
<td>#Html.DisplayFor(m => product.ProductNameAr)</td>
....
</tr>
}
</table>
}
In the controller, you can use a Linq .GroupBy() method to populate your collection of BankVM
Model : (file name : Class1.cs)
namespace Project
{
public class Class1
{
// attributes defined : title, date etc
}
public class Class1DBContext : DbContext
{
public DbSet<Class1> Class1Table{ get; set; }
}
public class Class2
{
// attributes defined : Name, Location etc
}
public class Class2DBContext : DbContext
{
public DbSet<Class2> Class2Table{ get; set; }
}
public class ParentView
{
public Class1 Class1{ get; set; }
public Class2 Class2{ get; set; }
}
}
In Class1Controller.cs file:
namespace Project.Controllers
{
public class Class1Controller : Controller
{
private Class1DBContext db = new Class1DBContext();
public ViewResult Index()
{
return View(db.Class1Table.ToList());
}
}
In Index.cshtml :
Index view:
#model IEnumerable<Project.Models.ParentView>
#{
ViewBag.Title = "List";
}
<div style="float:left;">
<h1 style="color:Black;" >List</h1>
</div>
<div>
#foreach (var item in Model)
{
<tr>
<td>
#Html.DisplayFor(modelItem => item.Class1.Title)
</td>
<td>
#Html.DisplayFor(modelItem => item.Class1.Description)
</td>
</tr>
}
</div>
<div>
#{Html.RenderPartial("_PartialNew");}
</div>
in partial view code (partialNew.cshtml):
#model IEnumerable<Project.Models.ParentView>
#foreach (var item in Model)
{
<tr>
<td>
#Html.DisplayFor(modelItem => item.Class2.Name)
</td>
<td>
#Html.DisplayFor(modelItem => item.Class2.Location)
</td>
</tr>
}
Error Message:
The model item passed into the dictionary is of type 'System.Collections.Generic.List1[Project.Models.Class1]', but this dictionary requires a model item of type 'System.Collections.Generic.IEnumerable1[Project.Models.ParentView]'.
Change the class to use make use of IEnumerable, e.g.:
public class ParentView
{
public IEnumerable<Class1> Class1{ get; set; }
public IEnumerable<Class2> Class2{ get; set; }
}
Remove IEnumerable so that it reads:
#model Project.Models.ParentView
Your passing a list of Class1 objects to a view that requires a list of ParentView objects. The error message is very clear here.
What is your question exactly?
Edit -
You need to return the correct type to the view, maybe something like:
public ViewResult Index()
{
var pv = new ParentView();
pv.Class1 = db.Class1Table.ToList();
pv.Class2 = db.Class2Table.ToList();
return View(pv);
}