Web API en ASP.NET with OData - Model issue - asp.net-mvc

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

Code First Using Entity Framework Core and ViewModel

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

Using Include in Entity Framework Core

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.

Not opening or recognizing particular controller index mvc EntityModel 1st

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();
}
}

Avoid duplicating the results on viewpage in mvc

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 &nbsp /> #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

Full code - Strongly typed Models.Error message

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);
}

Resources