MVC JSON data reference - c#

there is a call to my controller where something like this is performed:
someObject.Name = "Mike";
JsonResult result = Json(new { TheMan = someObject }, JsonRequestBehavior.AllowGet);
someObject.Name = "Paul";
return result;
The problem is that when the client receives the data, the name is "Paul" when I was expecting that the result JSON was created with "Mike".
In the docs it says "The result object that is prepared by this method is written to the response by the ASP.NET MVC framework when the object is executed."
Is there a workarround where I can manipulate the objects used on the JSON data without being worried about changing the response? (Clone the someObject or something)
Thanks.

You pretty much said it. When you create the Json object you are simply adding a reference to someObject so if you change the value it will be changed inside the Json object as well. The Json is not actually written to the response until the JsonResult is returned by the action. If you want to set the value to something else without affecting the original value you will need another copy of the object.

Related

Json object in post not recognized as object in web api

I have an angular client and want to execute a get request to my web api backend to get a list of items from the underlying Dapper Db Wrapper. Dapper allows me to pass in parameters as an anonymous object which would in csharp look like this:
connection.GetList<T>(new {myParam1:"a", myParam2: true});
What I want to achieve is, to create this parameter object in my angular frontend and pass it in a post request to the server which would then pass it on to the GetList function. The problem here is that the web api does not deserialize it as an (anonymous) object, but rather and IEnumerable of JTokens?
My web api signature is this:
public async Task<IHttpActionResult> MyFunction([FromBody]dynamic whereCond)
I have also tried to pass the object as string wrapped in an outer object like so (angular client):
this.migController.MigrationGetMigrationReports({whereCond: JSON.stringify({NotMigrated: true, MissingTargetFiles: 0})})
and then on the server I manually deserialize it as JObject:
string obj = whereCond.whereCond;
dynamic pObj = JObject.Parse(obj);
But this results in the exact same result: pObj is an IEnumerable and therefore I get an error message from the GetList call:
An enumerable sequence of parameters (arrays, lists, etc) is not allowed in this context
can anybody help?
The answer to my question turned out rather simple:
dynamic pObj = JObject.Parse(obj).ToObject<ExpandoObject>();
I had to cast it as ExpandoObject not just dynamic.
#Tsahi: this is not a design problem. My intention was to provide the server with parameters (filter) which is a quite common task for a client to reduce the dataset to be transferred. We could debate a standard way how to provide these parameters, however. In my special case the most practical way is the anonymous object.

How to assert that a API response has a specific structure?

I have a SpecFlow test definition set up where I want to assert that the response have a specific collection of fields in its response. For example I have this particular expected response from the API:
{
isActive: false,
lastProcessed: "2020-11-03T19:03:16.537"
}
What I want to verify is that the response contains those two fields, not necessarily the value those fields contain. I tried the following method:
Assert.NotNull(response.Model.isActive);
Assert.NotNull(response.Model.lastProcessed);
But I'm getting an error using this method:
Do not use Assert.NotNull() on value type 'bool'
How else can I make sure the response structure is as expected other than using "NotNull()"?
The Solution:
Following the accepted answer, I serialized the model returned from the API call into JSON and parsed it into a JObject. Then I used the ContainsKey() method to assert it.
JObject jObject = JObject.Parse(JsonConvert.SerializeObject(response.Model));
Assert.True(jObject.ContainsKey("isActive"));
I don't know what packages you use for sending requests and deserialization but if you could get the response content as a raw json string you could then use Newtonsoft.JSON to parse the response into a JObject with JObject.Parse(responseContent). JObject has a method called ContainsKey(propertyName) which determines whether there is a field of a specified name in the object. You could assert if it returns true for the desired property names.
Edit
Regarding Greg's answer below. The original error is in fact caused by bool not being a nullable type and making it nullable in the model would fix the error. However, this solution is not ideal. In some cases null can be a valid value returned by the API and this would generate false negatives. e.g. if we recieved:
{
isActive: null,
lastProcessed: "2020-11-03T19:03:16.537"
}
then Assert.NotNull(response.Model.isActive) would yield a negative test result even though the field is present in the json, and that's what we wanted to check.
So theoretically if we are 100% sure that null will never be returned by the API itself, then we could do it that way, but it won't be a universal method. Also not very descriptive of what we are trying to achieve ;)
Since the isActive property is a bool you'll need to assert that it is false. If instead you want a true or false value, and then something to represent that it is missing, use a nullable boolean instead in your DTO:
public class YourDTO
{
public bool? isActive { get; set; }
...
}
Then you can assert isActive is null, true or false.
Alternative: If you cannot update the original data transfer object, then this might be a good use case for writing your own code to call the web service and map the JSON response to your own DTO used just for your tests.
This could be a large amount of work, however. The advantage is your test code is truly decoupled from the code it tests. I've done this with applications that use a database as well. It is extra work, but it allows your tests to use whatever data structure makes sense for the test.

