Passing model data to view in MVC 5 - c#

I am darned new to the world of MVC 5 .NET... I am trying to get my head around the Tree Control from Ignite UI.
I need to retrieve my data from my model and pass it to my view... However, I keep getting the following error:
The model item passed into the dictionary is of type 'System.Data.Entity.Infrastructure.DbQuery1[System.String]', but this dictionary requires a model item of type 'System.Linq.IQueryable1[DataViewer.Models.Viewer]'.
Here is my model:
namespace DataViewer.Models
{
[Table("dbo.table")]
public class Viewer
{
[Key, Column(Order=0)] public long id { get; set; }
public int Revision { get; set; }
public string GeoPSRType { get; set; }
public string GeoName { get; set; }
public string L2Name { get; set; }
public string L3Name { get; set; }
}
}
etc.
And my Controller:
namespace DataViewer.Controllers
{
public class ViewerController : Controller
{
private ViewerDBContext db = new ViewerDBContext();
public ActionResult Index()
{
var result = db.Items.Select(i => i.GeoName).Distinct().OrderBy(n => n);
return View(result);
}
}
}
And my view:
#model IQueryable<DataViewer.Models.Viewer>
#using Infragistics.Web.Mvc
#(Html.Infragistics().Tree()
.DataSource(Model)
.Bindings(b =>
b.TextKey("GeoName")
.PrimaryKey("ID")
.ValueKey("ID")
.ChildDataProperty("L2PSRType")
)
)
.DataSource(Model)
.LoadOnDemand(true)
.DataSourceUrl(Url.Action("tree-data-on-demand"))
.DataBind()
.Render()
)
I figured the issue was that I should be passing a strongly typed return-type by using
IQueryable<Viewer> result =
db.Items.Select(i => i.GeoName).Distinct().OrderBy(n => n);
but that throws a different error:
InvalidOperationException: The model item passed into the dictionary is of type
'System.Data.Entity.Infrastructure.DbQuery`1[System.String]',
but this dictionary requires a model item of type
'System.Linq.IQueryable<DataViewer.Models.Viewer>.'
An explicit conversion exists, are you missing a cast?
Is this relating to the fact that I'm pretty rusty on strongly typed syntax, or am I just being a total maroon (or is it a combination)?
P.S. Also, I have no doubt that the Infragistics().Tree() portion of the view is not correct, I am really more concerned about getting my data to my view (at this point). I just wanted to include all code I had...

You're passing the wrong type to your view. Look at the type of model the view expects:
#model IQueryable<DataViewer.Models.Viewer>
A queryable collection of Viewer objects. But look at what you're giving it:
db.Items.Select(i => i.GeoName).Distinct().OrderBy(n => n);
A collection of GeoName properties. Is GeoName by any chance a string? (The error implies that it is.)
If your model is expecting a collection of objects, you need to give it such a collection. You can't just give it some strings and expect it to convert those strings into objects.

The problem is the view is expecting different data than you're providing it with your controller.
Inside your view:
#model IQueryable<DataViewer.Models.Viewer>
means the view is expecting an IQueryable<> collection of Viewer's, but inside your controller:
// GET: /Viewer/
public ActionResult Index()
{
var result = db.Items.Select(i => i.GeoName).Distinct().OrderBy(n => n);
return View(result);
}
You are passing a collection of strings.
You need to either return Viewer's, or change the model of the view.

Change your Action Result to this...
public ActionResult Index()
{
// var result = db.Items.Select(i => i.GeoName).Distinct().OrderBy(n => n);
Viewer model = new Viewer();
model = (from x in db.Items
select new Viewer
{
GeoPSRType = x.FieldName
}).Distinct().FirstOrDefault().OrderBy(x => x.SomeField);
return View(model);
}
There's probably some syntax errors in that query but this gives you a general understanding of what you need to pass into the View

Related

MVC Pass display data between views

