public ActionResult Index()
{
var groups = db.SHP_Products
.GroupBy(c => c.SHP_Category.Name,
(category, items) => new
{
CategoryName = category,
ItemCount = items.Count(),
Items = items
}
);
ViewBag.group = groups.ToList();
return View();
}
When running this it will show an error like this:
<ul>
#foreach (var m in ViewBag.group)
{
<h2>#m.CategoryName</h2>
PreviousNext
<li></li>
}
</ul>
'object' does not contain a definition for 'CategoryName'
You are passing a list of anonymous objects to the View.
Take a look at this answer Dynamic Anonymous type in Razor causes RuntimeBinderException
i think you are trying to access the <h2>#m.CategoryName</h2> directly, may be you can access it like #m.SHP_Category.Name i don't really know you the sequence on class in your code. try #m.
See this answer MVC Razor dynamic model, 'object' does not contain definition for 'PropertyName'
The reason for the error is that the dynamic type created by your GroupBy statement has an access level of "internal", which is not visible to the View. You can rectify by declaring a type or using an Explando - as discussed in this and other answers.
From
The reason for this is that the anonymous type being passed in the controller in internal, so it can only be accessed from within the assembly in which it’s declared. Since views get compiled separately, the dynamic binder complains that it can’t go over that assembly boundary.
One way to solve this is to use System.Dynamic.ExpandoObject.
public static ExpandoObject ToExpando(this object obj)
{
IDictionary<string, object> expandoObject = new ExpandoObject();
new RouteValueDictionary(obj).ForEach(o => expandoObject.Add(o.Key, o.Value));
return (ExpandoObject) expandoObject;
}
Then:
ToExpando(groups); // might need toList() it too.
Please use ViewData instead of ViewBag here like.
Controller:
public ActionResult Index()
{
var groups = db.SHP_Products
.GroupBy(c => c.SHP_Category.Name,
(category, items) => new
{
CategoryName = category,
ItemCount = items.Count(),
Items = items
}
);
ViewData["groups"] = groups.ToList();
return View();
}
View:
<ul>
#foreach (var m in (dynamic) ViewData["groups"])
{
<h2>#m.CategoryName</h2>
PreviousNext
<li></li>
}
Related
Actually i want to integrate paging in my view page and for that I am retrieving data from below code and I am passing this code from controller to view but any how I am facing issue as
var model = (from sr in db.StudentRequests
join c in db.Classes
on sr.ClassId equals c.ClassId
select new { sr.StudentRequestId,c.ClassName,sr.CreatedOn,sr.Location,sr.PaymentMethod }).ToList().ToPagedList(page ?? 1, 1);
return View(model);
and I am getting issue as
Type : InvalidOperationException
Message : The model item passed into the dictionary is of type 'PagedList.PagedList`1[<>f__AnonymousType3`5[System.Int32,System.String,System.Nullable`1[System.DateTime],System.String,System.String]]', but this dictionary requires a model item of type 'PagedList.IPagedList`1[Student_Tutor.Models.StudentRequest]'.
Source : System.Web.Mvc
My view side is as
#using PagedList;
#using PagedList.Mvc;
#model IPagedList<Student_Tutor.Models.StudentRequest>
#if (ViewBag.StudentRequest != null)
{
var StudentRequestId = (int)Model.First().StudentRequestId;// Here I am able to get the StudentRequestId
var StudentRequestTimecount = StudentRequestTime.Where(d => d.StudentRequestId == StudentRequestId).ToList();
var TutorStudentRequestcount = TutorStudentRequest.Where(d => d.StudentRequestId == StudentRequestId).ToList();
#Html.Displayfor(model => model.First().StudentRequestId)// here only text is displaying as StudentRequestId
#Html.Displayfor(Model => Model.First().CreatedOn)//here only text is diplaying as created on
}
please expalin why I am getting this error?
Update 1
var model = (from sr in db.StudentRequests
join c in db.Classes
on sr.ClassId equals c.ClassId
select new Studentvm{ StudentRequestId = sr.StudentRequestId,ClassName= c.ClassName,
CreatedOn =Convert.ToDateTime(sr.CreatedOn),Location= sr.Location,PaymentMethod= sr.PaymentMethod })
.ToList().ToPagedList(page ?? 1, 1);
return View(model);
but I am getting error as
An exception of type 'System.NotSupportedException' occurred in Student_Tutor.dll but was not handled in user code
Additional information: LINQ to Entities does not recognize the method 'System.DateTime ToDateTime(System.Object)' method, and this method cannot be translated into a store expression.
This part of your LINQ code
select new { sr.StudentRequestId,c.ClassName,sr.CreatedOn,sr.Location,sr.PaymentMethod }
That is creating an annonymous object for each item in the result collection you get from your LINQ query and you are creating a PagedList from that. But your view is strongly typed to PagedList<StudentRequest>
The ideal solution is to create a viewmodel to represent the data needed for this view and use that in the projection part of your LINQ query
public class StudentVm
{
public int StudentRequestId { set;get;}
public string ClassName { set;get;}
public DateTime CreatedOn { set;get;}
public string Location { set;get;}
}
Now use this view model for your projection
select new StudentVm { StudentRequestId = sr.StudentRequestId,
ClassName= c.ClassName,
Location = sr.Location,
CreatedOn = sr.CreatedOn }
And make sure your view is not strongly typed to PagedList<StudentVm>
#using PagedList;
#model PagedList<YourNamespace.StudentVm>
<table>
#foreach (var item in Model)
{
<tr>
<td>#Html.DisplayFor(a=> item.ClassName)</td>
<td>#Html.DisplayFor(a=> item.Location)</td>
<td>#Html.DisplayFor(a=> item.StudentRequestId)</td>
<td>#Html.DisplayFor(a=> item.CreatedOn)</td>
</tr>
}
</table>
Also, from your question, you are not really using the PagedList. To pass just a list of items, you do not need to convert that to PagedList<T> . You can simply send a List<StudentVm> from action method and change your view to be strongly typed to do that. Use PagedList if you are really using it (for paging)
can someone tell me what I'm doing wrong? :-)
I have this simple query:
var sample = from training in _db.Trainings
where training.InstructorID == 10
select new { Something = training.Instructor.UserName };
And I pass this to ViewBag.
ViewBag.Sample = sample;
Then I want to access it in my view like this:
#foreach (var item in ViewBag.Sample) {
#item.Something
}
And I get error message 'object' does not contain a definition for 'Something'. If I put there just #item, I get result { Something = SomeUserName }
Thanks for help.
This cannot be done. ViewBag is dynamic and the problem is that the anonymous type is generated as internal. I would recommend you using a view model:
public class Instructor
{
public string Name { get; set; }
}
and then:
public ActionResult Index()
{
var mdoel = from training in _db.Trainings
where training.InstructorID == 10
select new Instructor {
Name = training.Instructor.UserName
};
return View(model);
}
and in the view:
#model IEnumerable<Instructor>
#foreach (var item in ViewBag.Sample) {
#item.Something
}
If you want to send in ViewData For example and don't want to send in model
you could use the same could as in the upper answer
and in the Controller
enter code here
ViewData[Instractor] = from training in _db.Trainings
where training.InstructorID == 10
select new Instructor {
Name = training.Instructor.UserName
};
and in the view you need to cast this to
`IEnumerable<Instructor>`
but to do this you should use
#model IEnumerable<Instructor>
Then you could do something like this
IEnumerable<instructors> Instructors =(IEnumerable<Instructor>)ViewData[Instractor];
then go with foreach
#foreach (var item in Instructors ) {
#item.Something
}
I apply .Net MVC structure with C#. In the controller, I want to distinct specific column (IndustryName), and return the result to Html.DropDownListFor in view. But I get a running time error at view:
System.Web.HttpException: DataBinding: 'System.String' not include
'IndustryName' property.
Is there any one meet such problem, and how to solve it?
Thank you very much for your helping.
Controller:
public ActionResult Create()
{
var industrys = this._pmCustomerService.GetAll().Select (x => x.IndustryName).Distinct();
ViewBag.Industrys = new SelectList(industrys, "IndustryName", "IndustryName", null);
return View();
}
View:
#Html.DropDownListFor(model => model.IndustryName, (SelectList)ViewBag.Industrys)
Your query is returning IEnumerable<string> (you select only the IndustryName property in the .Select() clause. string does not contain an property named IndustryName so you get this error. Just change the SelectList to
ViewBag.Industrys = new SelectList(industrys);
This will bind the options value and display text to the value of IndustryName
The following sample implementation may help you fix the problem:
var industries= this._pmCustomerService.GetAll()
.GroupBy(ind => new { ind.IndustryName})
.Select(group => new SelectListItem
{
Text = group.First().Name,
Value = group .First().Name
}
);
ViewBag.Industries= industries;
You can find more about the 'GroupBy & Select' approach instead of using linq's Distinct(), here
View
#Html.DropDownList("ddlIndustries",(#ViewBag.Industries) as IEnumerable<SelectListItem>)
If you like to use DropDownListFor helper instead then modify view code as follows:
#{
var industries = ViewBag.Industriesas IEnumerable<SelectListItem>;
}
#Html.DropDownListFor(m=> industries , industries )
You get this error because you create SelectList with wrong collection. This should work i think.
var industrys = this._pmCustomerService.GetAll().Select(x => new SelectListItem
{
Value = x.IndustryName,
Text = x.IndustryName
}).Distinct();
ViewBag.Industrys = new SelectList(industrys);
return View();
You are only Selecting IndustryName which is obviously of type String, use DistinctBy() from MoreLinq by Jon Skeet, here is the reference SO post:
public ActionResult Create()
{
var industrys = this._pmCustomerService.GetAll().DistinctBy(x => x.IndustryName);
ViewBag.Industrys = new SelectList(industrys, "IndustryName", "IndustryName", null);
return View();
}
I have ASP.NET MVC 4 application with one view model class and about 20 views representing this view model. This views differs only by fields which user can edit. I want to merge all that views to one and define list of properties available to editing in strongly-typed manner. Ideally, I want something like this:
// Action
public ActionResult EditAsEngineer(int id)
{
//...
viewModel.PropertiesToChange = new List<???>()
{
v => v.LotNumber,
v => v.ShippingDate,
v => v.Commentary
};
return View(viewModel);
}
// View
if (#Model.PropertiesToChange.Contains(v => v.LotNumber)
{
#Html.TextBoxFor(m => m.LotNumber)
}
else
{
#Model.LotNumber
}
Is it possible to do something like this? Or is there a better solution?
Thank you.
Why note something like this (its pseudo code)
public class Prop{
string PropertyName {get;set;}
bool PropertyEditable {get;set;}
}
public ActionResult EditAsEngineer(int id)
{
viewModel.PropertiesToChange = new List<Prop>()
{
new Prop{PropertyName = LotNumber, PropertyEditable = true}
};
return View(viewModel);
}
#foreach (var pin Model.PropertiesToChange)
{
if(p.PropertyEditable){
#Html.TextBoxFor(p)
}else{
#Html.DisplayFor(p)
}
}
This will solve HALF of your problem. You will also need to create a IEqualityComparer<Expression> for your code to work (the default is to check for ref-equals).
return from p in typeof(T).GetProperties()
let param = System.Linq.Expressions.Expression.Parameter(typeof(T), "x")
let propExp = System.Linq.Expressions.Expression.Property(param, p)
let cast = System.Linq.Expressions.Expression.Convert(propExp, typeof(object))
let displayAttribute = p.CustomAttributes.OfType<System.ComponentModel.DataAnnotations.DisplayAttribute>()
.Select(x => x.Order).DefaultIfEmpty(int.MaxValue).FirstOrDefault()
orderby displayAttribute
select System.Linq.Expressions.Expression.Lambda<Func<T, object>>(cast, new [] {param});
This will list out ALL the properties for T. You would also probabily want to use Expression<Func<T, object>> as the type for defining your list of properties.
This will allow you to create a generic view over all properties.
Also you will want to wrap this in some kind of a cache, as this code is SLOW.
Is there any way to create a ViewDataDictionary with a model and additional properties with a single line of code. I am trying to make a RenderPartial call to a strongly-typed view while assembling both the model and some extra display configuration properties without explicitly assembling the ViewDataDictionary across multiple lines. It seems like it would be possible given the RenderPartial overload taking both a model object and a ViewDataDictionary but it looks like it simply ignores the ViewDataDictionary whenever they are both populated.
// FAIL: This will result in ViewData being a ViewDataDictionary
// where Model = MyModelObject and there are no other parameters available.
this.Html.RenderPartial("SomePartialView", MyModelObject, new ViewDataDictionary(new { SomeDisplayParameter = true }));
I found someone else with the same problem, but their solution is the same multi-line concept I found: create a discrete ViewDataDictionary with the model, add the new parameter(s) and use it in the RenderPartial call.
var SomeViewData = new ViewDataDictionary(MyModelObject);
SomeViewData.Add("SomeDisplayParameter", true);
this.Html.RenderPartial("SomePartialView", SomeViewData);
I can always wrap that logic into a ChainedAdd method that returns a duplicate dictionary with the new element added but it just seems like I am missing some way of creating a ViewDataDictionary that would do this for me (and that is a bit more overhead than I was hoping for).
this.Html.RenderPartial("SomePartialView", new ViewDataDictionary(MyModelObject).ChainedAdd("SomeDisplayParameter", true));
public static ViewDataDictionaryExtensions {
public static ViewDataDictionary ChainedAdd(this ViewDataDictionary source, string key, object value) {
return source.ChainedAdd(new KeyValuePair<string,object>(key, value));
}
public static ViewDataDictionary ChainedAdd(this ViewDataDictionary source, KeyValuePair<string, object> keyAndValue) {
ViewDataDictionary NewDictionary = new ViewDataDictionary(source);
NewDictionary.Add(keyAndValue);
return NewDictionary;
}
}
As well, trying to assemble a ViewDataDictionary with an explicit Model and ModelState simply causes a compilation error because the ModelState is read-only.
// FAIL: Compilation error
this.Html.RenderPartial("SomePartialView", new ViewDataDictionary { Model = MyModelObject, ModelState = new ViewDataDictionary( new { SomeDisplayParameter = true }});
ANSWER(S): It looks like Craig and I ended up finding two separate syntaxes that will get the job done. I am definitely biased in this case, but I like the idea of setting the model first and "decorating" it afterwards.
new ViewDataDictionary(MyModelObject) { { "SomeDisplayParameter", true }, { "SomeOtherParameter", 3 }, { "SomeThirdParameter", "red" } };
new ViewDataDictionary(new ViewDataDictionary() { {"SomeDisplayParameter", true }})
{ Model = MyModelObject };
Of course, I would still be spinning my wheels without his [eventually spot-on] answer, so, circle gets the square.
Use an object initializer and collection initializers:
new ViewDataDictionary(new ViewDataDictionary() { {"SomeDisplayParameter", true }})
{
Model = MyModelObject
}
The inner ViewDataDictionary gets its collection initialized, then this populates the "real" ViewDataDictionary using the constructor overload which takes ViewDataDictionary instead of object. Finally, the object initializer sets the model.
Then just pass the whole thing without setting MyModelObject separately:
this.Html.RenderPartial("SomePartialView", null,
new ViewDataDictionary(new ViewDataDictionary() { {"SomeDisplayParameter", true }})
{ Model = MyModelObject });
Using Craig's answer as a starting point--I didn't even know you could combine both a constructor call and an object initializer--I stumbled on this snippet from Palermo that leads to a combination that works. He uses some sort of dictionary shorthand that somehow ends up populating the ModelState when consumed by the ViewDataDictionary object initializer.
new ViewDataDictionary(MyModelObject) { { "SomeDisplayParameter", true }, { "SomeOtherParameter", 3 }, { "SomeThirdParameter", "red" } };
// Of course, this also works with typed ViewDataDictionary objects (what I ended up using)
new ViewDataDictionary<SomeType>(MyModelObject) { { "SomeDisplayParameter", true }, { "SomeOtherParameter", 3 }, { "SomeThirdParameter", "red" } };
I still don't see how this ends up working given that you cannot set the ModelState explicitly in an initializer, but it does seem to maintain both the original model object and the "appended" parameters for the view. There are definitely a number of other permutations of this syntax that do not work--you cannot combine the model with the dictionary in a single object or use object-initializer syntax for the dictionary values--but the above version seems to work.
I created an extension method on HtmlHelper to copy the property names and values from an anonymous object to a ViewDataDictionary.
Sample
Html.RenderPartial("SomePartialView", MyModelObject, new { SomeDisplayParameter = true })
HtmlHelper Extension
public static void RenderPartial(this HtmlHelper htmlHelper, string partialViewName, object model, object viewData)
{
var vdd = new ViewDataDictionary(model);
foreach (var property in viewData.GetType().GetProperties()) {
vdd[property.Name] = property.GetValue(viewData);
}
htmlHelper.RenderPartial(partialViewName, vdd);
}
This is what worked for me in old style mvc aspx view:
<% Html.RenderPartial("ContactPartial", Model.ContactFactuur, new ViewDataDictionary(this.ViewData ) { TemplateInfo = new TemplateInfo { HtmlFieldPrefix = "Factuur" } }); %>
the thing here is that in the constructor I use the current viewdata "new ViewDataDictionary(this.ViewData)" which is a viewdatadictionary containing the modelstate that I need for the validationmessages.
I came here with the exact same question.
What I thought might work was this (pardon the VB Razor syntax)
#Code Html.RenderPartial("Address", Model.MailingAddress, New ViewDataDictionary(New With {.AddressType = "Mailing Address"}))End Code
But of course you get this error at run-time:
System.InvalidOperationException was unhandled by user code
Message=The model item passed into the dictionary is of type 'VB$AnonymousType_1`1[System.String]', but this dictionary requires a model item of type 'ViewModel.Address'.
But what I found, is that what I really wanted was to use was Editor Template anyway.
Instead of RenderPartial use:
#Html.EditorFor(Function(model) model.MailingAddress, "Address", New With {.AddressType = "Mailing Address"})
An editor template is just a partial view that lives
~/Views/{Model|Shared}/EditorTemplates/templatename.vbhtml
My template for Address is a strongly-typed partial view, but the EditorFor method gives the ability to add additional view data items easily with an anon object.
In the example above I didn't need to include the template name "Address", since MVC would would look for a template with the same name as the model type.
You can also override the display template in the same way.