I am using this package Microsoft.Extensions.Caching.Redis.
What is the way to pass tModel to set and get from cache.
public class TestModel
{
public int test1 { get; set; }
public string test2 { get; set; }
}
var tModel = new TestModel();
tModel.test1 = 1;
tModel.test2 = "abc";
_distributedCache.Set("model", tModel);
I would serialize the data to JSON and persist it as a string:
using Newtonsoft.Json;
// ...
// Set
var jsonData = JsonConvert.SerializeObject(tModel);
_distributedCache.SetString("model", jsonData);
// Get
var jsonData = _distributedCache.GetString("model");
var tModel = JsonConvert.DeserializeObject<TestModel>(jsonData);
Notice I used the GetString and SetString extension methods here, rather than the low-level Get and Set method which takes and returns bytes. There are also async methods available.
Related
I have a simple class with a property of type object, which can contain JSON built from different sources
public class SimpleClass
{
public string Id { get; set; }
public object JSONData { get; set; }
}
I'm using FluentAssertions object graph comparison but this isn't working
private void Verify(SimpleClass actualOutput, SimpleClass expectedOutput)
{
actualOutput.Should().BeEquivalentTo(expectedOutput);
}
Depending on the source of the objects, I'm seeing messages like these
Expected member JSONData to be a
System.Collections.Generic.IDictionary`2[System.String,Newtonsoft.Json.Linq.JToken], but found a
System.Text.Json.JsonElement.
Expected member JSONData to be
System.String, but found
System.Text.Json.JsonElement.
The objects are equal when I convert them to strings
private void Verify(SimpleClass actualOutput, SimpleClass expectedOutput)
{
actualOutput.JSONData.ToString().Should().Be(expectedOutput.JSONData.ToString());
}
I don't want to compare individual properties like that, because I need to compare a collection of SimpleClass instances. Is there any way to make this work using object graph comparison? Can I configure FluentAssertions to do the string conversion during comparison?
[Fact]
public void CompareJson()
{
var jsonText = #"{
""Element1"": 123,
""Element2"": ""text""
}";
var x1 = new SimpleClass { Id = "X", JSONData = jsonText };
var x2 = new SimpleClass { Id = "X", JSONData = JsonConvert.DeserializeObject(x1.JSONData.ToString()) };
x1.Should().BeEquivalentTo(
x2,
o => o.Using<object>(ctx => ctx.Subject.ToString().Should().Be(ctx.Expectation.ToString())).When(
info => info.SelectedMemberPath.EndsWith(nameof(SimpleClass.JSONData))));
}
I have a call to a WebAPI with the following code:
var client = new HttpClient
{
BaseAddress = new Uri("http://localhost:8490/")
};
var jObject = new JObject();
jObject.Add("paramA", paramA);
jObject.Add("paramB", paramB);
JArray jArr = JArray.FromObject(paramsGenericArr);
jObject.Add("paramC", jArr);
var content = new StringContent(jObject.ToString(), Encoding.UTF8, "application/json");
var result = await client.PostAsync("api/path/tofunc", content).ConfigureAwait(false);
result.EnsureSuccessStatusCode();
The ParamsGeneric class is an abstract type with 2 derived classes:
[DataContract]
public class ParamsTypeA : ParamsGeneric
{
[DataMember]
public long itemC {get; set;}
public ParamsTypeA() :
base()
{}
}
[DataContract]
public class ParamsTypeB : ParamsGeneric
{
[DataMember]
public long itemD {get; set;}
public ParamsTypeB() :
base()
{}
}
[DataContract]
[KnownType(typeof(ParamsTypeA))]
[KnownType(typeof(ParamsTypeB))]
public abstract class ParamsGeneric
{
[DataMember]
public long itemA { get; set; }
[DataMember]
public long itemB {get; set;}
public ParamsGeneric()
{}
}
I suspect that I have a problem with the deserialization in the WebAPI:
public class ClientData
{
public string paramA { get; set; }
public string paramB { get; set; }
public ParamsGeneric[] paramC { get; set; }
}
[HttpPost]
[Route("api/path/tofunc")]
public async Task<bool> DoStuffAsync(ClientData clientData)
{
....
}
I have a problem with the paramsGenericArr/paramC (which is of type ParamsGeneric[], and holds items of type ParamsTypeA & ParamsTypeB)
The WebAPI receives a blank array (ParamsGeneric[0]), along with the other parameters.
Help will be appriciated.
UPDATE
Even if I try to pass a single ParamsGeneric object instead of an array, I receive null instead of the object.
SOLUTION
var serializer = new JsonSerializer();
serializer.TypeNameHandling = TypeNameHandling.Auto;
JArray jArr = JArray.FromObject(paramsGenericArr, serializer);
Did the trick.
While inheritance in messages / json is definitely possible, IMHO; it's just too much of a hassle :)
Anyway, you can actually let Newtonsoft.Json handle the inheritance for you, by setting TypeNameHandling
// Allow inheritance in json payload
JsonSerializerSettings serializerSettings = GlobalConfiguration.Configuration.Formatters.JsonFormatter.SerializerSettings;
serializerSettings.TypeNameHandling = TypeNameHandling.All;
.. or just
serializerSettings.TypeNameHandling = TypeNameHandling.Auto;
.. depending on your needs.
This is the easy fix, which will work fine if it's an internal API or if you can guarantee you will always be in control of the clients. If you have external clients, I would go the 'override default model binder'-approach such as what is posted here "Deserialising Json to derived types in Asp.Net Web API" - or very strongly consider avoiding inheritance in the model of the API.
Try to pass data to your API as below
Dictionary<string, string> param = new Dictionary<string, string>();
param.Add("paramA", paramA);
param.Add("paramB", paramB);
HttpClient client = new HttpClient();
HttpFormUrlEncodedContent contents = new HttpFormUrlEncodedContent(param);
var result = await client.PostAsync(new Uri("http://localhost:8490/api/path/tofunc")
, contents);
var reply = await result.Content.ReadAsStringAsync();
if (reply.IsSuccessStatusCode)
{
}
Hope this will help you.
[HttpPost]
[Route("api/path/tofunc")]
public async Task<bool> DoStuffAsync([FromBody]ClientData clientData)
{
....
}
Please keep [FromBody] in the web api method, so that model binder will map your body data to parameter i.e clientData.
I have two classes in the different location.
namespace IVR.MyEndpointApi.POCO
{
[Table("MyServiceUrl")]
public class MyURL
{
[Key]
[Column("FacilityID")]
public int FacilityId { get; set; }
[Column("Url")]
public string Url { get; set; }
}
}
namespace OpsTools.Models
{
public class MyServiceEndpoint
{
public int FacilityId { get; set; }
public string Url { get; set; }
}
}
In another method, I get the list and want to convert then return it as the desired type. I manually do it as below:
public List<MyServiceEndpoint> GetAllUrls()
{
var management = GetMyEndpointManagement();
var list = management.GetAllUrls();
var urlList = new List<MyServiceEndpoint>();
foreach (var item in list)
{
// the type of item is MyURL
var MyUrl = new MyServiceEndpoint();
myUrl.FacilityId = item.FacilityId;
myUrl.Url = item.Url;
urlList.Add(myUrl);
}
return urlList;
}
My question: can I apply AutoMapper to it?
EDIT:
I used the code:
var myUrls = management.GetAllUrls();
var urlList = new List<MyServiceEndpoint>();
Mapper.CreateMap<MyServiceEndpoint, MyURL>();
urlList = Mapper.Map<List<MyServiceEndpoint>, List<MyURL>>(myUrls);
Mapper.AssertConfigurationIsValid();
However, it has the error:
Error CS1503 Argument 1: cannot convert from 'System.Collections.Generic.List' to ....
Oops, come on. I change the order, then it works.
From
urlList = Mapper.Map<List<MyServiceEndpoint>, List<MyURL>>(myUrls);
To
urlList = Mapper.Map<List< List<MyURL>,MyServiceEndpoint>>(myUrls);
If you inspect the actual exception (you left the relevant part off), you'll see that Mapper.Map<TSource, TDestination>() tries to map from and to the wrong types.
The full error will read:
cannot convert from System.Collections.Generic.List<MyURL> to System.Collections.Generic.List<MyServiceEndpoint>
Which means that call will actually try to map from List<MyServiceEndpoint>, requiring an argument of that type, which your source list isn't.
Simply switch the types in the Map() call:
urlList = Mapper.Map<List<MyURL>, List<MyServiceEndpoint>>(myUrls);
Or remove the new list creation entirely, move the declaration and use type inference:
var urlList = Mapper.Map<List<MyServiceEndpoint>>(myUrls);
We have got a Odata response as below:
"{\r\n \"#odata.context\":\"http://localhost/ApplicationService/model/$metadata#Edm.String\",\"value\":\"{\\\"Messages\\\":[\\\"message 1\\\",\\\"message 2\\\",\\\"message 3\\\",\\\"message 4\\\"],\\\"IsValidEntity\\\":false}\"\r\n}"
Now say we have a class:
public class myValidationResult
{
public myValidationResult()
{
Messages = new List<string>();
}
public List<string> Messages { get; set; }
public bool IsValidEntity { get; set; }
}
This class used in MyOdataController class as below:
public class MyODataController : ODataController
{
[Authorize(Roles = "Admin")]
public async Task<IHttpActionResult> Post(T entity)
{
myValidationResult vResult = new myValidationResult();
vResult.Messages.Add("message 1");
vResult.Messages.Add("message 2");
vResult.Messages.Add("message 3");
vResult.Messages.Add("message 4");
vResult.IsValidEntity = false;
var strResult = JsonConvert.SerializeObject(vResult);
var resp = Content(HttpStatusCode.BadRequest, strResult );
return resp;
}
}
For the client Consuming this, we created below Class:
public class OData<T>
{
[JsonProperty("odata.context")]
public string Metadata { get; set; }
public T value { get; set; }
}
In the method where we call the Odata method & store response in 'msg':
var resp = msg.Result.Content.ReadAsStringAsync().Result;
resp is:
"{\r\n \"#odata.context\":\"http://localhost/ApplicationService/model/$metadata#Edm.String\",\"value\":\"{\\\"Messages\\\":[\\\"message 1\\\",\\\"message 2\\\",\\\"message 3\\\",\\\"message 4\\\"],\\\"IsValidEntity\\\":false}\"\r\n}"
var odatares = JsonConvert.DeserializeObject<OData<myValidationResult>>(resp);
But the above line giving error:
Can not convert value\":\"{\\\"Messages\\\":[\\\"message 1\\\",\\\"message 2\\\",\\\"message 3\\\",\\\"message 4\\\"],\\\"IsValidEntity\\\":false} to <.....namespace......>myValidationResult
Please suggest accordingly.
The OData response contains a string, not an instance of myValidationResult. Also, the response looks like it's missing some backslashes. (Are you sure the response shown is exactly what you received from the service?)
You can either fix the serialization of myValidationResult on the service:
// Don't serialize vResult yourself. OData will do it for you.
var resp = Content(HttpStatusCode.BadRequest, vResult );
Or deserialize in two steps as follows.
var data = "{\r\n \"#odata.context\":\"http://localhost/ApplicationService/model/$metadata#Edm.String\",\"value\":\"{\\\"Messages\\\":[\\\"message 1\\\",\\\"message 2\\\",\\\"message 3\\\",\\\"message 4\\\"],\\\"IsValidEntity\\\":false}\"\r\n}";
var outer = Newtonsoft.Json.JsonConvert.DeserializeObject<OData<string>>(data);
var inner = Newtonsoft.Json.JsonConvert.DeserializeObject<myValidationResult>(outer.value);
One more thing: The JsonProperty on OData<T> should be named #odata.context.
In my case the OData response did not contain a string but an object array which contains the data string as its first element. So in this case reading the data should look like this:
var outer = Newtonsoft.Json.JsonConvert.DeserializeObject<OData<object[]>>(data);
var inner = Newtonsoft.Json.JsonConvert.DeserializeObject<myValidationResult>(outer.value[0].ToString());
I'm parsing a JSON string using the NewtonSoft JObject.
How can I get values from a dynamic object programmatically?
I want to simplify the code to not repeat myself for every object.
public ExampleObject GetExampleObject(string jsonString)
{
ExampleObject returnObject = new ExampleObject();
dynamic dynamicResult = JObject.Parse(jsonString);
if (!ReferenceEquals(dynamicResult.album, null))
{
//code block to extract to another method if possible
returnObject.Id = dynamicResult.album.id;
returnObject.Name = dynamicResult.album.name;
returnObject.Description = dynamicResult.albumsdescription;
//etc..
}
else if(!ReferenceEquals(dynamicResult.photo, null))
{
//duplicated here
returnObject.Id = dynamicResult.photo.id;
returnObject.Name = dynamicResult.photo.name;
returnObject.Description = dynamicResult.photo.description;
//etc..
}
else if..
//etc..
return returnObject;
}
Is there any way I can extract the code blocks in the "if" statements to a separate method e.g:
private void ExampleObject GetExampleObject([string of desired type goes here? album/photo/etc])
{
ExampleObject returnObject = new ExampleObject();
returnObject.Id = dynamicResult.[something goes here?].id;
returnObject.Name = dynamicResult.[something goes here?].name;
//etc..
return returnObject;
}
Is it even possible since we can't use reflection for dynamic objects. Or am I even using the JObject correctly?
Thanks.
Assuming you're using the Newtonsoft.Json.Linq.JObject, you don't need to use dynamic. The JObject class can take a string indexer, just like a dictionary:
JObject myResult = GetMyResult();
returnObject.Id = myResult["string here"]["id"];
Hope this helps!
Another way of targeting this is by using SelectToken (Assuming that you're using Newtonsoft.Json):
JObject json = GetResponse();
var name = json.SelectToken("items[0].name");
For a full documentation: https://www.newtonsoft.com/json/help/html/SelectToken.htm
with dynamic keyword like below:
private static JsonSerializerSettings jsonSettings;
private static T Deserialize<T>(string jsonData)
{
return JsonConvert.DeserializeObject<T>(jsonData, jsonSettings);
}
//if you know what will return
var jresponse = Deserialize<SearchedData>(testJsonString);
//if you know return object type you should sign it with json attributes like
[JsonObject(MemberSerialization = MemberSerialization.OptIn)]
public class SearchedData
{
[JsonProperty(PropertyName = "Currency")]
public string Currency { get; set; }
[JsonProperty(PropertyName = "Routes")]
public List<List<Route>> Routes { get; set; }
}
// if you don't know the return type use dynamic as generic type
var jresponse = Deserialize<dynamic>(testJsonString);