Return an unknown object in an ApiController witih EF?

Is it possible to return a result set from the database without knowing the schema in advanced?
I'm exposing the ability for the client to pass parameters to a stored procedure via API:
[Route("TheRequest")]
public object Get([FromUri] TheRequest request)
This will then return:
_repository.Database.SqlQuery<object>(request.ToSqlString()); //execute sql against a stored procedure, passing in the required parameters to it
When attempting to do this, I believe that the controller doesn't know how to serialize the object returned. This is what postman returns:
Is it possible to return a Json serialized object response from the database without knowing the object schema?
Is it possible to return a result set from the database without knowing the schema in advanced?
In regards to just EF, the answer is not really. EF was designed for you to know the schema in advance. So you can still use DAO off of the ContextDb.Database but there isn't much point to using EF you you do that.
Now if the question was, can I generically typed instance from EF then sure, no problem:
var result = DbContext.Set<T>().FirstOrDefault();
This code doesn't know what it's pulling at compile time.
Is it possible to return a Json serialized object response from the database without knowing the object schema?
Sorta, as I mentioned before, you can't use EF as it was intended, but you could certainly do something like
public ActionResult Index(string type)
{
var entityType = Type.GetType(type);
// reflection
var methods = typeof(ContextDb).GetMethods("Set");
// Not tested, but something like the following
// Find the Generic Version
var method = methods.Where(m => m.IsGenericMethod).FirstOrDefault();
// Make the method info for the typed method
var methodInfo = method.MakeGenericMethod(entityType);
// Invoke method, cast as needed, get value
var result = (methodInfo.Invoke(ContextDb) as DbSet).FirstOrDefault();
return Json(result);
}

How to explicitly call the ASP.NET json serializer

So I have a small asp.net app which returns Json objects that are serialized from C# objects. If I just create a function:
[HttpGet(getTheObj)]
public SomeObj GetTheObject()
{
return new SomeObj() { SomeProperty = 1 };
}
Then it works fine and I can do an HttpRequest for the Json object. However I also want to save some these serialized objects into a database for later use. So I'm wondering, can I explicitly call the Json serializer? I understand that several different serializers can be used with ASP.NET, how do I figure out which one I am using (I didn't create the project).
string json = JsonConvert.SerializeObject(_data.ToArray());
you can save this in database. you can again retrive this object from database and deserialize this object.

Passing model with dynamic json object to MVC controller

I have a model like so in javascript:
function SomeModel()
{
this.Id = ko.observable();
this.Name = ko.observable();
this.MetaData = {};
}
Then I need to send this to MVC so I can persist it in mongodb, however I dont know how is best to store the MetaData in the C# representation... as the model looks like:
public class SomeModel
{
public Guid Id {get;set;}
public string Name {get;set;}
public object MetaData {get;set;}
}
However it doesnt like binding the json object to the object, I am not sure if I should be using an ExpandoObject or something, as I am not really that fussed about accessing the data within the c# mvc service, it is only for plugins in the front end which can store their own data on an object for use later. So an example could be:
var someModel = new SomeModel();
someModel.MetaData.Bookmarks = [];
someModel.MetaData.Bookmarks.push({ location: 120, document: 23003032 });
So it could contain anything and sub objects, and like I say I just need to bung it into an object just as a transport mechanism for Mongo, which will save it fine as its schema-less.
Currently the models from JS are sent over ajax via Jquery, and auto binded in the controller then sent off to the repository.
== Edit ==
I have done a few minor tests with MVC4, and I can put an expando object within my models and have it populated, however it only seems to store 1 layer of data, so if I were to postback:
...,
MetaData: {
something: {
one: 1,
two: 2
}
},
...
I would end up with my expando object for the MetaData field knowing about the something field, but would not store any of the data below that first traversal, so is there any way to store the above other than turning it into a string of json to be stored?
As there seemed to be no solid answers I just had to do an extra step in the jquery ajax to convert all "metadata" fields into a json string, then store it as a string on the server.
Then if I need to access or change the data I use the web helper Json.Encode/Decode which returns a dynamic, then I can just add child elements which I do as:
Anonymous type for simple included json object
List for an array element
Then I just encode it again casting the dynamic as an object.

Categories

Resources