I have a working Web API that i am converting to a .Net Azure Mobile Service. The API returns a complex model - objects with properties - some of which are collections of other objects. This works as expected with plain Web API but with Azure Mobile Services I have an issue where one of my models does not have all it's properties serialized.
When i set a break point on the return statement in the controller, I see that all the properties and their values are present. This leads me to believe that the issue is with serialization (JSON).
return Request.CreateResponse(HttpStatusCode.OK, myModel);
Examples of properties that are being serialized:
public Guid Id { get; set; }
public IEntityDto ModelDto { get; set; } //this is an object with many properties all of which serialize
Examples of properties that are NOT being serialized:
public ItemStatus Status { get; set; } //this is an enum
public string Message { get; set; }
public string TestProp { get; set; } //this is a simple string property I added to help debug
How can I go about further debugging this so that i can see why these properties are being excluded?
Note: At the moment I am still running this locally not off Azure. This is with Visual Studio 2013 Update 2 RTM.
UPDATE: Upon closer inspection it appears that the properties not being serialized are properties that are either enums or have a value of null.
As #carlosfigueira mentioned in a comment to the original question, the default behavior of the JSON serializer is to exclude properties with null and default values. To address this I changed the following settings:
httpConfig.Formatters.JsonFormatter.SerializerSettings.DefaultValueHandling = Newtonsoft.Json.DefaultValueHandling.Include;
httpConfig.Formatters.JsonFormatter.SerializerSettings.NullValueHandling = Newtonsoft.Json.NullValueHandling.Include;
...where httpConfig is of type HttpConfiguration. You can make these changes on app start - in a config file like WebApiConfig.cs or directly in Global.asax.cs.
Related
I am building a simple Asp.Net Core app in linux(pop os). I am using VueJs + Aps.Net Core 3.1.101
I am trying to do a POST call to my app and my model is like below:
public class AddConfigurationContextValueApiRequest
{
public int ContextId { get; set; }
[Required(ErrorMessage = "Value is required to continue")]
[StringLength(500, ErrorMessage = "Value can not be longer than 500 characters")]
public string Value { get; set; }
[StringLength(500, ErrorMessage = "Display name can not be longer than 500 characters")]
public string DisplayName { get; set; }
}
As you can see there is not Required attribute for the DisplayName field, but whenever I pass a null value from VueJS app for this field I get The DisplayName field is required..
I am trying to figure out why would AspNet Core complain for this, since there is no Required attribute for such field!
Does anybody know if this intentional ? I tried to remove the StringLength attribute and still it triggers required attribute.
My action is fairly simple:
[HttpPost(UrlPath + "addConfigurationContextValue")]
public async Task AddConfigurationContextValue([FromBody]AddConfigurationContextValueApiRequest request)
{
using var unitOfWork = _unitOfWorkProvider.GetOrCreate();
if (!ModelState.IsValid)
{
//Here it throws because ModelState is invalid
throw new BadRequestException(ModelState.GetErrors());
}
//do stuff
await unitOfWork.CommitAndCheckAsync();
}
I have seen the same issue where the .csproj Nullable setting caused a property that was not marked as [Required] to act as though it were. I took a different approach than changing the Nullable settings in the .csproj file.
In my case it came down to a property that is required by the database; but the model allows null during POST as this particular property is a secret from the user. So I had avoided changing string to string? initially.
Once again, the Fluent API has provided an alternative solution.
Original Property
[JsonIgnore]
[StringLength(15)]
public string MyProperty { get; set; }
Updated Property
[JsonIgnore]
public string? MyProperty { get; set; }
Fluent API Directives (in your DbContext file)
protected override void OnModelCreating(ModelBuilder builder) {
builder.Entity<MyClass>(c => {
c.Property(p => p.MyProperty)
.IsRequired()
.HasMaxLength(15)
.IsFixedLength();
});
}
Apparently .NET 6 Web APIs have the "Nullable" property added by default. I simply had to remove it.
.csproj file:
Edit: As Luke pointed out (without elaborating any further), the above behavior is working as intended and actually makes sense. If your JSON has a null value, your code might crash, if not handled. Throwing a compile time error is impossible, since the JSON is unknown. Forcing yourself to using nullable reference types makes the code more resilient.
I haven't checked yet, how swagger behaves, the default config surely doesn't care about a .NET 6 setting and will no longer flag mandatory fields, if you leave out the [Required] attribute and use only nullable reference types to point out what is mandatory and what isn't.
After #devNull's suggestion I found out that somehow while I was playing around with Rider IDE it seems it switched that feature on!
There is an option in rider that allows to change that configuration on project level:
If somebody has the same problem: right click on the project level, go to properties, Application and there you can see this configuration.
Thank you #devNull for the help :)
In my VS2017 EF-Core 2.2 solution, I have 3 projects:
Backend: WebServer - storing data in SQLServer, delivering data with NewtonSoft.Json
DataModels - classes used to create tables in SQLServer (Backend) and SQLite (Frontend)
Frontend: Xamarin.Forms app - using SQLite, getting data with NewtonSoft.Json
The Backend contains a project reference to the DataModels project.
Since my local SQLite database stores just a subset of the content defined in the DataModels classes, I did not add a project reference to the DataModels project, but created a Windows 10 symbolic link in my Frontend project to the DataModels directory, which allows me to modify the DataModel classes like
namespace DataModels
{
[Table("Info")] // Used for front- and backend
public abstract class BaseInfo
{
public Guid Id { get; set; }
[Required]
[StringLength(200)]
public string Title { get; set; }
#if FRONTEND // Only available on frontend side
[NotMapped]
public bool IsSelected {get; set;}
#endif
#if !FRONTEND // Only available on backend side
[Required]
[StringLength(25)]
public MediaProcessingStateEnum MediaProcessingState { get; set; }
#endif
}
}
This is quit nice, but now I have the problem,
that the Backend serializes the data with NewtonSoft.Json adding the $type item to it (since there are abstract classes)
but when I want to deserialize the json content on the Frontend side using
var info = JsonConvert.DeserializeObject<Info>(content, new JsonSerializerSettings
{
TypeNameHandling = TypeNameHandling.Auto
});
the deserializer can not create an instance of the class, because the (typeinfo) link to the DataModels assembly is missing!
"Error resolving type specified in JSON 'DataModels.Info, DataModels'. Path '$type', line 1, position 41."
Could someone please give me an idea, how to solve the 'missing reference' problem in NewtonSoft.Json deserializer, without giving up the concept described above?
I have a very weird scenario which I can't seem to figure out what's going on. I have a web service which is written in C# and the target framework is 3.5. I have many Classes and Methods but for simplicity I will make use of only two classes in the question.
public class PathNames
{
private string _pathA = "Some Path";
private string _pathB = "Another Path";
public string BaseDirectoryPath
{
get
{
return Path.Combine(_pathA, _pathB);
}
}
}
The second class is as follows:
public class UserInformation
{
public string UserName { get; set; }
...//more properties
}
Both of the above classes are in the same namespace.
The web service is referenced in a WebForm Application with the target framework being 4.0. Everything seems to be working fine and I can see the UserInformation class when I view it in Object Browser. However the PathNames class does not seem to be visible in the Object Broswer.
Both of the source files in question are set to Compile in the File Properties windows. I have 5 classes similar to that of UserInformation and same settings in the File Properties window where they are just simple POCO and only have public auto propteries. These all seem to be coming through and I can access them and see them in the Object Browser. For some strange reason I cannot PathNames class to come through. I have tried to add some new dummy classes and have the same issue as PathNames class. Can someone tell me what I am doing wrong please.
The web service old ASMX
Web service client is being created through VS add service reference
Using VS 2017 pro - version 15.6.7.
After publish if I de-compile the dll then the PathNames class is there. So it's clearly in the dll.
I have look at this but still no luck.
Using Data Contracts in web service
Service can't expose private/read-only properties. DataMember attribute is used for marking public members or properties (with public getter and setter) of class marked with DataContract attribute. DataContract can be used as parameter or return value of operation.
Windows Communication Foundation (WCF) uses a serialization engine called the Data Contract Serializer by default to serialize and deserialize data (convert it to and from XML), and XML serialization (by default) doesn't serialize read=only properties.
For More Information you can read the MS Docs for Data Contracts :- https://learn.microsoft.com/en-us/dotnet/framework/wcf/feature-details/using-data-contracts
To understand the various Limitation of Data Contracts please refer : -https://learn.microsoft.com/en-us/dotnet/framework/wcf/feature-details/types-supported-by-the-data-contract-serializer
Solution :
Anyway. What are you trying to do? Exposing properties means that you expect some stateful behavior. If you going to use this property in operation contract, which is seems to be, then you you must define the properties with public getter and setter so it can be serialized.
As the #Nikosi stated in previous answer, you have to define public setter for your BaseDirectoryPath property.
The only difference based on the example provided that one property is readonly while the other can be modified.
In order to make the class serializable consider rafactoring the property
public class PathNames {
private string _pathA = "Some Path";
private string _pathB = "Another Path";
public PathNames() {
BaseDirectoryPath = Path.Combine(_pathA, _pathB);
}
public string BaseDirectoryPath { get; set; }
}
You can use a default constructor to set the default value of the property
or just have an empty setter on the property
public class PathNames {
private string _pathA = "Some Path";
private string _pathB = "Another Path";
public string BaseDirectoryPath {
get {
return Path.Combine(_pathA, _pathB);
}
set {
//No OP
}
}
}
You can see how creating a Custom ASP.NET Web Service.
Might you need to rebuild web service ASP.Net and add your assembly to the global assembly cache (GAC).
I hope this help you.
A long time ago when I first looked at OData response payloads to GET requests contained links to other entities or entity sets (e.g. an OrderHeader entity would contain a link to the order's OrderDetails). I believe the correct term for this is hypermedia.
Today I'm checking out OData again and have built a OData v4 service using ASP.Net Web API however no such hypermedia links are being returned in the payloads. Why is this? Is it because the payload is now JSON (whereas when I looked years ago it was XML)? Is there any way to include hypermedia links in the payload?
Here's what I've built. I have an entity called Proposition:
public class Proposition
{
public int Id { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public ICollection<Event> Events { get; set; }
}
Notice that a Proposition has a collection of Events. I was hoping that when I requested a Proposition via the OData endpoint that I would get a link to the Events resource, but I don't:
I've found some information at OData JSON Format Version 4.0 Plus Errata 02#JSON Format Design that suggests adding $format=odata.metadata=full to the URL will return what I need:
The odata.metadata=full format parameter indicates that the service MUST include all control information explicitly in the payload
(odata.metadata=full)
but I've tried that and I'm not getting back any such metadata (screenshot this time from Postman):
Postman shows me that what got returned was odata.metadata.minimal:
Why is it ignoring my request for full metadata?
You should be able to add $format=application/json;odata.metadata=full to your querystring to achieve this via a GET request.
Aha, nailed it. I was specifying the URL wrongly. It needs to be ?$format=application/json;odata.metadata=full
Thanks to Stuart Preston for the help: https://twitter.com/StuartPreston/status/601107122550616064
I have been tasked with writing a web-service that will be used as an integration point between an ASP.NET MVC application and corporate SAP. Basically, I just have to write a SOAP web-service (Wcf SOAP) with an endpoint that SAP will call and transmit a bunch of data that I will then have to store in a SQL Server database.
I've never written a web-service before and am a but unsure on how the process will ultimately work. I generated an XML schema from the EF classes in the MVC application which I plan to provision SAP with for the data they are to send to the web-service. My solution designer suggested that I mimic the message structure that he uses in one of his REST web-services. This structure is as follows for the in-comming request:
public class DtoRequest<T> : IDtoRequest where T : class
{
public InteractionContext Context { get; set; }
public T Request { get; set; }
}
The InteractionContext is to hold various properties such details of the calling application and a license key for authentication. The Request will contain the payload.
namespace RfqService.Messages
{
public class SendRfqRequest : DtoRequest<RequestDataDto>
{
}
public class SendRfqResponse : DtoResponse<DtoVoidResponse>
{
}
}
The RequestDataDto looks as follows:
namespace RfqService.DataTransferObjects
{
[DataContract]
public class RequestDataDto
{
[DataMember]
public int RequestId { get; set; }
[DataMember]
public DateTime CreatedDate { get; set; }
[DataMember]
public DateTime DateProcessed { get; set; }
[DataMember]
public string Status { get; set; }
[DataMember]
public string XmlData { get; set; }
}
}
What I'm planning is for the XmlData in the RequestDataDto to contain the xml data sent from SAP that I can then deserialize into the EF classes and persist to the database.
SAP can then call an endpoint in my .asmx file:
[WebMethod]
public SendRfqResponse SendRfq(SendRfqRequest request)
{ ... }
Problem I'm facing is when inspecting the implementation of the the REST service of the solution designer. He calls the REST endpoint from a Webforms application. I can see how he constructs the message object in C# and send it to the web-service. I have no idea how this will work when its coming from a different platform like SAP. I've been trying to read up on SOAP in general and have come across the following extract here:
The primary difference between ORPC and the RPC protocols that preceded them was that ORPC codified the mapping of a communication endpoint to a language-level object.
Is the data sent from SAP automatically converted to my Request object? Does the data come in XML and I have to deserialize it into my object that in turn contains xml data in the payload? Are the parameters in the InteractionContext come from what's in the SOAP object's header? Do I have to serialize my response into xml before returning it to SAP?
Example If you are doing a REST web service call
If you really are doing a SOAP call.
Then load create a new client service using the wsdl from IIS where the service is hosted.
This will create a PROXY in ABAP.
You just call that proxy.
The data sent is transformed in XML as per the WSDL definition for you.
Equally on the IIS end the Service should populate the your Data Contract type for you automatically.
You should not have to worry about XML anywhere if done properly and you are using SOAP.
ABAP->IIS
if its aysnch call you will need to set up reliable messaging on the SAP side. That will keep you basis guy busy