I have, surprisingly, been searching on the internet how to bind a webgrid column from a complex type that is data source last 3 hours. But I could not find any useful information.
there is a topic on Complex WebGrid Binding in MVC 3, but it did not work in my scenario.
To simplify it, let's say I have a list of Employee objects that I populate WebGrid from, each Employee have Address property which is Address type.
I would like to show the City property of Address field in the WebGrid along with other fields of Employee.
I assumed that grid.Column("Address.City") would work, but it does not. Is that something not supported, or I am doing something wrong.
Thanks for your help.
Regards
AnarchistGeek
I could not see how your answer was fixing the issue until I realised that what I was missing is that sometimes the property can be null but instead of a null reference error you get the error Column "Address.City" does not exist. unless you check for null in the format property....I found the the answer here
#functions{
public class Employee
{
public string Name { get; set; }
public Address Address { get; set; }
}
public class Address
{
public string City { get; set; }
}
}
#{
var myClasses = new List<Employee>{
new Employee { Name="A" , Address = new Address{ City="AA" }},
new Employee { Name="B" , Address = new Address{ City="BB" }},
new Employee { Name="C" , Address = new Address{ City=null }},
new Employee { Name="D" , Address = null},
};
var grid = new WebGrid(source: myClasses);
}
#grid.GetHtml(
columns: grid.Columns(grid.Column("Address.City",
header: "City",
format: #<text>#if (item.Address != null)
{#item.Address.City}
</text>),
grid.Column("Name")))
This is the answer I got in ASP.NET forums and I wanted post it here in case others may need that.
The thread link is http://forums.asp.net/p/1718114/4586676.aspx/1?Re%20Complex%20data%20type%20in%20WebGrid
and the solution is(Tested):
#functions{
public class Employee
{
public string Name { get; set; }
public Address Address { get; set; }
}
public class Address
{
public string City { get; set; }
}
}
#{
var myClasses = new List<Employee>{
new Employee { Name="A" , Address = new Address{ City="AA" }},
new Employee { Name="B" , Address = new Address{ City="BB" }},
new Employee { Name="C" , Address = new Address{ City="CC" }},
new Employee { Name="D" , Address = new Address{ City="DD" }},
};
var grid = new WebGrid(source: myClasses);
}
#grid.GetHtml(
columns: grid.Columns(grid.Column("Address.City",header:"City"), grid.Column("Name")))
I hope it helps others.
Cheers.
Related
when I use TryUpdateModelAsync method to update Model I give this error, any one have an idea about this
The passed expression of expression node type 'NewArrayInit' is invalid. Only simple member access expressions for model properties are supported.
Code for this issue is as below.
[HttpPost,ActionName("Edit")]
[ValidateAntiForgeryToken]
public async Task<ActionResult> EditLocaton([ModelBinder(typeof(EncryptDataBinder))]int id, IFormCollection formCollection)
{
ModelState.Clear();
LocationModel location = new LocationModel();
try
{
await TryUpdateModelAsync<LocationModel>(location, "", p => new object[] { p.ID, p.Name, p.Code, p.RowVersion });
code for the Location Model
public class LocationModel : BaseEntity
{
[Required]
[StringLength(100)]
[Display(Name = "Location Name")]
public string Name { get; set; }
[Required]
[StringLength(20)]
public string Code { get; set; }
[NotMapped]
public string enID { get; set; }
}
Please help for this issue.
Here's a sample for TryUpdateModelAsync.
var studentToUpdate = await _context.Students.FirstOrDefaultAsync(s => s.ID == id);
if (await TryUpdateModelAsync<Student>(
studentToUpdate,
"",
s => s.FirstMidName, s => s.LastName, s => s.EnrollmentDate))
{
try
...
It updates the studentToUpdate using data provided in the incoming request.
So I'm afraid you can try await TryUpdateModelAsync<LocationModel>(location, "", p => p.enID, p => p.Name, p => p.Code);. In your code snippet, I don't find RowVersion in your LocationModel, not sure about it.
let problemDocument = documentClient.CreateDocumentQuery<ProblemDatabaseModel>("")
problemDocument
doesn't seem to work
(problemDocument.Select(fun problem -> problem))
doesn't seem to work
(problemDocument.Where(fun problem -> problem.id = problem.id))
doesn't seem to work either. Any ideas?
If you want to query all document in document db, please try to below code:
documentClient.CreateDocumentQuery<ProblemDatabaseModel>("").ToList();
Please note that, we can store different json entity in documentDB, if document property does not in your data model, it will give a default value. I have a simple test for this:
Data model:
public class Cred
{
[JsonProperty(PropertyName = "id")]
public string ID { get; set; }
[JsonProperty(PropertyName = "title")]
public string Title { get; set; }
[JsonProperty(PropertyName = "credits")]
public int Credits { get; set; }
[JsonProperty(PropertyName = "authordetails")]
public AuthDetail AuthInfo { get; set; }
}
If json data in documentDB is:
{
"id": "CDC103",
"title": "Fundamentals of database design",
"authordetails": {
"Name": "dave",
"Age": 33
},
"custom":"test"
}
client.CreateDocumentQuery<Cred>(UriFactory.CreateDocumentCollectionUri("jambordb", "jamborcols")).ToList();
Here is the result:
From the screenshot we know that, the property "custom" will not included in our data model. and the credits will give a default value 0.
How to bind a column/field to a child property of the json result in the model settings of the Kendo grid (in javascript)? For example, I want the grid to contain columns: FName, LName, Street and Address. Basically I want to flatten the hierarchical structure returned by the web service.
Kendo Settings
fields: {
FName: { type: "string" },
LName: { type: "string" },
// How to map to child properties below?
Street: { field: "Address.Street" }, // this is wrong
City: { field: "Address.City" } // this is wrong
}
JSON
{
"FName": "William",
"LName ": "Shakespeare",
"Address":
{
"Address": "123 Street Ln",
"City": "Philadelphia"
}
}
You don't do it like that. You need to create a class 'Model' that flattens the data graph. You will be able to use lazy loading during the construction of the Model. Either send this Model to the View via the controller or attach it to a larger ViewModel (just a Model of Models not MVVM) that is sent to the View. Then bind this to the Grid.
But, you will be happier to use Ajax loading of the same Model as JSON, which is what I think you are trying to do.
Model
public class ContactModel
{
public string FName { get; set; }
public string LName { get; set; }
public string Address { get; set; }
public string City { get; set; }
public ContactModel()
{}
public ContactModel(Contact contact) // IContact is better if you have Interfaces
{
FName = contact.FName;
LName = contact.LName;
Address = contact.Address.Address;
City = contact.Address.City;
}
// Neat Linq trick to convert database query results directly to Model
public static IList<ContactModel> FlattenToThis(IList<Contact> contacts)
{
return contacts.Select(contact => new ContactModel(contact)).ToList();
}
}
Controller
public JsonResult ReadContacts([DataSourceRequest]DataSourceRequest request)
{
var contacts = _contactsDataProvider.Read(); // Your database call, etc.
DataSourceResult result = ContactModel.FlattenToThis(contacts).ToDataSourceResult(request);
return Json(result, JsonRequestBehavior.AllowGet);
}
But I don't think Will ever made it to Philly. ;)
The DisplayAttribute in System.ComponentModel.DataAnnotations has a GroupName property, which allows you to logically group fields together in a UI control (e.g. a property grid in WPF/WinForms).
I am trying to access this metadata in an ASP.NET MVC3 application, essentially to create a property grid. If my model looks like this:
public class Customer
{
[ReadOnly]
public int Id { get;set; }
[Display(Name = "Name", Description = "Customer's name", GroupName = "Basic")]
[Required(ErrorMessage = "Please enter the customer's name")]
[StringLength(255)]
public string Name { get;set; }
[Display(Name = "Email", Description = "Customer's primary email address", GroupName = "Basic")]
[Required]
[StringLength(255)]
[DataType(DataType.Email)]
public string EmailAddress { get;set; }
[Display(Name = "Last Order", Description = "The date when the customer last placed an order", GroupName = "Status")]
public DateTime LastOrderPlaced { get;set; }
[Display(Name = "Locked", Description = "Whether the customer account is locked", GroupName = "Status")]
public bool IsLocked { get;set; }
}
and my view looks like this:
#model Customer
<div class="edit-customer">
#foreach (var property in ViewData.ModelMetadata.Properties.Where(p => !p.IsReadOnly).OrderBy(p => p.Order))
{
<div class="editor-row">
#Html.DevExpress().Label(settings =>
{
settings.AssociatedControlName = property.PropertyName;
settings.Text = property.DisplayName;
settings.ToolTip = property.Description;
}).GetHtml()
<span class="editor-field">
#Html.DevExpress().TextBox(settings =>
{
settings.Name = property.PropertyName;
settings.Properties.NullText = property.Watermark;
settings.Width = 200;
settings.Properties.ValidationSettings.RequiredField.IsRequired = property.IsRequired;
settings.ShowModelErrors = true;
}).Bind(ViewData[property.PropertyName]).GetHtml()
</span>
</div>
}
</div>
then the form is laid out very nicely based on the metadata, with labels, tooltips, watermarks etc all pulled out of the model's metadata; but, I would like to be able to group the items together, for instance in a <fieldset> per group. Does anyone know how to get the GroupName out of the metadata, short of writing an extension method for ModelMetadata?
GroupName is not parsed by the DataAnnotationsModelMetadataProvider. So there's no way to get it right off the ModelMetadata object, even with an extension method.
You could implement your own provider that extends the existing one to add support for GroupName, which Brad Wilson explains in his blog.
You could also write your own attribute instead of using Display(GroupName = ) and implement the IMetadataAware interface to add the groupname to ModelMetadata.AdditionalValues.
You can also use this Extension Method:
public static class ModelMetadataExtensions
{
public static T GetPropertyAttribute<T>(this ModelMetadata instance)
where T : Attribute
{
var result = instance.ContainerType
.GetProperty(instance.PropertyName)
.GetCustomAttributes(typeof(T), false)
.Select(a => a as T)
.FirstOrDefault(a => a != null);
return result;
}
}
Then
var display= this.ViewData.ModelMetadata
.GetPropertyAttribute<DisplayAttribute>();
var groupName = display.Groupname;
An alternative could be to use the ShortName property of the DisplayAttribute class. It is exposed by the ModelMetadata class as the ShortDisplayName property.
This isn't exactly what you're looking for, but it will allow you to avoid creating another attribute class...and on top of that, you can take advantage of the localization ability of the DisplayAttribute.
Given the follow data class,
public class EmployeeMenu
{
public int ID { get; set; }
public string HeaderName { get; set; }
public List<string> ItemNames { get; set; }
}
how can I get a sub-query into the ItemNames field?
My current query of
IQueryable<EmployeeMenu> retValue =
from mh in menuHeaders
select new EmployeeMenu
{
ID = mh.ID,
HeaderName = mh.HeaderName,
ItemNames = (from mhi in mh.MenuItems
select mhi.MenuItemName).ToList<string>()
};
doesn't seem to be doing the trick...
The data structure is
MenuHeaders MenuItems
----------- ---------
ID ID
HeaderName <-(FK)--MenuHeaderID
MenuItemName
I ended up just changing from a List to IEnumerable. This fixed it.
Wouldnt you want to just put a where in your sub-select to filter that down to all the menu items with the MenuHeaderID equals mh.HeaderName. You can just .Equals() with the StringComparison type if you want as well.
Here is an example...
IQueryable<EmployeeMenu> retValue =
from mh in menuHeaders
select new EmployeeMenu
{
ID = mh.ID,
HeaderName = mh.HeaderName,
ItemNames = (from mhi in mh.MenuItems
select mhi.MenuItemName where mhi.MenuHeaderID = mh.HeaderName).ToList<string>()
};
My guess is that your not initiliazing the list within your class. I basing this off the experience I was having with Nhibernate.
public class EmployeeMenu
{
public int ID { get; set; }
public string HeaderName { get; set; }
public List<string> ItemNames { get; set; }
public EmployeeMenu()
{
ItemNames=new List<string>();
}
}
Hope this helps.
Okay. Try replacing
(from mhi in mh.MenuItems
select mhi.MenuItemName).ToList<string>()
by
mh.MenuItems
.AsEnumerable()
.Select(mhi => mhi.MenuItemName)
.ToList()
I question if you want a where clause in there somewhere, but this should get you past the runtime exception.
Any time you see an error message of the form "LINQ to Entities does recognize the method ... and this method can not be translated into a store expression" LINQ to Entities is telling you that it can't figure out how to translate part of the expression tree into a SQL statement. This means you need to pull things client side so that LINQ to Entities doesn't try to translate something that it can't translate.