serialize null navigation property - asp.net-web-api

I have simple class:
public class Person
{
public string Name {get; set;}
public Address Address {get; set;} // can be null
}
When I query Person I want webapi return me empty Address property. I tryed the following:
GlobalConfiguration.Configuration.Formatters.JsonFormatter.SerializerSettings = new Newtonsoft.Json.JsonSerializerSettings()
{
NullValueHandling = Newtonsoft.Json.NullValueHandling.Include,
};
but, nothing chaged, it's just not exist in result.

You don't need to configure the formatter, just add $expand query option:
GET ~/Person?$expand=Address
I tried, it works. and I'm using WebApi OData for OData v4.
{
"#odata.context":"http://jinfutan13:9123/$metadata#Albums/$entity",
"ID":5,"Name":"Name 5",
"Singer":null
}
Where Singer is also a navigation property.

Related

Validate parameters before model binding for PUT request in web api

How do I distinguish between a parameter being sent as String.Empty and not being sent at all for my parameter binding for a PUT request.
My request class looks like :
public class Person
{
string name {get; set;}
int? age {get; set;}
}
My problem is with binding
When my user sends request as
{
"name":"ABC"
}
In above mentioned case age parameter is mapped as null
However when request looks like below it maps to null as well. I would like to throw a validation error in below situation.
How do I achieve it in asp net core web api
{
"name":"ABC",
"age":""
}
You should take a look at DataAnnotations.
You can add the Range attribute on your nullable int. That will only allow integers or null, not empty strings.
public class Person
{
string name {get; set;}
[Range(0,300)]
int? age {get; set;}
}
If the data annotations are not fullfilled it will set the modelstate to false
Then check the modelstate in the controller method
if (ModelState.IsValid)
{
// your logic
return new HttpResponseMessage(HttpStatusCode.OK);
}
else
{
return Request.CreateErrorResponse(HttpStatusCode.BadRequest, ModelState);
}

Serialization attributes on TableEntity in Azure Table Storage