I have a view model that is used to display a form on one view, and then is also used to represent the POST data to an action. The action then displays another view model that contains much of the same data from the first view model. However, the first view model has several "display only" properties that are also required on the second view model (for display only on the second view also).
I am wondering what the best way to pass this "display only" data to the second view would be. Currently, the best solution I have come up with is to have a bunch of hidden form fields that contain the display only property values, and then the model gets auto-populated for the action that handles the form POST. However, using hidden form fields seems very "hackish", and there seems like there should be a better solution to passing this data to another view The action doesn't need the display only information, it is only accessing it to populate the properties of the second view model that is passed to the second view.
Let me just explain my question with code, as what I am after is probably better understood through code than words.
Models:
public class SearchFilters
{
// ...
}
public class SearchResult
{
public int Id { get; set; }
public bool Selected { get; set; }
public string SomeDisplayValue1 { get; set; }
public string SomeDisplayValue2 { get; set; }
// ...
}
public class ResultsViewModel
{
public IList<SearchResult> Results { get; set; }
// ...
}
public class DoSomethingWithSelectedResultsViewModel
{
public IList<SearchResult> SelectedResults { get; set; }
public string SomeOtherProperty { get; set; }
// ...
}
Controller:
[HttpPost]
public ActionResult Results(SearchFilters filters)
{
ResultsViewModel results = new ResultsViewModel();
// ...
return new View(results);
}
[HttpPost]
public ActionResult DoSomethingWithSelectedResults(ResultsViewModel model)
{
// ...
return View(new DoSomethingWithSelectedResultsViewModel
{
SelectedResults = model.Results.Where(r => r.Selected).ToList(),
SomeOtherProperty = "...",
// ...
});
}
View: Results.cshtml
#model ResultsViewModel
#using (Html.BeginForm("DoSomethingWithSelectedResults", "Search"))
{
<table>
for (int i = 0; i < Model.Results.Count; i++)
{
<tr>
<td>
#Html.CheckBoxFor(m => Model.Results[i].Selected)
#* I would like to eliminate these hidden inputs *#
#Html.HiddenFor(m => Model.Results[i].Id)
#Html.HiddenFor(m => Model.Results[i].SomeDisplayValue1)
#Html.HiddenFor(m => Model.Results[i].SomeDisplayValue2)
</td>
<td>#Html.DisplayFor(m => Model.Results[i].SomeDisplayValue1)</td>
<td>#Html.DisplayFor(m => Model.Results[i].SomeDisplayValue2)</td>
<tr>
}
</table>
<button type="submit">Do Something With Selected Results</button>
}
As far as I know, one of the best way to pass data from View to another View through a Controller is to use ViewBag, ViewData or TempData. As an example, you can pass the data retrieved from View I as shown below:
TempData[DataToBePassed] = model.CustomData;
And then retrieve this data in View II similar to that:
#if(TempData[DataToBePassed] != null)
{
var dataFromFirstView = TempData[DataToBePassed];
}
For more information take a look at When to use ViewBag, ViewData, or TempData in ASP.NET MVC 3 applications.
You could put the model in the TempData property of the controller, that way it's automatically available in the next request.
More here
Found what I was looking for, I just hadn't worked with MVC enough yet to know about it. The Controller.UpdateModel method does exactly what I was looking for.
Example (using the code from the question):
[HttpPost]
public ActionResult DoSomethingWithSelectedResults()
{
// Load initial model data here, in this case I had simply cached the results in
// temp data in the previous action as suggested by Emeka Awagu.
ResultsViewModel model = (ResultsViewModel)TempData["results"];
// Call UpdateModel and let it do it's magic.
UpdateModel(model);
// ...
return View(new DoSomethingWithSelectedResultsViewModel
{
SelectedResults = model.Results.Where(r => r.Selected).ToList(),
SomeOtherProperty = "...",
// ...
});
}
Using this method I was able to eliminate all the hidden form fields and did not have to write any custom copy logic, since UpdateModel deals with it automatically.
Note: I did have to implement some custom model binders to get things to work correctly with dictionaries and collections (see here, here, and here).

MVC 4 how pass data correctly from controller to view

