I am new to MVC, I referred this link (https://www.aspsnippets.com/Articles/Pass-Send-DataSet-DataTable-from-Controller-to-View-in-ASPNet-MVC.aspx) and passing data from Controller, but my project contains multiples Tables and I need to pass the data from Model->Controller->View.
I am facing error while doing this. Kindly check and provide the solution for my issue.
Error While running the Application
Server Error in '/' Application.
The model item passed into the dictionary is of type 'MyClassModel.Models.MyClass', but this dictionary requires a model item of type 'System.Collections.Generic.IEnumerable`1[MyClassModel.Models.MyClass]'.
<--Model--> (Data Representation)
using System.Collections.Generic;
using System.Linq;
using System.Web;
namespace MyClassModel.Models
{
public class Myclass
{
public List<int> Colors_ID { get; set; }
public List<string> ColorsInfo { get; set; }
public List<int> Completexity_code { get; set; }
public List<string> Completexity_name { get; set; }
public List<int> DeptCompletexity_code { get; set; }
public List<string> DeptCompletexity_name { get; set; }
}
}
<--Model--> (Business Logics)
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Configuration;
using System.Data;
using System.Data.SqlClient;
namespace MyClassModel.Models
{
public class MyClassBL
{
string conn = ConfigurationManager.ConnectionStrings["ConnectionString"].ConnectionString;
public DataSet details()
{
DataSet ds = new DataSet();
using (SqlConnection con = new SqlConnection(conn))
{
SqlCommand cmd = new SqlCommand("ItrackDropdown", con);
con.Open();
SqlDataAdapter da = new SqlDataAdapter(cmd);
da.Fill(ds);
}
return ds;
}
}
}
<--Controller-->
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using MyClassModel.Models;
using System.Data;
namespace MyClassModel.Controllers
{
public class Home : Controller
{
//
// GET: /Home/
public ActionResult Index()
{
MyClass newobj=new MyClass();
MyClassBL obj = new MyClassBL();
newobj.Colors_ID= obj.details().Tables[0].AsEnumerable().Select(x => x.Field<int>("Colors_ID")).ToList();
newobj.ColorsInfo = obj.details().Tables[0].AsEnumerable().Select(x => x.Field<string>("ColorsInfo")).ToList();
newobj.Completexity_code = obj.details().Tables[1].AsEnumerable().Select(x => x.Field<int>("Complexity_code")).ToList();
newobj.Completexity_name = obj.details().Tables[1].AsEnumerable().Select(x => x.Field<string>("Complexity_name")).ToList();
newobj.DeptCompletexity_code = obj.details().Tables[2].AsEnumerable().Select(x => x.Field<int>("Complexity_code")).ToList();
newobj.DeptCompletexity_name = obj.details().Tables[2].AsEnumerable().Select(x => x.Field<string>("Complexity_name")).ToList();
return View(newobj);
}
}
}
<--View-->
#model IEnumerable<MyClass.Models.MyClass>
#{
Layout = null;
}
<!DOCTYPE html>
<html>
<head>
<title>Index</title>
</head>
<body>
<p>
#Html.ActionLink("Create New", "Create")
</p>
<table>
#foreach (var item1 in #Model.Select(x => x.Colors_ID))
{
<tr>
<td>
#item1
</td>
</tr>
}
#foreach (var item in Model)
{
<tr>
<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>
</body>
</html>
Two suggestions for your references:
1) Model
a) Model name,
According to the controller, the Model name of the first one need update that the following line:
public class ItrackDD
should be
public class MyClass
b) Model Design should match the database. it will impact how to fix the error according to #2.
All of the members of the model ItrackDD are list, are you sure?
2) The error message should come from:
In the controller, it returned the newobj of type Myclass as in the following code line, which would be passed to the view
return View(newobj);
but in the view, it expect a dictionary as in this line:
#model IEnumerable<MyClass.Models.MyClass>
Related
I want to see movies of a certain media type (ie DVD, Bluray, VHS), so I have created stored procedure to do that. This stored procedure returns movies that have an ID associated with the media type. For instance, ID 1 returns DVDs. I have a page for my media type that lists the media (the razor pages automatically created the details page for me after scaffolding).
I have the code for my stored procedure below:
using System;
using System.ComponentModel.DataAnnotations;
using System.Data;
using System.Data.SqlClient;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace Movies.Models
{
public class MovieDataAccessLayer
{
string connectionString = "Server=ServerName;Database=DatabaseMovies;Trusted_Connection=True;MultipleActiveResultSets=true";
public IEnumerable<Movies> MovieByMedia(byte? MediaID)
{
List<Movies> lstmovie = new List<Movies>();
using (SqlConnection con = new SqlConnection(connectionString))
{
SqlCommand cmd = new SqlCommand("usp_MovieByMedia", con);
cmd.CommandType = CommandType.StoredProcedure;
con.Open();
SqlDataReader rdr = cmd.ExecuteReader();
while (rdr.Read())
{
Movies movies = new Movies();
movies.MovieTitle = rdr["MovieTitle"].ToString();
movies.MovieYear = Convert.ToInt32(rdr["MovieYear"]);
lstmovie.Add(movies);
}
con.Close();
}
return lstmovie;
}
}
}
Here is my Media Details .cshtml.cs code
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.EntityFrameworkCore;
using Movies.Models;
namespace Movies.Pages.MediaDetails
{
public class DetailsModel : PageModel
{
private readonly Movies.Models.MoviesContext _context;
public DetailsModel(Movies.Models.MoviesContext context)
{
_context = context;
}
public Media Media { get; set; }
public async Task<IActionResult> OnGetAsync(byte? id)
{
if (id == null)
{
return NotFound();
}
Media = await _context.Media.FirstOrDefaultAsync(m => m.MediaTypeID == id);
if (Media == null)
{
return NotFound();
}
return Page();
}
}
}
And here is my .cshtml page
#model Movies.Pages.MediaDetails.DetailsModel
#{
ViewData["Title"] = "Details";
}
<h1>Details</h1>
<div>
<h4>Media</h4>
<hr />
<dl class="row">
<dt class="col-sm-2">
#Html.DisplayNameFor(model => model.Media.MediaType)
</dt>
<dd class="col-sm-10">
#Html.DisplayFor(model => model.Media.MediaType)
</dd>
</dl>
</div>
<div>
<a asp-page="./Edit" asp-route-id="#Model.Media.MediaTypeID">Edit</a> |
<a asp-page="./Index">Back to List</a>
</div>
Here is the code for my movie details .cshtml that I tried to connect to my Media .cshtml without success
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.EntityFrameworkCore;
using Movies.Models;
namespace Movies.Pages.MovieDetails
{
public class DetailsModel : PageModel
{
private readonly Movies.Models.MoviesContext _context;
public DetailsModel(Movies.Models.MoviesContext context)
{
_context = context;
}
MovieDataAccessLayer objmovie = new MovieDataAccessLayer();
public Movies
Movies { get; set; }
public async Task<IActionResult> OnGetAsync(byte id)
{
Movies = await _context.Movies.FirstOrDefaultAsync(m => m.MediaTypeID == id);
if (Movies == null)
{
return NotFound();
}
return Page();
}
}
}
I would like to modify my details page that lists out my media types so that when I click "details" for the respective media type, I can generate the result of my stored procedure for the media type that I click on. For instance, when I click "DVD" my stored procedure is executed to show me only my DVDs. How do I do this? I know that I somehow have to reference to my movie data access layer to execute my SP. Any assistance will be greatly appreciated.
The following code worked for me. Izzy's response was very helpful, but there needed to be some tweaks.
My Data Access Layer
using System;
using System.ComponentModel.DataAnnotations;
using System.Data;
using System.Data.SqlClient;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace MyMovies.Models
{
public class MovieByMediaDataAccessLayer
{
public List<Movies> GetMovieByMedia(byte? MediaID)
{
string connectionString = "Server=Servername;Database=Movies;Trusted_Connection=True;MultipleActiveResultSets=true";
var movies = new Movies();
using (var con = new SqlConnection(connectionString))
{
using (var cmd = new SqlCommand("usp_MovieByMedia", con))
{
cmd.Parameters.Add(new SqlParameter("#MediaID", SqlDbType.TinyInt) { Value = MediaID });
cmd.CommandType = CommandType.StoredProcedure;
con.Open();
using (var reader = cmd.ExecuteReader())
{
List<Movies> Movies = new List<Movies>();
while (reader.Read())
{
Movies movie = new Movies()
{
MovieTitle = reader["MovieTitle"].ToString(),
MovieYear = Convert.ToInt32(reader["MovieYear"])
};
Movies.Add(movie);
}
return Movies;
}
}
}
}
}
}
My Details class where I return all the movies executed from stored procedure (.cshtml.cs file)
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.EntityFrameworkCore;
using MyMovies.Models;
namespace MyMovies.Pages.MovieDetails
{
public class DetailsModel : PageModel
{
private readonly MyMovies.Models.MyMoviesContext _context;
public DetailsModel(MyMovies.Models.MyMoviesContext context)
{
_context = context;
}
MovieByMediaDataAccessLayer objmovie = new MovieByMediaDataAccessLayer();
public List<Movies> Movies { get; set; }
public async Task<IActionResult> OnGetAsync(byte? id)
{
if (id == null)
{
return NotFound();
}
Movies = objmovie.GetMovieByMedia(id);
if (Movies == null)
{
return NotFound();
}
return Page();
}
}
}
On my "Details" razor page for Media, I actually just went ahead and posted my movie details, since for each media type I wanted to display all the movies.
#page
#model MyMovies.Pages.MovieDetails.DetailsModel
#{
ViewData["Title"] = "Details";
}
<h4>Movies</h4>
<hr />
<div>
<table>
<tr>
<th>Movie Title</th>
<th>Movie Year</th>
</tr>
#foreach (var item in Model.Movies)
{
<tr>
<td class="col-sm-10">
#Html.DisplayFor(Model => item.MovieTitle)
</td>
<td class="col-sm-10">
#Html.DisplayFor(Model => item.MovieYear)
</td>
</tr>
}
</table>
</div>
<div>
<a asp-page="./Index">Back to List</a>
</div>
Based on your comments:
You will need to firstly make changes in your stored procedure by introducing a where clause and accepting a parameter in.
ALTER PROCEDURE usp_MovieByMedia
#MediaID INT //This is the parameter we will accept in the stored procedure which will be passed via our c# code
SELECT MovieTitle, MovieYear
FROM MOVIES
WHERE MediaTypeID = #MediaID // I'm assuming the names here, change as required
Now you want to change your method as follows:
public Movies GetMovieByMedia(int MediaID)
{
string connectionString = "Server=ServerName;Database=DatabaseMovies;Trusted_Connection=True;MultipleActiveResultSets=true";
var movies = new Movies();
using (var con = new SqlConnection(connectionString))
{
using (var cmd = new SqlCommand("usp_MovieByMedia", con))
{
cmd.Parameters.Add(new SqlParameter("#MediaID", SqlDbType.Int) { Value = MediaID }); //you was missing this.
cmd.CommandType = CommandType.StoredProcedure;
con.Open();
using (var reader = cmd.ExecuteReader())
{
if (reader.HasRows)
{
movies.MovieTitle = reader["MovieTitle"].ToString();
movies.MovieYear = Convert.ToInt32(reader["MovieYear"]);
}
}
}
}
return movies;
}
This will return you a single Movie object.
Please note I have assumed that your MediaTypeID column is of type INT in the db and not of varbinary(1)/binary(1). If this is incorrect change as required.
Now in your OnGetAsync action you can do:
Movies = objmovie.GetMovieByMedia(id);
If you don't want to do the filtering in the database then you can use linq to filter in your application as:
Movies = objmovie.GetMovieByMedia(id).FirstOrDefault(i => i.MediaTypeID == id); //this will only work if your method stays with the return type of IEnumerable<Movies>
I have a ScoreDataModelsController that contains the following Action Method:
public ActionResult Getnames()
{
return View(db.ScoreDataModels.ToList());
}
In Views I have the corresponding ScoreDataModels folder containing Getnames.cshtml:
#model IEnumerable<WebApplication1.Models.ScoreDataModel>
#{
ViewBag.Title = "Get Names";
Layout = "~/Views/Shared/_emptyLayout.cshtml";
}
<table class="table">
#foreach (var item in Model)
{
<tr>
<td>
#Html.DisplayFor(modelItem => item.Name)
</td>
</tr>
}
</table>
This all works fine. Now I would like to make this data (i.e. Names) accessible as json/XML using REST. I have managed to get the ApiController working with the standard settings and by opening http://.../api/Andi i get the values from the string[] in XML format:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Web.Http;
namespace WebApplication1.Controllers
{
public class AndiController : ApiController
{
// GET api/<controller>
public IEnumerable<string> Get()
{
return new string[] { "value1", "value2", "und en dritte" };
//Here I need help: ScoreDataModelsController sdm = new ScoreDataModelsController();
// var res = from r in sdm
}
// GET api/<controller>/5
public string Get(int id)
{
return "value";
}
// POST api/<controller>
public void Post([FromBody]string value)
{
}
// PUT api/<controller>/5
public void Put(int id, [FromBody]string value)
{
}
// DELETE api/<controller>/5
public void Delete(int id)
{
}
}
}
Now, instead of "value1, value2 ..." I would like to get the names from my ScoreDataModel / ScoreDataModelsController.
The ScoreDataModel looks like this. I have used this model to create the controller and view by scaffolding in Visual Studio:
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Web;
namespace WebApplication1.Models
{
public class ScoreDataModel
{
[Key]
public int ID { get; set; }
public string Name { get; set; }
public int Score { get; set; }
}
}
I would very much appreciate if you could lead me into the right direction to get this REST API working with my existing data controller / data model.
Create a central class which holds your data access logic, something like this:
public class DataRepository
{
private DatabaseContext db = new DatabaseContext();
public List<ScoreDataModel> GetNames()
{
return db.ScoreDataModels.ToList();
}
}
Now you can use this class to access your data from both the MVC controller and the api controller:
public class AndiController : ApiController
{
private DataRepository dbRepo = new DataRepository();
public IEnumerable<ScoreDataModel> Get()
{
List<ScoreDataModel> names = dbRepo.GetNames();
return names;
}
}
use this
var data= db.ScoreDataModels.ToList()
List<String>list=new List<String>();
foreach(var r in data)
{
list.add(r.Name);
}
return list;
$ I am able to create Menu Item in administration section
Plugin-->Import Product
however when i click on it, it gives me
error resource cannot be found. Please help on this
matter. I have attached model, view, controller and other related code below.
*************Model*************
namespace Nop.Plugin.Import.Product.Models
{
public class ImportProductModel
{
public string Button1 { get; set; }
}
}
**********Controller*************
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Web.Mvc;
using Nop.Admin.Models.Catalog;
using Nop.Core.Domain.Catalog;
using Nop.Core.Infrastructure;
using Nop.Plugin.Import.Product.Models;
using Nop.Services.Catalog;
using ns_5OClock;
namespace Nop.Plugin.Import.Product.Controllers
{
class ImportProductController : Controller
{
//
// GET: /DisplayTime/
public ActionResult Index(ImportProductModel model)
{
if (!String.IsNullOrEmpty(model.Button1))
{
CreateProduct();
}
return View();
}
//
}
}
*************View (ImportProduct.cshtml) **********
#{
Layout = "";
}
#model Nop.Plugin.Import.Product.Models.ImportProductModel
<table>
<tr>
<td>
<button title="Click to Create Products"
style="width:300px;height:60px" name="Button1"></button>
</td>
</tr>
</table>
***************Plugin (ImportProduct) **********************
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Nop.Core.Plugins;
using Nop.Web.Framework.Web;
namespace Nop.Plugin.Import.Product
{
public class ImportProduct : BasePlugin, IAdminMenuPlugin
{
public void BuildMenuItem(Telerik.Web.Mvc.UI.MenuItemBuilder menuItemBuilder)
{
menuItemBuilder.Text("Import Product");
//menuItemBuilder.Url("/Plugins/ProductImport/Index");
menuItemBuilder.Route("Plugin.Import.Product.ImportProduct");
}
}
}
**************Route Provider ****************
using System.Web.Mvc;
using System.Web.Routing;
using Nop.Web.Framework.Mvc.Routes;
namespace Nop.Plugin.Import.Product
{
public partial class RouteProvider : IRouteProvider
{
public void RegisterRoutes(RouteCollection routes)
{
routes.MapRoute("Plugin.Import.Product.ImportProduct",
"Plugins/Import/ImportProduct",
new { controller = "Import", action = "ImportProduct" },
new[] { "Nop.Plugin.Import.Product.Controllers" }
);
}
public int Priority
{
get
{
return 0;
}
}
}
}
Please check in your route register
routes.MapRoute("Plugin.Import.Product.ImportProduct",
"Plugins/Import/ImportProduct",
new { controller = "ImportProduct", action = "Index" },
new[] { "Nop.Plugin.Import.Product.Controllers" }
);
Whenever I try to pass a value from the view to the controller using a model, it shows up in the controller as null. I tried something very similar in another part of the project and did not have this problem. Why is this coming back null?
Here is the code from the controller:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using MovieProject2.Models;
namespace MovieProject2.Controllers
{
public class ReviewsController : Controller
{
public ActionResult Edit(int id = -1)
{
if (id < 0)
return HttpNotFound();
MovieReview review = MovieADO.getReviewByID(id);
return View(review);
}
[HttpPost]
public ActionResult Edit(MovieReview review)
{
if (review == null) return HttpNotFound();
return View(review);
}
}
View:
#model MovieProject2.Models.MovieReview
#{
ViewBag.Title = "Edit Review";
}
#{ //Not null here
if(#Model != null) {<h2>Edit Review for #Model.MovieReviewed.Title</h2>
<h4>Reviewed by #Model.Reviewer.Username</h4>}
else{<h2>Not Found</h2>
}
}
#using (Html.BeginForm())
{
Html.ValidationSummary(true);
<fieldset>
<div class="editor-label">
#Html.LabelFor(itemModel => Model.Rating)
</div>
<div class="editor-label">
#Html.EditorFor(itemModel => Model.Rating)
#Html.ValidationMessageFor(itemModel => Model.Rating)
</div>
<div class="editor-label">
#Html.LabelFor(itemModel => Model.Review)
</div>
<div class="editor-label">
#Html.EditorFor(itemModel => Model.Review)
#Html.ValidationMessageFor(itemModel => Model.Review)
</div>
<p>
<input type="submit" value="Change" />
</p>
</fieldset>
}
Model:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
namespace MovieProject2.Models
{
public class MovieReview
{
public int ReviewID { get; set; }
public Movie MovieReviewed { get; set; }
public User Reviewer { get; set; }
public int Rating { get; set; }
public string Review { get; set; }
public DateTime DateReviewed { get; set; }
public MovieReview() { }
}
}
Instead of
[HttpPost]
public ActionResult Edit(MovieReview review)
write
[HttpPost]
public ActionResult Edit(MovieReview model)
(and rename it further in that method from review to model. It should work.
OR
rename property of MovieReview.Review to something else (for example, Review1). You cannot have the same name for a property and passed model object (case insensitive)
You have to try this
#using (Html.BeginForm("Action methodName","controllerName"))
I want to design a table user control and dynamically add the properties of custom objects. I want to populate this table with a collection object with a generic type. How can I do that ? I can not send the properties of the class to PopulateTable with its custom headers.
What I have so far (in a usercontrol) is..
<table class="datatable">
<thead>
<asp:Literal id="ltrlHeaders" runat="server" />
</thead>
<tbody>
<asp:Literal runat="server" ID="ltrlContent" />
</tbody>
</table>
public void PopulateTable(? myCollection)
{
List<string> headers = new List<string>();
FieldInfo[] fields = type.GetFields();
foreach (FieldInfo f in fields)
{
headers.Add(f.Name);
}
// headers
StringBuilder headerString = new StringBuilder();
headerString.Append("<thead>");
headers.ForEach(delegate(string h)
{
headerString.Append(String.Format("<th>{0}</th>", h));
});
headerString.Append("</thead>");
ltrlHeaders.Text = headerString.ToString();
StringBuilder contentString = new StringBuilder();
foreach (var item in list)
{
contentString.Append("<tr>");
foreach (string fieldInfo in headers)
{
contentString.Append(String.Format("<td>{0}</td>", type.GetField(fieldInfo).GetValue(item) as string));
}
contentString.Append("</tr>");
}
ltrlContent.Text = contentString.ToString();
}
public void PopulateTable<T>(T myCollection)
Assuming that T objects can be cast to string. Or you might want:
public void PopulateTable<T>(IEnumerable<T> myCollection)
if you wanted to be able to pass a collection and know something about its interface.
A very simple method to do what you want might look like this. I've updated it with a List of Person as defined in my code. Note that I've also included a class derived from Person so you can see other properties.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Reflection;
public partial class _Default : System.Web.UI.Page
{
public class Person
{
public string Name { get; set; }
public string Title { get; set; }
}
public class WorkingPerson : Person
{
public string Job { get; set; }
}
protected void Page_Load(object sender, EventArgs e)
{
List<Person> people = new List<Person>();
people.Add(new Person() {Name = "T", Title = "Mr" }) ;
people.Add(new WorkingPerson() {Name="Face", Title="Mr", Job ="Film Star" } );
TextBox textBox = new TextBox();
Controls.Add(CreateTableFromObject<Person>(people));
}
public PlaceHolder CreateTableFromObject<T>(IEnumerable<T> objectList) where T : Person
{
PlaceHolder holder = new PlaceHolder();
foreach (T thing in objectList)
{
Table table = new Table();
PropertyInfo[] properties = thing.GetType().GetProperties();
TableRow propertiesSpan = new TableRow();
propertiesSpan.Cells.Add(new TableCell() { ColumnSpan = properties.Length, Text = String.Format("Properties of {0}", thing.GetType().FullName) });
table.Rows.Add(propertiesSpan);
TableRow tableRow = new TableRow();
foreach (PropertyInfo propertyInfo in properties)
{
TableCell tc = new TableCell();
tc.Text = propertyInfo.Name;
tableRow.Cells.Add(tc);
}
table.Rows.Add(tableRow);
holder.Controls.Add(table);
}
return holder;
}
}
This effectively takes an object, uses reflection to discover its properties, and then writes them out to table cells. You can change the signature to accept your custom collection, and modify the internals to render your table.