Im using web API to return data in azure table storage. Im returning a class that I 'm inheriting TableEntity in a class and adding properties but want to keep to the .Net convention of capitalized property names but also keep to the JavaScript/json convention of lowercase properties names.
I've tried adding the Json.net property attributes to the class but it appears to be ignored. E.g.:
[JsonProperty("id")]
public string ID {get;set;}
If the instance has a value set on ID, null is represent in the serialized result.
According to your description, I tested this issue on my side and found it works well on my side and Azure. Here is my detailed steps, you could refer to it.
Create a controller named UserInfoController in the Web API application with the Get function like this:
// GET: api/UserInfo
[HttpGet]
public async Task<string> Get()
{
CloudStorageAccount storageAccount = CloudStorageAccount.Parse(<your-Storage-ConnectionString>);
CloudTableClient tableClient = storageAccount.CreateCloudTableClient();
CloudTable cloudTable = tableClient.GetTableReference("UserInfo");
TableQuery<User> query = new TableQuery<User>()
.Where(TableQuery.GenerateFilterCondition("PartitionKey", QueryComparisons.Equal, "Grade Four"));
var results =await cloudTable.ExecuteQuerySegmentedAsync(query, null);
//Serialize the object to string by using the latest stable version of Newtonsoft.Json
string jsonString = JsonConvert.SerializeObject(results);
return jsonString;
}
Entity
public class User : TableEntity
{
public User(string partitionKey, string rowKey)
{
this.PartitionKey = partitionKey;
this.RowKey = rowKey;
}
public User() { }
[JsonProperty("id")]
public long ID { get; set; }
[JsonProperty("username")]
public string UserName { get; set; }
[JsonProperty("phone")]
public string Phone { get; set; }
[JsonProperty("age")]
public int Age { get; set; }
}
Result
Deploy the Web API application to Azure, then you could find the following result by calling the function via Fiddler.
In summary, please try to check the version of Json.NET you are using. If you aren't using the latest (9.0.1), then please try to upgrade to the latest version and run your application again to find whether it could work as expected.
FYI - while this doesn't answer the direct answer of how to get TableEntity to respect JSON.net attributes... I was able to solve the use case by overriding the ReadEntity and WriteEntity method in the inherited class:
e.g.
public class User : TableEntity{
//Upper case name
public string Name {get; set};
public override void ReadEntity(IDictionary<string, AzureTableStorage.EntityProperty> properties, OperationContext operationContext){
base.ReadEntity(properties, operationContext);
//lower case
this.Name = properties["name"];
}

Web API parameters binding

I have this action in Web Api controller:
public Data GetData(ComplexModel model)
and model is
class ComplexModel
{
Guid Guid1 { get; set;}
Guid Guid2 { get; set;}
string String1 { get; set;}
}
i would like to specify custom binder for Guid type such as empty string or null would bind to empty guid and would like not use nullable type. Was trying to register model binder like this:
var pb = configuration.ParameterBindingRules;
pb.Insert(0, typeof(Guid), param => param.BindWithModelBinding(new GuidBinder()));
but it is not called and I am getting invalid model state with error message that empty string cant be converted to type Guid.
Remember, ParameterBindingRules checks the controller action parameter itself (ComplexModel in your case), not the contents of the parameter. You'd need to register a parameter binding against ComplexModel and do any processing with the custom binder to validate the model instance. Seems like you're better off making the Guid properties nullable despite your reluctance to do so.

MetaData class for entityframework POCO

I am creating a meta data class for a POCO object. I am adding the "CSVColumn" (from LINQToCSV) attribute to the meta data class. But when I run the program, it couldn't find its attributes.
So I tested it using reflection,
Type t = typeof(Case);
PropertyInfo pi = t.GetProperty("ProviderId");
//bool isReadOnly = ReadOnlyAttribute.IsDefined(pi,typeof( ReadOnlyAttribute);
var attributes = pi.GetCustomAttributes(typeof(Case),true);
It acutally return nothing by calling the "GetCustomAttributes".
What have I done wrong??
Below is the way I created metadata class.
One thing I don't understand is, it works perfectly well with MVC validations. Wondering how does that retrieve the custom attributes???
This is the entityframework POCO object
public partial class Case
{
public string ProviderName { get; set; }
public string ProviderId { get; set; }
}
Here I create a partial class of Case and metadata classes,
[MetadataType(typeof(CaseMetaData))]
public partial class Case
{
public class CaseMetaData
{
[CsvColumn(Name = "ProviderName", FieldIndex = 1)]
public string ProviderName { get; set; }
[CsvColumn(Name = "ProviderID", FieldIndex = 2)]
public string ProviderId { get; set; }
}
}
Please someone can help me, much appreciated.
Cheers
typeof(Case) isn't an attribute type.
You mean typeof(CsvColumnAttribute).
Also, standard Reflection isn't aware of metadata classes.
You need to use AssociatedMetadataTypeTypeDescriptionProvider.
A good example can be found here

Can't get a collection in a entity class

Suppose I have following tables with the right relationships built:
Employee(empID, ...)
Address(AddresID, ...)
EmployeeAddress(EmpID, AddressID, ...)
Then modified the generated code for GetEmployee by .NET RIA Services like:
public IQueryable<Employee> GetEmployee()
{
var Employee = this.Context.Employee.Include("EmployeeAddress").Include("Address");
return Employee;
}
Attribute [Include] has been added for EmployeeAddress in Employee and Address in EmployeeAddress.
When running the code at silverlight client side with following code:
EntityQuery<Employee> query = from e in ctx.GetEmployeeQuery()
select e;
I got nothing. If I remove the include from GetEmployee, like:
public IQueryable<Employee> GetEmployee()
{
var Employee = this.Context.Employee;
return Employee;
}
It works fine.
For lookup member in Employee, like Gender
public IQueryable<Employee> GetEmployee()
{
var Employee = this.Context.Employee.Include("GenderLookup");
return Employee;
}
It works fine. Here Employee.Gender is a single object. Is it because Employee.EmployeeAddress is a collection, not a single object?
Can't figure out the reason. How to resolve it?
You have to use the IncludeAttribute on the property representing the collection on the "buddy" metadata class. Most RIA services examples demonstrate this.
This is eager loading, not lazy loading. I'm guessing that's what you meant, since lazy loading from a distributed server is not generally a good idea.
Are you using RIA services to get data from your server to your client? If so, then you'll need to use meta-data and the [Association] attribute so that RIA Services recognizes the relationship.
[MetadataType(typeof(EmployeeMetadata))]
public partial class Employee
{
public int EmployeeId {get; set; }
public EmployeeAddress Address {get; set; }
}
public partial class EmployeeAddress
{
public int EmployeeId {get; set; }
}
public class EmployeeMetaData
{
[Include]
[Association("EmployeeAddress", "EmployeeId", "EmployeeId")]
public EmployeeAddress Address {get; set;}
}
The example above assumes that both your Employee class and your Address class have an "EmployeeId" property that RIA Services can use to create the association.
More information
MSDN documentation
Brad Abrams blog post
I am pretty sure that you cannont edit the GetEmployee() method. Rather you should create a new method called: GetEmployeeAddress()
public IQueryable<Employee> GetEmployeeAddress(string address)
{
var Employee = this.Context.Employee.Where((x) => x.ID = address_ID)
}
Something like that. Brad is also right but I would suggest making the associations in the model viewer or on the DB itself.

Resources