I currently have a controller with a LINQ statement that i am passing data from to my view. I am trying to find a more efficient and better coding method to do this.
My home controller statement is as follows.
Var Melt
Furnace1 =
(from item in db.tbl_dppITHr
where item.ProductionHour >= StartShift && item.ProductionHour <= EndDate
select item).Sum(x => x.Furnace1Total),
ViewData["Furnace1Total"] = Melt.Furnace1;
In my view i then reference the ViewData To show this. Using
#model dynamic
Now i have quite alot of linq statements inside the Index method. And for each one i am doing the ViewData[]
I am hoping that someone can show how i pass more than one var from a controller across to a view without the ViewData or ViewBag methods. And how i would get access to this within my view.
You should create a ViewModel with all of your data needed and then pass that down to the view.
public class ViewModel
{
public List<int> Melt1 { get; set; }
public void LoadMeltProperties()
{
if (Melt1 == null)
{
Melt1 = new List<int>();
}
Melt1 = (from item in db.tbl_dppITHr
where item.ProductionHour >= StartShift && item.ProductionHour <= EndDate
select item).Sum(x => x.Furnace1Total).ToList();
}
public ViewModel Load()
{
LoadMeltProperties();
return this;
}
}
public ActionResult YourControllerAction()
{
var vm = new ViewModel().Load();
return View("ViewName", vm);
}
Then in your View you can use a strongly typed model rather than dynamic
#model ViewModel
You can then iterate over your ViewModel properties via:
foreach(var melt in Model.Melt1) {
// do what you require
}
IMHO, you should create a ViewModel an pass data using it.
Create a class
public class MyViewModel
{
public <MeltFurnace1Type> MeltFurnace1{get;set;}
}
In Action Method
public ActionResult Action()
{
MyViewModel vm = new MyViewModel();
vm.MeltFurnace1 = something;
return View("YourViewName", vm);
}
In View
#model MyViewModel
//You can access your property using
Model.MeltFurnace1
If you need to pass data actually from the controller and its data is depend on internal state or input controller parameters or has other properties of "business data" you should use Model part from MVC pattern:
Model objects are the parts of the application that implement the
logic for the application's data domain. Often, model objects retrieve
and store model state in a database. For example, a Product object
might retrieve information from a database, operate on it, and then
write updated information back to a Products table in a SQL Server
database.
You can see details here or look to the Models and Validation in ASP.NET MVC part of Microsoft tutorial.
Add model class:
public class Person
{
public int Id { get; set; }
public string Name { get; set; }
public int Age { get; set; }
public string City { get; set; }
}
Pass model object to the view:
public ActionResult Index()
{
var model = GetModel();
return View(model);
}
Add strongly typed View via define model type:
#model Person
Use Model variable in your view:
#Model.City
Use models instead
var Melt
Furnace1 =
(from item in db.tbl_dppITHr
where item.ProductionHour >= StartShift && item.ProductionHour <= EndDate
select item).Sum(x => x.Furnace1Total),
return View("SomeVIew",MeltFurnace1)
In view#model "TypeOfMeltFurnace1"
You can reference model in view by property Model
If someone is still looking for options…
You also can pass object from Controller to View, if you don’t use any particular Model for the View. To pass just a single (or maybe few) parameter from the Controller to the View, you can do it inside View() Method.
In the Controller:
public async Task<IActionResult> Update(int id)
{
return View(new { Id = id });
}
Then in the View you can access your parameter like this (assuming you don’t use any other Model inside your View):
<div>
#Html.ActionLink("Link name", "ControllerMethod", "ControllerName", new { id = (int)Model.Id })
</div>
Otherwise, like already mentioned, pass your model inside View() Method.

Which model I have to pass to view?

The goal
Work with a model in my view.
The problem
I'm getting the follow exception:
The model item passed into the dictionary is of type 'System.Linq.Enumerable+WhereSelectListIterator2[MyApp.Models.Data.bm_product_categories,<>f__AnonymousType31[System.String]]', but this dictionary requires a model item of type 'System.Collections.Generic.IEnumerable`1[MyApp.Models.Data.bm_product_categories]'.
Details
I'm using:
C#.Net
Razor Engine
MVC 4
Visual Studio 2012
My partial view (_CategoriesList):
#model IEnumerable<BluMercados.Models.Data.bm_product_categories>
<h1>Testing</h1>
My controller (CategoriesController) and his method:
public ActionResult Render()
{
var sluggifiedProjection =
db.bm_product_categories
.ToList()
.Select(category => new
{
CategoryNameSlugged = category.Category_Name.GenerateSlug()
});
return PartialView("_CategoriesList", sluggifiedProjection);
}
The question
How can I fix this problem? I really do not know what is the model that I have to pass from controller to view.
you are passing an anonymous type as part of the model. While you can do this with the dynamic keyword, you are probably better off making a ViewModel.
Something like
public class CategoryViewModel
{
public WhatEverTypeThisIs NameSlugged { get; set; }
}
then
public ActionResult Render()
{
var sluggifiedProjection =
db.bm_product_categories
.ToList()
.Select(category => new CategoryViewModel
{
NameSlugged = category.Category_Name.GenerateSlug()
});
return PartialView("_CategoriesList", sluggifiedProjection);
}
and the model will be something like
#model IEnumerable<BluMercados.ViewModels.CategoryViewModel>
depending what namespace you put it in
The problem is caused by the select where you currently create instances of an anonymous type with a single property CategoryNameSlugged. You should make sure that your select operation does not change the type of the enumerable. This might work out if the property can be set:
.Select(category =>
{
category.CategoryNameSlugged = category.Category_Name.GenerateSlug();
return category;
});

