MVC4 WebApi OData Delta<T> is not populating GUIDs or Int fields - asp.net-mvc-4

I'm looking to do partial updates on a web api controller action by using the Delta wrapper.
I have a model like such:
public class Person
{
public Guid PersonId { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public bool IsActive { get; set; }
public int NumVacationDays { get; set; }
public double Salary { get; set; }
}
I have the api controller like such:
public void Put(Delta<Person> person)
{
var p = person.GetEntity();
Person existingPerson = _repository.Get(p.PersonId);
person.Patch(existingPerson);
_repository.Update();
return;
}
I make the call to the web api like such (using fiddler)
url: http://localhost:49933/api/Person (PUT)
Response Body
{
"PersonId": "b269c49f-8a90-41d6-b102-7cfba3812b1c",
"FirstName": "sample string 2",
"LastName": "sample string 3",
"IsActive": true,
"NumVacationDays": 5,
"Salary": 6.1
}
The controller is hit and al
l the data is populated other than the NumVacationDays (which is 0) and the PersonId (which defaults to 00000000-0000-0000-0000-000000000000)
Does anyone know why the GUIDs and int fields are not populating from the json?

This problem is mentioned in this bug: http://aspnetwebstack.codeplex.com/workitem/562 ...Claims to be fixed but still exists in the 4.0 just released.
The problem being that Newtonsoft Json deserializes a number as Int64 which fails test that IsAssignable to an int, and so it is skipped. Similar issue for guids as strings.
You are supposed to be able to fix this by using the OData media type formatters, which are enabled by deriving from ODataController instead of ApiController. However this had no effect for me - int values still do not work (but when I change the data type to Int64, it works).
I would love to see a working example of posting json with a patch delta that contains an int.

I can venture a guess for what is happening with PersonId but NumVacationDays cannot be explained. My guess is that PersonId is the key property for the Person entity and by default the ODataFormatter does not patch key properties. If you want that behavior, you can change the setting on ODataMediaTypeFormatter.PatchKeyMode to Patch.
Also, it would be interesting to see the value of person.GetChangedPropertyNames() in the action to see if PersonId and NumVacationDays actually show up there or not.

Related

How to send complex data to controller?

I want to send an object look like
{
jobno :8001,
formid :FF000123,
type:02,
fd03:["1,"2","3"]
}
how to map this data with a controller in asp.net core?
You can use the built-in Visual Studio "Paste JSON as Classes" feature, it will create a C# class (or multiple classes) to match the JSON that you pasted.
For more info see here:
https://www.c-sharpcorner.com/article/how-to-paste-json-as-classes-or-xml-as-classes-in-visual-stu/
https://visualstudiomagazine.com/blogs/tool-tracker/2018/02/paste-json-and-xml-as-class.aspx
Note that your JSON (example) is incorrectly formatted, this is what you'd have to "Paste Special" for it to work:
{ "jobno": 8001, "formid": "FF000123", "type": 02, "fd03": ["1","2","3"] }
If you are not using Visual Studio, then you can generate C# classes here:
https://app.quicktype.io/?l=csharp
The answer is simple - use c# class to describe this data.
Something like that:
public sealed class Request
{
[JsonProperty("jobno")]
public int JobNumber { get; set; }
[JsonProperty("formid")]
public string FormId { get; set; }
[JsonProperty("type")]
public string Type { get; set; }
[JsonProperty("fd03")]
public string[] Fd03 { get; set; }
}
Then add your class as argument to your controller method.

API testing using post man

I am new to api testing from postman.I went through several blogs and articles on
api testing.But I don't know how deeper we can done testing.
Also how can we write test for post request.I have the below department mode class
from my web api.
[Key]
public int DepartmentId { get; set; }
[Required]
[Display(Name ="Department Name")]
public string DepartmentName { get; set; }
public string Description { get; set; }
public bool IsActive { get; set; }
The sample get request i used is given below
var body = JSON.parse(responseBody);
if(!body.DepartmentId)
{
tests["department id must exists in response"]=true;
}
else
{
tests["department id exists in response"]=true;
}
if(typeof body.DepartmentName !='string')
{
tests["department name must be type string"]=true;
}
if(responseCode.name.has("OK"))
{
tests["Status code name has string OK"] = true;
}
Whether the above testing procedure is correct.?
What all things to be tested while invoking a post request and get request against department controller for the above model.
You dont have to write 2 test cases to test for +ve and -ve scenario. All you need to do is write one test case and it will either pass or fail based on the scenario. For example lets consider your DepartmentId
The test case should be
tests["department id must exists in response"]= body.DepartmentId? true : false;
Based on whether you department id exists or not either your test case will pass (marked green) or fail (marked red).
Alternatively if you are dealing with not only testing but also designing your APIs and documenting them then have a look at http://myapic.com. Its a great tool for end to end API design documenting and testing.

loosing dataAnottation when upload model from database

I have a big database existing database to comunicate with, and I'm using EF 5.0 database first, the problem I'm having is that if I create any data decoration like [stringlength(50)] on the class and then the databases is uploaded, when I "upload from database" all data annotations are gone. How can I do to keep them?
It's very simple: You Can't! Because those codes are auto-generated and will be over written on each model update or change.
However you can achieve what you need through extending models. Suppose that EF generated the following entity class for you:
namespace YourSolution
{
using System;
using System.Collections.Generic;
public partial class News
{
public int ID { get; set; }
public string Title { get; set; }
public string Description { get; set; }
public int UserID { get; set; }
public virtual UserProfile User{ get; set; }
}
}
and you want do some work arounds to preserve your you data annotations and attributes. So, follow these steps:
First, add two classes some where (wherever you want, but it's better to be in Models) like the following:
namespace YourSolution
{
[MetadataType(typeof(NewsAttribs))]
public partial class News
{
// leave it empty.
}
public class NewsAttribs
{
// Your attribs will come here.
}
}
then add what properties and attributes you want to the second class - NewsAttribs here. :
public class NewsAttrib
{
[Display(Name = "News title")]
[Required(ErrorMessage = "Please enter the news title.")]
public string Title { get; set; }
// and other properties you want...
}
Notes:
1) The namespace of the generated entity class and your classes must be the same - here YourSolution.
2) your first class must be partial and its name must be the same as EF generated class.
Go through this and your attribs never been lost again ...
The accepted answer may work for standard data operations, but I am trying to validate the model prior to the call to DbSet.Add using TryValidateObject. With the accepted answer, it is still not picking up on the data annotations.
What did work for me I found in a .NET Runtime GitHub thread, as proposed by what I'm inferring is one of the .NET developers.
Basically, this is a bug, and you have to force the model to recognize the metadata decorations using TypeDescriptor.AddProviderTransparent . . .
TypeDescriptor.AddProviderTransparent(new AssociatedMetadataTypeTypeDescriptionProvider(typeof(News), typeof(NewsAttrib)), typeof(News));
Once I make this call, TryValidateObject recognizes the data annotations and returns false when any of the constraints are not met.
Here's the link. I little more than half-way down, there's a working code sample in a .zip file.
https://github.com/dotnet/runtime/issues/46678

ASP.NET MVC Web Api Get Not Mapping QueryString To Strongly Typed Parameter

The parameter request is always null using Web API. Am I missing something with using a strongly typed object as a parameter instead of simple types as the parameters.
Url
http://localhost:2222/api/v1/divisions?EventId=30
Controller Action
public virtual ApiDivisionsResponse Get(ApiDivisionsRequest request)
{
return _apiDivisionsService.GetDivisions(request);
}
Object
public class ApiDivisionsRequest : ApiAuthorizedRequest
{
public ApiDivisionsRequest()
{
Page = 1;
PageSize = 10;
}
public int EventId { get; set; }
public int PageSize { get; set; }
public int Page { get; set; }
public string[] Includes { get; set; }
}
I very strongly invite you to read the following article to better understand how parameter binding works in the Web API. After reading it you will understand that by default the Web API binds query string parameters to primitive types and request body content to complex types.
So if you need to bind query string parameters to complex types you will need to override this default behavior by decorating your parameter with the [FromUri] parameter:
public virtual ApiDivisionsResponse Get([FromUri] ApiDivisionsRequest request)
{
...
}
And yeah, I agree with you - that's a hell of a mess - model binding was so easy in plain ASP.NET MVC and they created a nightmare in the Web API. But once you know how it works you will avoid the gotchas.

How to auto-load details (with conditions) associated with an entity using Ria Services?

I'm developing a project using Silverlight 4 and Entity Framework 4 and I'm trying to auto-load the details (with conditions) associated with an entity when the client loads the EntityQuery.
So far, I've been able to put in place a solution, using the Include attribute, that returns all the details associated with the master entity. What I'm missing here is to be able to filter out the details based on some criteria.
As an example, here's what my entities look like:
Entity Movie
Id (int)
[Include]
MovieLocalizedInformations (EntityCollection<MovieLocalizedInformation>)
Entity MovieLocalizedInformation
Id (int)
Movie_Id (int)
LanguageCode (eg.: en)
Title
On my DomainService object, I expose the following method:
public IQueryable<Movie> GetMovies( string languageCode )
{
return this.ObjectContext.Movies.Include( "MovieLocalizedInformations" );
}
This works fine. But when I try to add where clause to filter out the localized information based on the language code, only the movies get loaded on the client.
Is there a way to achieve the filtering in one query?
Note: I'm also using the DomainDataSource with paging on the client so the solution needs to work with that.
Any help would be greatly appreciated!
Thanks,
Jacques.
Not sure about Enitity Framework but with a LinqToSqlDomainService you use the LoadWith loadOption
to include the details entities and then use the AssociateWith LoadOption to filter the detail e.g
DataLoadOptions options = new DataLoadOptions();
options.LoadWith<Movies>(i => i.MovieLocalizedInformations);
options.AssociateWith<Movies>(i => i.MovieLocalizedInformations.Where(d=> myListOfIds.Contains(d.LocationId)));
Ok,
For efficiency reason, I decided to go with custom DTO object that fetches the localized information and flatten the result.
But, the same problem occurred when my custom DTO needed to reference another custom localized DTO.
Here is how I came to do the same as the .Include( "PropertyName" ) that the ObjectSet offers:
Entity LocalizedMovieCollection
public class LocalizedMovieCollection
{
[Key]
public int Id { get; set; }
public string Name { get; set; } (the result of a sub query based on the language)
[Include]
[Association( "LocalizedMovieCollection_LocalizedMovies", "Id", "MovieCollection_Id" )]
public IEnumerable<LocalizedMovie> Movies { get; set; }
}
Entity LocalizedMovie
public class LocalizedMovie
{
[Key]
public int Id { get; set; }
public string Name { get; set; } (the result of a sub query based on the language)
public int MovieCollection_Id { get; set; }
[Include]
[Association( "LocalizedMovie_LocalizedMovieCollection", "MovieCollection_Id", "Id", IsForeignKey = true]
public LocalizedMovieCollection MovieCollection { get; set; }
}
Then, I've declared two methods: One that returns an IQueryable of LocalizedMovieCollection and the other, an IQueryable of LocalizedMovie. (Note: There must be at least one method that returns each type of entity for the entity to get auto-generated on the Silverlight client)
My goal is to automatically load the MovieCollection associated with a Movie so the method definition to get the movies is as follow:
public IQueryable<LocalizedMovie> GetMovies( string languageCode )
{
return from movie in this.ObjectContext.Movies
join movieLocalizedInfo in this.ObjectContext.MovieLocalizedInformations
on movie equals movieLocalizedInfo.Movie
join movieCollection in this.ObjectContext.MovieCollections
on movie.MovieCollection equals movieCollection
join movieCollectionLocalizedInfo in this.ObjectContext.MovieCollectionLocalizedInformations
on movieCollection equals movieCollectionLocalizedInfo.MovieCollection
where movieLocalizedInfo.LanguageCode == languageCode && movieCollectionLocalizedInfo.LanguageCode == languageCode
select new LocalizedMovie()
{
Id = movie.Id,
Name = movieLocalizedInfo.Name
MovieCollection_Id = movieCollection.Id,
MovieCollection = new LocalizedMovieCollection(){ Id = movieCollection.Id, Name = movieCollectionLocalizedInfo.Name }
}
}
When the Silverlight client loads the query, all the LocalizedMovies and their associated LocalizedMovieCollections will be loaded into the context.