MVC Controller w/ two arguments of same ViewModel type does not bind correctly - ajax

I have the following controller action:
public JsonResult AcceptChanges(RoomPricing currentData, RoomPricing lastSaveData)
{
...
}
This controller receives JSON encoded objects from my AJAX request:
$.ajax(
{
type: "POST",
url: _controllerURL,
data: JSON.stringify({ currentData: _currentData, lastSaveData: _lastSaveData }),
...
});
The AJAX request data is derived from a form in my partial view, which uses a ViewModel of type RoomPricing. Current and lastSave data are respectively the updated and stale serialized versions of the form. The purpose of these objects is to do form update checks server-side. Server-side because the RoomPricing model includes an enumerable and other junk that would make reliable update checking complex and obnoxious to perform client-side.
Now, when the controller receives the currentData and lastSaveData, it automagically creates the RoomPricing objects, BUT both objects are identical, taking on the values of currentData. To be clear: lastSaveData is created, but only in name, as its contents are identical to that of currentData (and so ignoring the data that was passed by AJAX, which I assume goes into the void).
I expect that this behavior is a side effect of MVC trying to be helpful with Model Binding. Does anyone have a suggestion for getting around this problem? Making a super-ViewModel with two RoomPricing objects to use as the controller argument did not resolve this issue.
Thanks in advance!
Edit: The current and lastSave data comes from the following JQuery code:
var $lastSaveFormData = null;
function AjaxSubmitForm() {
var $lastSaveSerialized = ($lastSaveFormData == null ? null : $lastSaveFormData)
var $form = $('#MyForm');
submitOverrideForm(
$form.serialize(), // currentData
$lastSaveSerialized, // lastSaveData
$form.attr('action'),
);
$lastSaveFormData = $form.serialize();
}
Through the above I'm able to keep a record of all changes since the last save. Although the models are somewhat complex, the size of the data being sent is quite small so I figured I'd compare the data server-side, and here we are. Last thing of note is that I've verified that at this point - JSON.stringify({ currentData: _currentData, lastSaveData: _lastSaveData } - the encoded data is as expected...currentData and lastSaveData are unique if form updates have taken place.
Edit: I just noticed something else. Here is the data being sent to the server as well as the data received server-side just prior to being bound to the model:
_currentData, # AJAX call:
"Book%5B0%5D.Author=1&Book%5B0%5D.Title=100&Book%5B0%5D.IsPaperback=false&Book%5B1%5D.Author=2&Book%5B1%5D.Title=2222&Book%5B1%5D.IsPaperback=false"
_lastSaveData # AJAX call:
"Book%5B0%5D.Author=1&Book%5B0%5D.Title=100&Book%5B0%5D.IsPaperback=false&Book%5B1%5D.Author=2&Book%5B1%5D.Title=77&Book%5B1%5D.IsPaperback=false"
JSON received server-side, just before model binding:
{"currentData": "Book[0].Author: 1,1 Book[0].Title: 100,100
Book[0].IsPaperback: false,false Book[1].Author: 2,2 Book[1].Title:
2222,77 Book[1].IsPaperback: false,false", "lastSaveData":"false"}
Its like MVC is trying to bind the distinct values to a single model, realizes it cant, and drops the 'lastSave' data. Then, when the model hits the controller, it discovers that two models are anticipated, so it uses this model for both. Why is it behaving like this and how can I fix it?

You can add another ViewModel:
public class CurrentAndLastSavedRoomPricingViewModel
{
RoomPricing CurrentData {get;set;}
RoomPricing LastSaveData {get;set;}
}
and get it in controller:
public JsonResult AcceptChanges(CurrentAndLastSavedRoomPricingViewModel data)
{
...
}
passing this model with ajax:
$.ajax(
{
type: "POST",
url: _controllerURL,
data: {data: { currentData: _currentData, lastSaveData: _lastSaveData }},
...
});
Or you can get last saved data on server from database and don't pass it from client.

Related

Updating Backbone Model Attributes Dynamically

I have a model that is part of a collection and retrieves data from the api. This model contains (among other attributes) the following attributes:
updatedDate //-> value retreived from API/DB
lastUpdateAttempt //-> value retrieved from API/DB
status //-> value NOT retrieved from API/DB, depends on values of above two attributes ("updated", "error", "out of date", etc...).
My question is, how can/when should I set the status attribute? Is there a way where I can dynamically set status when trying to retrieve the value? (i.e. modelObj.get("status") -> calls function to calculate value, returns result). Or should I call a function from the view to update this attribute on initialize, then add an event listener that does the same on change? (<-- somehow doesn't seem like the best solution)
I have a feeling I'm overthinking this and there is a really practical way of doing this, but I'm still a bit inexperienced with Backbone.
Thanks.
You can set the initial value of status, and listen for further changes to updatedDate or lastUpdateAttempt when the model is initialized.
Something like
Backbone.Model.extend({
initialize: function(){
this.updateStatus();
this.on("change:updatedDate change:lastUpdateAttempt",this.updateStatus);
},
updateStatus: function(){
// your logic
}
});
Or you can try this weird way (not at all tested, just a thought), which is somewhat like updating the status while accessing it. Might help if you want to control the frequency of status updates. (if you are likely to access the status way less than the number of changes that is likely to occur with the other two properties)
Backbone.Model.extend({
initialize: function(){
this.updateStatus();
this.on("updateStatus",this.updateStatus);
},
updateStatus: function(){
// your logic
}
});
and access the status like, model.trigger('updateStatus').get('status')

Passing multiple values from Ajax to MVC Controller then build a dynamic query string using ExecuteQuery to get Json result

So A little bit background here:
I have built about 9 cascaded dropdowns/listboxs in the webpage and user can select depending on what they like from step-1 to the step-final. After that, they click submit button and I have built a collector to collect all of what they have selected and use Ajax to pass those parameters(and arrays, some of those are multi-selection)to MVC Controller.
The number of data back into the database is gigantic, if I don't set up any restrictions to get all of data in SQL database, that's going to be more than 4 millions of records in more than 15mins. So I can't use any typical approaches like I set up repositories in model and retrieve data in controller using linq, this is the way how I built those dropdowns and listboxs. But in these situation, this method will throw OutOfMemoryExecption and Json will throw maxJsonLength error to me and based on the time effecive, I can't take any single process more than seconds.
One thought about this solution is that, I get data only from what exactly user selected. Actually this is also how it works in sql database. Because no one wants to get everything in this database, they just want what exact field they want to see in this data view. If there are some solutions that I can directly talk from controller to sql database. I can put those selections in a query and pass it to sql database, then I return result only depends on what this query says. That will be very efficient.
Code:
So I wrote Ajax to pass those vars to controller:
$("#submit").click(function(){
$.ajax({
cache: false,
type: "GET",
url: "url/url",
data: {
"DateFrom": datefrom,
"DateTo": dateto,
"dropdownselectioninstep1": step1,
"dropdownselectioninstep2": step2,
"multiselectionstep3": intarraystep3,
"multiselectionstep4": stringarraystep4,
//....step 5,6,7,8,9,etc...
},
success: function (data) {...}
});
});
New Update - So I took these suggestions below and did some research online, I came up with some ideas that can deal with this giant database view. So the idea is writing a query that directly talks to SQL in controller so that I can get very detailed data using those values that I collected from Ajax, but I came across with some problems because of my lack of C# knowledge... Here's my controller:
public ActionResult GetAllCases(string DateFrom, string DateTo, string step1, string step2, int[] intarraystep3, string [] stringarraystep4, ...)
{
// So I worte a long query to match the dynamic needs from those dropdown/listbox
//selections and if it can be passed through database directly,
//I can return very specific data set instead of waiting SQL to
// process the full millions of records in database.
var query = "SELECT" + step1 + "," + step2 + "FROM view_cases as w WHERE " + step2 + " IN(" + intarraystep3 + ") AND" w.date "BETWEEN '" + DateFrom + "' AND '" + DateTo + "'";
var result = _dataContext.ExecuteQuery<// what things do I need to put here?>(#query).ToList();
return Json(result, JsonRequestBehavior.AllowGet);
}
I realized when I put a break point in this class, I did see this query passed to SQL, and successfully returned the correct amount of data set in Locals window. But If I put my view view_cases in var result = _dataContext.ExecuteQuery<view_cases>(#query).ToList(); It returns all of the columns in this view but not returning the columns that I put in the SELECT clause in query. So I guess I must missed some steps or should I create a model class or something like that?
If you have any question about what I'm trying to explain or any ideas about this question, drop a line to me. Any comment will be highly appreciated!
Thank you
Kevin.
you can create a queryObject and generate the sql query base on the queryObject
$.ajax({
cache: false,
type: "GET",
url: "url/url",
data: {
criteria: {
"DateFrom": datefrom,
"DateTo": dateto,
"dropdownselectioninstep1": step1,
"dropdownselectioninstep2": step2,
"multiselectionstep3": intarraystep3,
"multiselectionstep4": stringarraystep4,
//....step 5,6,7,8,9,etc...
}
},
success: function (data) {...}
});
public ActionResult GetAllCases(QueryObject criteria)
{
// do not do sql query from your controller, your controller should be
// as thin as possible, use a QueryService to encapsulate the query logic
queryService.queryAllCase(criteria);
}
public class QueryObject {
public DateTime DateFrom { get; set; }
...
}
if the data is gigantic, conside to paginate your result.

ASP.NET Web API: convert Object Literal to CLR class

My Web API controller needs to take in a property bag, i.e. a list of key-value pairs, which it will use to insert or update records of indeterminate type. My ideal code would look something like this. This example would be to insert a new record.
Route:
/api/data/{EntityName}
Data from client, passed in using jQuery.ajax() (or similar) with POST:
URI:
http://mysite/api/data/employee
Post data:
"values":
{"FirstName":"Jim",
"LasName":"Nobody",
"StartDate": new Date(),
"Salary": 100000 }
Web API Controller function:
public HttpResponseMessage PutRecord(string entity, ??? values)
{
// confirm valid entity, look up metadata.
// Take values from "values" and feed them into my framework to insert a new employee.
}
What is the ideal configuration to accomplish this? What would be the type for "values" in the controller method - possibly a Dictionary< string,object>?
Thanks very much for your help.
If you create a model that has all of the properties, the model binder will map your post data directly to the model.
So your post would look more like this:
$.post(url, {FirstName:'Jim', LasName: 'NoBody',StartDate: someDate, Salary: 10000},
function(data){
// do something with callback
});
And your action would look like:
public HttpResponseMessage PutRecord(string entity, employeeModel model)
{
// confirm valid entity, look up metadata.
// Take values from "values" and feed them into my framework to insert a new employee.
}
This is a tad more type safe than the property bag approach, and save you a lot of casting and typing issues.

Using IDictionary in controller method ASP.NET MVC 4/JSON

I'm attempting to post data using AJAX back to an ASP.NET MVC Controller with a viewModel like so:
public class FunkyThingUpdateModel
{
public int FunkyThing_ID{get;set;}
public string Name{get;set;}
public IDictionary SizesAvailable{ get; set;}
}
Unfortunately, I'm struggling to get the model binder to bind to the IDicationary.
If in the JSON passed to the controller the sizesAvailable is null then the controller method is called successfully. However if there is data in sizesAvailable then I'm getting an internal server error 500.
My JSON is formatted as follows:
{"FunkyThing_ID":1,"Name":"Pogo Stick","SizesAvailable":{"1":"Extra
Large","2":"Extra Tiddly"}}
What I'm not sure is - am I trying to do something impossible. Is there a way of binding to an IDictionary - if not what is the best alternative?
Or does my JSON just need to be formatted in a different fashion?
I found a number of similar questions on here, but most of them are quite old.
There seemed to be some indications that this was possible in MVC 4 which I'm using - but I could have been misunderstanding the context.
Not tested, But i think you have to specify Generic interface definition for the object to be created.
public IDictionary<int, string> SizesAvailable{ get; set;}
and also you have to change the data in ajax definition to notify SizesAvailable as array
$.ajax({
url:'your url',
type: 'post',
contentType : 'application/json',
data: JSON.stringify({FunkyThing_ID:1, Name:"Pogo Stick", SizesAvailable:[{key: 1, value:"Extra Large"},{key:2, value: "Extra Tiddly"}]}),
success: function(response){
console.log(response);
}
});
Edit
If your intention is to only get string array(not a dictionary) of sizes, change
public string[] SizesAvailable{ get; set;}
and send data as,
data: JSON.stringify({FunkyThing_ID:1, Name:"Pogo Stick", SizesAvailable:["Extra Large","Extra Tiddly"]}),
hope this helps.

Associated model not updated from nested JSON on update through proxy?

In my Ext JS 4.1 application, I have a model Project with an belongsTo association to ProjectCategory:
Ext.define('MyApp.model.Project', {
extend: 'Ext.data.Model',
fields: ['id', 'project_category_id', 'title'],
belongsTo: {
model: 'MyApp.model.ProjectCategory',
associationKey: 'ProjectCategory',
getterName: 'getCategory'
}
// ...
});
Ext.define('MyApp.model.ProjectCategory', {
extend: 'Ext.data.Model',
fields: ['id', 'title']
});
The project is read via a Direct proxy, and ProjectCategory details are included as nested values in the response (mostly for display purposes). When loading a store, the associated data is read correctly, and I'm able to read the ProjectCategory's title in a grid via a custom renderer:
renderer: function(v, p, rec) {
return v ? rec.getCategory().get('title') : '';
}
However, when editing and saving the parent Project record through form.updateRecord(record), the associated record's fields aren't updated with values from the server response, unlike the Project's "native" fields. So when changing the project_category_id, even though the server will respond with a correctly nested ProjectCategory field containing the new category, getCategory will still return the old category.
Why isn't this updated from the server response, and what do I have to do to update it?
I already found out that in the store's write listener, I have access to the record and the returned association data. But I can't figure out how to update the record silently without triggering any other events.
It seems like I found my solution. Add this to the Project store:
listeners: {
write: function(store, operation, opt) {
var c = operation.records[0].getCategory();
c.set(operation.response.result.ProjectCategory);
c.commit(true);
}
}
The key element is the commit(true) call, which will skip notifying the store/proxy about the changes.
It's still a bummer that this isn't done automatically, though.

Resources