Using an IEnumerable<Anonymous Type> within Dynamic Type View

var s = myInventoryEntities.p_Forms().Select(i => new { i.Customer, i.Order_ID }).Distinct().ToList();
ViewBag.MyData = s;
return View();
this gives me
{ Customer = "Bob", Order_ID = 2644550 }
In my razor code
i traverse the data
#foreach ( var x in #ViewBag.MyData){
x.Customer // Does not exist! :(
}
Please help!
'object' does not contain a definition for 'Customer'
ViewBag data's life time is very limited. Are you sure you are coming to this view from the same action method where you set the ViewBag data ?
Suggestion : Try to Avoid Dynamic types liek ViewData/ViewBag. Sticking to Strongly typed makes your code much better readable/ maintainable.
If your Domain object and What you want to display is same, do not bother about creating a ViewModel. else create a viewmodel
public class CustomerOrder
{
public string CustomerName
public int OrderID { set;get;}
//other properties as relevant (to the view)
}
and return that instead of the ViewBag
public ActionResult GetOrders()
{
var s = myInventoryEntities.p_Forms().
Select(i => new CustomerOrder {
CustomerName= i.Customer,
OrderID=i.Order_ID
}).Distinct().ToList();
return View(s);
}
And in the view
#model IList<CustomerOrder>
#foreach ( var x in Model)
{
<p>#x.Customer</p>
}

Passing object to view error

I am getting this error when trying to pass my object to the view. I am new to MVC so please forgive me.
The model item passed into the dictionary is of type 'System.Collections.Generic.List1[<>f__AnonymousType13[System.Int32,System.String,System.Nullable1[System.DateTime]]]', but this dictionary requires a model item of type 'System.Collections.Generic.IEnumerable1[MvcApplication1.Models.storageProperty]'
I am trying to pass a list for a table that will show objects from the storageProperty table with the last date (if there is one) from the expenses table. Most properties have had at least one expense audit, some have had many, and others have had none.
Here is the code from the controller:
var viewModel = db.storageProperties.Select(s => new
{
s.storagePropertyId,
s.BuildName,
latestExpenseSurvey = (DateTime?)s.expenses.Max(e => e.expenseDate)
}).ToList();
return View(viewModel);
}
and the #model statement in the view calls for a storageproperty object. I am using mvc3 with the entity framework. It appears obvious that I cannot pass this list object in place of the storageproperty object, but I can't figure out what to do instead, how should I do this?
Thanks in advance.
Never pass anonymous objects to views. You should always pass view models.
So as always in an ASP.NET MVC application you start by defining a view model which will reflect the requirements of your view:
public class MyViewModel
{
public int StoragePropertyId { get; set; }
public string BuildName { get; set; }
public DateTime? latestExpenseSurvey { get; set; }
}
Then in your controller return an IEnumerable<MyViewModel>:
public ActionResult Index()
{
var viewModel = db.storageProperties.Select(s => new MyViewModel
{
StoragePropertyId = s.storagePropertyId,
BuildName = s.BuildName,
LatestExpenseSurvey = (DateTime?)s.expenses.Max(e => e.expenseDate)
}).ToList();
return View(viewModel);
}
and finally strongly type your view to a collection of this view model:
#model IEnumerable<MyViewModel>
<div>
#Html.EditorForModel()
</div>
Your Linq query projects to an anonymous type. You need to create a named type for this projection in order to refer to it from the view.

Categories