When using Json.NET to serialize a MVC view model to json, I have a generic object property on my view model (public object Result { get; set;}) that is getting serialized to key-value pairs instead of an actual json object. Is there a converter I can use to force it to be serialized properly?
This is what is currently being output by Json.NET:
"result": [
{
"key": "AssetId",
"value": "b8d8fb71-2553-485b-91bf-14c6c563d78b"
},
{
"key": "SearchResultType",
"value": "Assets"
},
{
"key": "Name",
"value": "abstract-q-c-1920-1920-8"
}
]
Instead, I would want it to output this:
"result": {
"AssetId": "b8d8fb71-2553-485b-91bf-14c6c563d78b",
"SearchResultType": "Assets",
"Name": "abstract-q-c-1920-1920-8"
}
EDIT:
To answer the question of how that property is getting populated, it is via a RavenDB index:
public class SiteSearchIndexTask : AbstractMultiMapIndexCreationTask<SiteSearchResult>
{
public class Result
{
public object[] Content { get; set; }
}
public override string IndexName
{
get
{
return "SiteSearch/All";
}
}
public SiteSearchIndexTask()
{
AddMap<Asset>(items => from item in items
where item.IsDeleted == false
select new
{
Id = item.Id.ToString(),
ObjectId = item.Id,
ResultType = SearchResultType.Assets,
Title = item.Name.Boost(3),
Tags = item.Tags.Select(x => x.Name).Boost(2),
Result = (object)item,
Query = string.Join(" ", item.Description)
});
AddMap<User>(items => from item in items
where item.IsDeleted == false
select new
{
Id = item.Id,
ObjectId = item.UserId,
ResultType = SearchResultType.Users,
Title = item.Username.Boost(3),
Tags = (BoostedValue) null,
Result = (object)item,
Query = string.Join(" ", item.FullName, item.Email)
});
Store(x => x.ObjectId, FieldStorage.Yes);
Store(x => x.ResultType, FieldStorage.Yes);
Store(x => x.Title, FieldStorage.Yes);
Store(x => x.Tags, FieldStorage.Yes);
Store(x => x.Result, FieldStorage.Yes);
Store(x => x.Query, FieldStorage.Yes);
}
}
Edit 2
Here are the Asset and User models (truncated for brevity, since they're just a bunch of auto properties)
public class Asset : IHasId
{
public string Id { get; set; }
public Guid AssetId
{
get
{
Guid assetId;
Guid.TryParse((Id ?? string.Empty).Replace("assets/", ""), out assetId);
return assetId;
}
set { Id = "assets/" + value; }
}
public string Name { get; set; }
public string Description { get; set; }
}
public class User : IHasId
{
public User()
{
Status = UserStatus.Active;
}
public string Id { get; set; }
public int UserId
{
get
{
int userId;
int.TryParse((Id ?? string.Empty).Replace("users/", ""), out userId);
return userId;
}
set { Id = "users/" + value; }
}
public string Username { get; set; }
public string Password { get; set; }
public string Email { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public UserStatus Status { get; set; }
}
Edit 3
As it turns out, when I step through the code, that object is actually of the type Raven.Abstractions.Linq.DynamicJsonObject, which contains an array of key-value pairs. So I guess this question might be more related to Raven than Json.NET. Unless of course there is a converter to go from key-value pair to json object.
It would help to see how you are querying exactly, but you should take a look at the example on querying unlike documents with a multimap index on the RavenDB site.
When I look at your index, it seems like you are trying to do way too much in terms of field storage and encapsulation of your results. It's sometimes difficult to grasp, but the mapping done in the index is not to define how the results are returned, but rather how the index is built. Unless you are doing a Reduce or TransformResults step, you are still returning the original document.
So encapsulating the document as Result = (object)item is overkill. So is having a ResultType enum. If you need to know the type of document that matched, simply use .GetType() on the object and you will quickly see if it is a User or an Asset.
Here is how I would define your search index. Note there's some differences because there were properties you showed in your index that aren't in the models you provided. (I'm assuming there is a separate entity on the backend than the models on the front end, but adjust as needed.)
public class SearchIndex : AbstractMultiMapIndexCreationTask<SearchIndex.Result>
{
public class Result
{
public object[] Content { get; set; }
public string ResultType { get; set; }
}
public SearchIndex()
{
AddMap<Asset>(items => from item in items
select new Result
{
Content = new object[] {item.Name, item.Description},
ResultType = MetadataFor(item)["Raven-Entity-Name"].ToString()
});
AddMap<User>(items => from item in items
select new Result
{
Content = new object[] { item.Username, item.FirstName, item.LastName, item.Email },
ResultType = MetadataFor(item)["Raven-Entity-Name"].ToString()
});
Index(x => x.Content, FieldIndexing.Analyzed);
}
}
And then I would query it like so:
var results = session.Advanced
.LuceneQuery<object, SearchIndex>()
.Where("ResultType:" + resultTypeName)
.AndAlso()
.Search("Content", searchTerm);
You can then simply examine your results and find that while cast as object, you can indeed do result.GetType() and see how that object is constructed. If desired, you could also put a common interface on your entities to cast to instead of object, such as the IAmSearchable shown in the Raven example.
When you finally pass your result back through MVC, it should be serialized properly, since it will be coming from the real object and not the raven DynamicJsonObject.
Related
CommentCollection
{
"_id":"5b63f0f23846b70011330889",
"CommentType":"task",
"EntityReferenceId":"6082ef25-6f9a-4874-a832-f72e0f693409",
"CommentLink":null,
"EntityName":"task2",
"participants":[
ObjectId("52ffc4a5d85242602e000000"),
ObjectId("52ffc4a5d85242602e000001")
],
"Threads":[
{
"_id":"69bcef71-3695-4340-bdec-4a6e4c58c490",
"CommentType":"task",
"UserId":ObjectId("52ffc4a5d85242602e000000"),
"CommentByUserType":"Admin",
"EntityReferenceId":"6082ef25-6f9a-4874-a832-f72e0f693409",
"Content":"fdffd",
"ProjectName":null,
"PostedDate":"2018-08-03T13:03:05.939Z",
"Active":true,
"Attachment":[
]
}
another Collection is
userCollection
{
"Id":ObjectId("52ffc4a5d85242602e000000"),
"Name":"Pms Admin",
"Email":"pms#xtrastaff.com",
"Type":"Admin",
"UserId":"6082ef25-6f9a-4874-a832-f72e0f693409",
"UserImage":"6082ef25-6f9a-4874-a832-f72e0f693409"
}
In the CommentCollection there is an array of "participants" which is storing the id's of users (from usercollection).
My requirement is join these two collections for getting user details in my asp.net core project(Linq).Participants contains list of id's
In Mongo shell you would use $lookup which can be used on arrays like in this example and your query could look like this:
db.Comment.aggregate([
{
$lookup: {
from: "user",
localField: "participants",
foreignField: "Id",
as: "participants"
}
}
])
Which simply replaces participants with array of objects from second collection:
{
"_id" : "5b63f0f23846b70011330889",
"CommentType" : "task",
"EntityReferenceId" : "6082ef25-6f9a-4874-a832-f72e0f693409",
"CommentLink" : null,
"EntityName" : "task2",
"participants" : [
{
"_id" : ObjectId("5b6e875b9d52833fbe9879c2"),
"Id" : ObjectId("52ffc4a5d85242602e000000"),
"Name" : "Pms Admin",
"Email" : "pms#xtrastaff.com",
"Type" : "Admin",
"UserId" : "6082ef25-6f9a-4874-a832-f72e0f693409",
"UserImage" : "6082ef25-6f9a-4874-a832-f72e0f693409"
}
],
"Threads" : //...
}
In C# you can express that using Lookup syntax. First option allows you to get a list of BsonDocument type which simply skips type checking:
var collection = db.GetCollection<Comment>("Comment");
List<BsonDocument> result = collection.Aggregate()
.Lookup("user", "participants", "Id", "participants")
.ToList();
The reason why you can't use regular LINQ join here is that actually you're compaing an array with a scalar value (that's what should be in equals part of join). However if you need strongly typed result instead of BsonDocuments you can use different version of Lookup method which takes types and expressions instead of strings. So you need another class for $lookup result which might be solved using inheritance:
public class Comment
{
public string _id { get; set; }
public string CommentType { get; set; }
public string EntityReferenceId { get; set; }
public string CommentLink { get; set; }
public string EntityName { get; set; }
public ObjectId[] participants { get; set; }
public Thread[] Threads { get; set; }
}
public class CommentWithUsers : Comment
{
public User[] Users { get; set; }
}
Then you can get a list of CommentWithUser:
var comments = mydb.GetCollection<Comment>("Comment");
var users = mydb.GetCollection<User>("user");
List<CommentWithUser> result = comments.Aggregate()
.Lookup<Comment, User, CommentWithUsers>(
users,
x => x.participants,
x => x.Id,
x => x.Users).ToList();
Hi i have a C# model class. This class i need to pass as json response by setting his property . one property name Product of this class have type of another product class, when there is no data for Product i am getting all inner property value as blank but instead of that i want blank json property .
For Exp My Class is
public class Profile_BO
{
public int Id { get; set; }
public string Username { get; set; }
public Product product{ get; set; }
public class Product
{
public int Id { get; set; }
public string Type { get; set; }
}
}
i am initializing this class from C# data table like below : -
Profile_BO profile_BO = new Profile_BO();
foreach (DataRow dr in result.Tables[0].Rows)
{
profile_BO.Id = Convert.ToInt32(dr[0]);
profile_BO.Username = Convert.ToString(dr[1]);
}
Product product = new Product();
foreach (DataRow dr1 in result.Tables[1].Rows)
{
product.Id = Convert.ToInt32(dr1[0]);
product.Type = Convert.ToString(dr1[1]);
}
profile_BO.product = product;
Finally when i am passing as a response to method : -
public async Task<HttpResponseMessage> GetUserInfo(Profile_Request profile_Request)
{
return request.CreateResponse(HttpStatusCode.OK, profile_BO);
}
And when calling on client side i am getting response json if data is present in table like : -
{
"Id": "1",
"Username": "abc",
"product": {
"Id": "232",
"Type": "34cvdcbv"
}
}
But when i have no data in product table i am getting below : -
{
"Id": "1",
"Username": "abc",
"product": {
"Id": 0,
"Type": ""
}
}
But if no data i want output like below : -
{
"Id": "1",
"Username": "abc",
"product": {}
}
One other question is : - Is it right way for binding response model from dataset ?
the problem you are facing, is that you are initializing an instance of Product regardless of the fact, that there might be no product at all. this results, to its properties getting initialized with default values. Int32 defaults to 0. System.String as a reference type is null.
Profile_BO profile_BO = new Profile_BO();
foreach (DataRow dr in result.Tables[0].Rows)
{
profile_BO.Id = Convert.ToInt32(dr[0]);
profile_BO.Username = Convert.ToString(dr[1]);
}
//I am assuming you only expect one row, since oyur model uses a single Product
//and no collection of products. No need for a loop then.
if(result.Tables[1].Rows.Count == 1) {
Product product = new Product();
var dr1 = result.Tables[1].Rows[0];
product.Id = Convert.ToInt32(dr1[0]);
product.Type = Convert.ToString(dr1[1]);
profile_BO.product = product;
}
This should result in the following JSON being returned:
{
"Id": "1",
"Username": "abc",
"product": null
}
EDIT: If you really must have product : {}, then you need ot change your model.
public class Profile_BO
{
public int Id { get; set; }
public string Username { get; set; }
public object product { get; set; }
}
public class Product
{
public int Id { get; set; }
public string Name { get; set; }
}
Declare product as an object. Since all classes inherit from object, you can instantiate it as an object or Product, depending on your case:
if(result.Tables[1].Rows.Count == 1) {
Product product = new Product();
var dr1 = result.Tables[1].Rows[0];
product.Id = Convert.ToInt32(dr1[0]);
product.Type = Convert.ToString(dr1[1]);
profile_BO.product = product;
}
Or:
if(result.Tables[1].Rows.Count == 0) {
var product = new object();
profile_BO.product = product;
}
This will then result in:
{"Id":1,"Username":"Foo Bar","product":{}}
However I strongly advise to go with the first approach, because this will make testing and modifications easier, since you keep your strongly typed approach.
My previous answer doesn't contribute to your final question. So here is my edited solution.
A Better Solution
May be better solution is using Custom Message Handler.
A delegating handler can also skip the inner handler and directly
create the response.
Custom Message Handler:
public class NullJsonHandler : DelegatingHandler
{
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
var updatedResponse = new HttpResponseMessage(HttpStatusCode.OK)
{
Content = null
};
var response = await base.SendAsync(request, cancellationToken);
if (response.Content == null)
{
response.Content = new StringContent("{}");
}
else if (response.Content is ObjectContent)
{
var contents = await response.Content.ReadAsStringAsync();
if (contents.Contains("null"))
{
contents = contents.Replace("null", "{}");
}
updatedResponse.Content = new StringContent(contents,Encoding.UTF8,"application/json");
}
var tsc = new TaskCompletionSource<HttpResponseMessage>();
tsc.SetResult(updatedResponse);
return await tsc.Task;
}
}
Register the Handler:
In Global.asax file inside Application_Start() method register your Handler by adding below code.
GlobalConfiguration.Configuration.MessageHandlers.Add(new NullJsonHandler());
Now all the Asp.NET Web API Response which contains null will be replaced with empty Json body {}.
References:
1.
2.
I'm trying to allow my entities to be ordered by the Last-Modified property in their metadata, using OData query options.
I tried using a transformer as described in Converting to JSON and accessing metadata, but when I apply ODataQueryOptions to the resulting IQueryable, I get an empty array.
The model and view-model:
public class Foo
{
public int Id { get; set; }
}
public class FooViewModel
{
public string Id { get; set; }
public DateTime LastModified { get; set; }
}
The transformer:
public class Foos_WithLastModified : AbstractTransformerCreationTask<Foo>
{
public Foos_WithLastModified()
{
TransformResults = foos => from foo in foos
let metadata = MetadataFor(foo)
select new
{
Id = foo.Id.ToString(CultureInfo.InvariantCulture),
LastModified = metadata.Value<DateTime>("Last-Modified")
};
}
}
The relevant method in FooController (_session is an IAsyncDocumentSession):
public async Task<ICollection<FooViewModel>> Get(ODataQueryOptions<FooViewModel> options)
{
var settings = new ODataValidationSettings();
settings.AllowedOrderByProperties.Add("LastModified");
options.Validate(settings);
var foos = _session.Query<Foo>()
.TransformWith<Foos_WithLastModified, FooViewModel>();
var odataFoos = (IQueryable<FooViewModel>)options.ApplyTo(foos);
return await odataFoos.ToListAsync();
}
When I hit /api/Foo, the results are as expected:
[
{
"Id": "foos/456",
"LastModified": "2015-11-23T08:43:10.913662Z"
},
{
"Id": "foos/123",
"LastModified": "2015-11-23T08:50:34.0907996Z"
}
]
But when I add OData query options (/api/Foo?$orderby=LastModified), I get an empty array: [].
I also tried changing _session to an IDocumentSession and modifying Get as follows,
[EnableQuery(AllowedOrderByProperties = "LastModified")]
public IQueryable<FooViewModel> Get()
{
return _session.Query<Foo>()
.TransformWith<Foos_WithLastModified, FooViewModel>();
}
but I get the same results.
Are transformers the wrong approach? How can I sort by Last-Modified using OData query options?
I do not know how to handle the OData stuff, never tried that, but in order to query for entities, ordered by the metadata value "Last-Modified" using only RavenDB techniques you can do the following:
Create an index for your entity (in my example a Customer). In this index we add the field LastModified that's using the document's metadata value for Last-Modified.
public class Customer_ByLastModified : AbstractIndexCreationTask<Customer>
{
public class QueryModel
{
public DateTime LastModified { get; set; }
}
public Customer_ByLastModified()
{
Map = customers => from customer in customers
select new
{
LastModified = this.MetadataFor(customer).Value<DateTime>("Last-Modified")
};
}
}
The QueryModel isn't mandatory, but it makes querying via the client API easier, imo. You can then add a Transformer to be able to use the metadata value in your return model:
public class Customers_WithLastModified : AbstractTransformerCreationTask<Customer>
{
public Customers_WithLastModified()
{
TransformResults = results => from customer in results
select new CustomerViewModel
{
Id = customer.Id,
Name = customer.Name,
LastModified = MetadataFor(customer).Value<DateTime>("Last-Modified")
};
}
}
And then query it like this:
using (var session = documentStore.OpenSession())
{
var customers = session.Query<Customer_ByLastModified.QueryModel, Customer_ByLastModified>()
.OrderByDescending(x => x.LastModified)
.TransformWith<Customers_WithLastModified, CustomerViewModel>()
.ToList();
}
Hope this helps!
For web application in development(ASP.Net MVC), I'm using the telerik grid. The grid is bound to an IQueryable of my list, because it's a big table, and I want that telerik apply it's filter on the list, and then executes this result, not dowloading 10'000 rows(with the joined tables), and then with the filter, use only rows.
I'm using(and I really need it for this page, it's one of the key feature) the filter/order of the grid.
One of the main column(determining the kind of the data) is an enum.
The problem is that I get a "Specified type member is not supported in linq to entities" as soon as I'm trying to filter/sort it.
I've to bind it on the enum(and not the mapped int) because if I use the id, filters/order by will be on an int, and I can't expect that the user knows the id of the foreign table.
I just cannot implement myself again all grids parameter(located in url)(I assume, it's either I do everything, or nothing) and filter it correctly, order it correctly).
Do you have an idea of workaround?
I don't know how your Entity Model looks like but I'll suppose that you've something like this Model:
public partial class Project
{
public int Id { get; set; }
public string Name { get; set; }
public int Status { get; set; }
}
and the Status property represents your enum value then you've this enum:
public enum ProjectStatuses
{
Current = 1,
Started = 2,
Stopped = 3,
Finished = 4,
}
Then just create new ViewModel like this :
public class ProjectDetails
{
public int Id { get; set; }
public string Name { get; set; }
public int Status { get; set; }
public ProjectStatuses StatusValue { get { return (ProjectStatuses) Status; } }
// This property to display in telerik ClientTemplate
public string StatusName { get { return Enum.GetName(typeof (ProjectStatuses), Status ); } }
}
And because I love Extension Methods I'll add this one :
public static class ModelListExtensions
{
public static IQueryable<ProjectDetails> ToViewModelDetails(this IQueryable<Project> modelList)
{
return modelList.Select(m => new ProjectDetails
{
Id = m.Id,
Name = m.Name,
Status = m.Status,
};
}
}
Update :
Here is the Controller
public ActionResult Index()
{
int total;
var viewModel = getGridList(out total);
ViewBag.Total = total;
return View(viewModel);
}
//this Action to get ajax pages
[GridAction(EnableCustomBinding = true)]
public ActionResult ReGetIndex(GridCommand command, int roleId)
{
int total;
var list = getGridList(out total, roleId, command);
return View(new GridModel {Data = list, Total = total});
}
private IEnumerable<ProjectDetails> getGridList(out int total, GridCommand command = null)
{
command = command ?? new GridCommand {Page = 1};
foreach (var descriptor in command.SortDescriptors)
{
if (descriptor.Member == "StatusValue")
descriptor.Member = "Status";
}
foreach (FilterDescriptor descriptor in command.FilterDescriptors)
{
if (descriptor.Member == "StatusValue")
descriptor.Member = "Status";
}
var list = modelService.AllAsQuery()
.ToViewModelDetails() // To convert it to our ViewModel if we have one
.Where(command.FilterDescriptors);
total = list.Count();
return (IEnumerable<ProjectDetails>) list.Sort(command.SortDescriptors)
.Page(command.Page - 1, command.PageSize)
.GroupBy(command.GroupDescriptors).ToIList();
}
And this is the View
#model IEnumerable<ProjectDetails>
#{
Html.Telerik()
.Grid(Model)
.Name("ProjectsGrid")
.Sortable()
.Filterable()
.EnableCustomBinding(true)
.DataBinding(dataBinding => dataBinding
.Ajax()
.Select("ReGetIndex", "Projects"))
.Pageable(page => page.Style(GridPagerStyles.PageSizeDropDown | GridPagerStyles.NextPreviousAndNumeric).Total(ViewBag.Total))
.Columns(column =>
{
column.Bound(m => m.Id).Hidden(true);
column.Bound(m => m.Name);
column.Bound(m => m.StatusValue).ClientTemplate("<#= StatusName #>");
})
.Render();
}
Update :
If you want to enforce at least one sort order you could use something like this:
if (!command.SortDescriptors.Any())
{
command.SortDescriptors.Add(new SortDescriptor {Member = "YourDefaultProperty"});
}
You don't really have choice (or few annoying choices)
Wether you use a class instead of enum (but if you used an enum, that's because it was better).
Or you "pseudo-sort" your enum, and use the mapped int.
public enum TT
{
Brown = 0,
Green = 1
}
Of course, you'll have to check the actual datas (mapped int) in your DB and update them to conform to the new order (can't change enum order without impact). And you'll have to do that everytime you want to insert a value between existing enum values.
Or you wait for next EF / linq / c# version, which should have enum support in linq2entities
I'm using jqGrid to display some data on a page. Within the controller action, we're using an anonymous object to represent the data that the jqGrid needs. My question is, is there a way we can create a strongly typed object to represent the jqGrid data that we are sending with Json()?
Main reason for this is so that we can do unit testing with the objects that are being sent to it.
Thanks!
EDIT:
[AcceptVerbs(HttpVerbs.Post)]
public JsonResult GridData(FormCollection form, string alias, string location, string state)
{
int pageSize = Convert.ToInt32(form["rows"]);
int pageIndex = Convert.ToInt32(form["page"]) - 1;
var deviceList = this._device.GetList(CreateFilter(location,alias,state),this._securityCache.GetSecurityContext(),pageSize,pageIndex);
int totalResults = deviceList.TotalRecords;
int totalPages = (int)Math.Ceiling((float)totalResults / (float)pageSize);
var jsonData = new {
total = totalPages,
page = pageIndex + 1,
records = totalResults,
rows = (from device in deviceList.Data
select new {i = device.Alias,cell = new string[]{device.Alias,device.Location,device.RatePlan,device.State,device.DateCreated.ToString()}}).ToArray()
};
return Json(jsonData);
This above here works, but we can't unit test the data that is being passed into the Json() method.
var newJsonData = new JsonJQGridReturnData();
newJsonData.total = totalPages;
newJsonData.page = pageIndex + 1;
newJsonData.records = totalResults;
List<JsonJQGridRow> list = new List<JsonJQGridRow>();
foreach (var device in deviceList.Data)
{
list.Add(new JsonJQGridRow(device.Alias, new string[] { device.Alias, device.Location, device.RatePlan, device.State, device.DateCreated.ToString() }));
}
newJsonData.rows = list.ToArray();
_cookieHelper.SaveCookie("DeviceListIndex", this._securityCache.GetSecurityContext().UserID.ToString(), COOKIE_PAGE_SIZE_KEY, pageSize.ToString());
return Json(newJsonData);
}
Here is my poor attempt at trying to wrap these into strongly typed objects. Unfortunately, running this gives me a "u is undefined" in the jqGrid file. I suspect that this is because the json being passed in is not correctly formatted. Here are the classes....
[DataContract]
public class JsonJQGridReturnData
{
[DataMember]
public int total { get; set; }
[DataMember]
public int page { get; set; }
[DataMember]
public int records { get; set; }
[DataMember]
public JsonJQGridRow[] rows { get; set; }
}
[DataContract]
public class JsonJQGridRow
{
public JsonJQGridRow(string i, string[] columns)
{
this.i = i;
this.cells = columns;
}
[DataMember]
public string i { get; set; }
[DataMember]
public string[] cells { get; set; }
}
If I understand your question you can use Generics to do this:
Model:
// represents one row in the JQGrid
class Customer
{
public string firstname { get; set; }
public string lastname { get; set; }
}
JQGrid class:
class JQGridData<TModel>
{
// add in whatever other properties you want for JQGrid
public int responseTime {get; set; };
public List<TModel> rows = new List<TModel>();
}
Controller Action :
public JsonResult GridData(int page)
{
var gridData = new JQGridData<Customer>();
// Populate your data here, this is just an example:
gridData.rows.Add(new Customer()
{
firstname = "fred", lastname = "pharkas"
});
// return the result
return Json(gridData, JsonRequestBehavior.AllowGet);
}
Result:
{
responseTime: 0
rows: [
{
firstname: "fred"
lastname: "pharkas"
}
]
}
Is that what you were asking?
David,
Here's the kinda thing i use in an app i'm working on at the moment for this type of thing. I know it doesn't provide a strongly typed object as such, but the 'list' could be a part of the model that is then sent ToArray() at the end of the piece.
public JsonResult GridData(int id)
{
// get our messages based on id
var bookingmessagesList = _repository.Find(x => x.ID == id);
var list = new ArrayList();
foreach (var bookingmessage in bookingmessagesList) //populate data containers with read data
{
list.Add(new
{
bookingmessage.ClassRowVersionDate,
bookingmessage.ID,
bookingmessage.BookingID,
bookingmessage.AssignedFrom,
bookingmessage.AssignedTo,
bookingmessage.AssignedDate,
bookingmessage.CompletedDate,
bookingmessage.MessageType,
bookingmessage.Notes
});
}
int totalOjectCount = list.Count;
return Json(new { dataitems = list.ToArray(), totalItems = totalOjectCount });
}
hope it gives you some ideas.. Will be interested to see the suggestions made.
Here's a quick take on a strongly-typed JQGridResult.
public class JQGridResult<T> : JsonResult where T : class
{
public T Model
{
get { return (T)this.Data; }
set { this.Data = value; }
}
}
Used as...
return new JQGridResult<JsonModel> {
Model = new GridModel { ... initialize model here ... }
});
where GridModel is basically a container class holding the strongly typed properties for the grid.
I feel really silly. I had a misspelling in the GridRow that was causing jqGrid to blow up. After I fixed that, I was able to get the jqGrid to work with my strongly typed object...
Now in my unit tests, I can just do...
var result = controllerToTest.GridData(form, null, null, null) as JsonResult;
var data = result.Data as JsonJQGridReturnData;
and now I can access the fields :D