how I can send 2 object to my API (ASP.NET)? - asp.net

I have a problem that I cannot solve. I would like to transmit the deck created by the user to my API but the problem is that the card and the deck are two different entities in my database that's why I need to pass the information of the deck and the list card to my API to add them to my database.
my entity:
public class Card
{
public Card() {
this.Decks = new HashSet<Deck>();
}
public int Id { get; set; }
[Column(TypeName = "json")]
public string Content { get; set; }
[NotMapped]
public virtual ICollection<Deck> Decks { get; set; }
}
public class Deck
{
public Deck() {
this.Cards = new HashSet<Card>();
}
public int Id { get; set; }
public string Name { get; set; }
[DataType(DataType.Date)]
public DateTime CreateAt { get; set; }
public User User { get; set; }
[NotMapped]
public virtual ICollection<Card> Cards { get; set; }
}
public class Join
{
public int DeckId { get; set; }
public Deck Deck { get; set; }
public int CardId { get; set; }
public Card Card { get; set; }
}
my API:
[HttpPost]
public void Add ([FromBody] JsonObject request) {
}
the JSON:
{
"Deck":{
"Name": "",
"CreateAt": "2007-07-15",
"User": "null"
},
"Crads":[
{"content": {}},
{"content": {}}
]
}
Response:
{
"type": "https://tools.ietf.org/html/rfc7231#section-6.5.1",
"title": "One or more validation errors occurred.",
"status": 400,
"traceId": "|99e854f-4f63859a690203b6.",
"errors": {
"$.Deck.User": [
"The JSON value could not be converted to MTG_Deck.Models.User. Path: $.Deck.User | LineNumber: 4 | BytePositionInLine: 22."
]
}
}

I think it is better that in your API you receive a DTO that encapsulates the two classes you need (Deck and Card). Inside your method then take the information you need and save the entities correctly in the database.
public class AddDeckAndCardDTO
{
public Deck Deck { get; set; }
public List<Card> Card { get; set; }
}
And the method Add in the API
[HttpPost]
public void Add ([FromBody] AddDeckAndCardDTO request)
{
var card = request.Card;
var deck = request.Deck
// the code to mapped entities
// the code to save your entities
}

Related

.NET web API one to many relation

I'm building a one to many relation web API with .NET and got error 400.
I have two tables:
public class CarBrand
{
public int CarBrandId { get; set; }
public string Name { get; set; }
[JsonIgnore]
public List<CarModel> CarModels { get; set; } = new List<CarModel>();
}
public class CarModel
{
public int CarModelId { get; set; }
public string ModelName { get; set; }
public int CarBrandId { get; set; }
[JsonIgnore]
public CarBrand CarBrand { get; set; } = new CarBrand();
}
My Controller is:
[HttpPost]
public async Task<ActionResult<CarModel>> PostCarModel(CarModel carModel)
{
_context.CarModels.Add(carModel);
await _context.SaveChangesAsync();
return CreatedAtAction("GetCarModel", new { id = carModel.CarModelId }, carModel);
}
But when I make a POST request and try to add CarModel, I get error 400:
CarBrand:[
"This Field is required!"
]
Thanks!

Entity Framework Core disable recursive retrieval of information in .Include() function

The database has tables Machines, Wheels, Characteristics and Pilot. 1 record of the car includes 1 records about Pilot, 4 records of a wheel and several characteristics. In my Models class, it looks something like this:
public class Machines
{
public int Id { get; set; }
public string Description { get; set; }
public string Color { get; set; }
public int Weight { get; set; }
public int Param { get; set; }
public List<Characteristics> characteristics { get; set; }
public List<Wheels> wheels { get; set; }
public Pilot pilot { get; set; }
}
public class Characteristics
{
public int Id { get; set; }
public string Parameter { get; set; }
public string Value { get; set; }
public string Description { get; set; }
public int MachineId { get; set; }
[ForeignKey("MachineId")]
public Machines machine{ get; set; }
}
public class Wheels
{
public int Id { get; set; }
public int Radius { get; set; }
public int Weight { get; set; }
public int MachineId { get; set; }
[ForeignKey("MachineId")]
public Machines machine{ get; set; }
}
public class Pilot
{
public int Id { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public int MachineId { get; set; }
[ForeignKey("MachineId")]
public Machines machine{ get; set; }
}
When I try to download full information about a machine like this:
var car = context.Machines.Where(x => x.Id == 2)
.Include(m=> m.Wheels)
.Include(m=>m.Charateristics)
.Include(m=>m.Pilot)
.FirstOrDefault();
In response, I get a car that contains a pilot, an array of all the characteristics, and an array of wheels. But at the same time, the array of wheels contains again information about the car, which includes information about the wheels (but for the second time without a car).
It looks something like this:
{
"id": 2,
"Description": "",
"Color": "red",
"Weight": 2000,
"Pilot": {
"id": 1,
"Name": "John",
"Description": ""
},
"Wheels": [
{
"id": 7,
"Radius": 14,
"Weight": 5,
"MachineId": 2,
"machine": {
"id": 2,
"Description": "",
"Color": "red",
"Weight": 2000,
"Pilot": {
"id": 1,
"Name": "John",
"Description": ""
},
"Wheels": [
{
"id": 7,
"Radius": 14,
"Weight": 5,
"MachineId": 2
},
...
How do I get information without recursive data?
Also, the request takes a very long time (much longer than 4 separate requests). The Microsoft website says that you can disable the download if you remove the virtual keyword. I cleaned, but it did not help. Also in the context, I prescribed this.ChangeTracker.LazyLoadingEnabled = false; But this did not help either.
I also saw a lot where the use of the .Load() function is mentioned which can speed up the execution of the request, but I did not quite understand how to use it in my case.
UPDATE
Since everyone advises me to include Newtonsoft.Json.ReferenceLoopHandling.Ignore I will give my Startup and context files. In fact, I already have this enabled. Until I added this, I was not able to send a response at all, and after I added it, it began to come as I indicated (See JSON responce upper).
My context:
public DbSet<Machines> machines{ get; set; }
public DbSet<Characteristics> characteristics{ get; set; }
public DbSet<Wheels> wheels{ get; set; }
public DbSet<Pilot> pilot{ get; set; }
public dbContext(DbContextOptions<dbContext> options) : base(options)
{
this.ChangeTracker.LazyLoadingEnabled = false;
}
My Startup:
public void ConfigureServices(IServiceCollection services)
{
...
services.AddDbContext<dbContext>(options => {
options.UseMySql(Configuration.GetConnectionString("DefaultConnection"),
builder =>
{
builder.EnableRetryOnFailure(5, TimeSpan.FromSeconds(10), null);
});
});
services.AddControllers().AddNewtonsoftJson(x =>
x.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore);
}
For asp.net core 2.x,you could use the following code in your Startup.cs:
services.AddMvc().AddJsonOptions(options =>
options.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore
);
For asp.net core 3.x,you need first install Microsoft.AspNetCore.Mvc.NewtonsoftJson then use the following code:
services.AddControllers().AddNewtonsoftJson(options =>
{
options.SerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore;
});
In any implementation like this, with foreign keys, I have used virtual in the following scenarios. It could be that when you don't use virtual the recursive retrieval kicks in?
public virtual ICollection<Characteristics> characteristics{ get; set; }
public int MachineId { get; set; }
[ForeignKey("MachineId")]
public virtual Machines machine{ get; set; }

How to pass multiple entity or json as parameter in .net core api

I am new in programming. I read a tutorial about the .net core API, but I noticed every tutorial only shows how to pass an entity class as parameter. If I want to pass a combination of entity class as below, does it mean I need to submit multiple post request to server?
JSON:
{
"postId": "123",
"postText": "test item",
"items": [
{
"itemId": "t3st",
"postId": null
},
{
"itemId": "t3st3",
"postId": null
}
] }
C#:
public class Post
{
[Key]
public string postId { get; set; }
public string postText { get; set; }
}
public class Item
{
[Key]
public string itemid{ get; set; }
public string postId { get; set; }
}
public IActionResult Post([FromBody] Post post)
{
return Ok(data);
}
public class Post
{
[Key]
public string postId { get; set; }
public string postText { get; set; }
public List<Item> items {get; set;}
}
public class Item
{
[Key]
public string itemid{ get; set; }
public string postId { get; set; }
}
You need to change your object like above
and then you need to return type List<Post>
You can create a viewModel to parse you json to an object as well. like this.
public class Post
{
[Key]
public string postId { get; set; }
public string postText { get; set; }
public IEnumerable<Item> items {get; set;}
}
public class Item
{
[Key]
public string itemid{ get; set; }
public string postId { get; set; }
}
public IActionResult Post([FromBody] Post post)
{
var entity = JsonConvert.DeserializeObject<PostViewModel>(post.ToString())
return Ok(data);
}

JSON.NET error "Cannot deserialize the current JSON array (e.g. [1,2,3]) into type because the type requires a JSON object (e.g. {"name":"value"})"

I have the following WebCleint to call a Restful web service inside my .net console application:-
try
{
using (WebClient wc = new WebClient())
{
wc.Encoding = Encoding.UTF8;
string url = "https://*****/paged?hapikey=*********&properties=website&properties=i_scan&limit=2";//web service url
string tempurl = url.Trim();
var json = wc.DownloadString(tempurl);//get the json
Marketing ipfd = JsonConvert.DeserializeObject<Marketing>(json);//deserialize
}
}
catch (Exception e)
{
//code goes here..
}
where i am using JSON.Net to Deserialize the json object, which will be as follow:-
{
"has-more": true,
"offset": 622438650,
"companies": [
{
"portalId": *******,
"companyId": *****,
"isDeleted": false,
"properties": {
"website": {
"value": "****.net",
"timestamp": 1520938239457,
"source": "CALCULATED",
"sourceId": null,
"versions": [
{
"name": "website",
"value": "*****.net",
"timestamp": 1520938239457,
"source": "CALCULATED",
"sourceVid": [
731938234
]
}
]
}
},
"additionalDomains": [],
"stateChanges": [],
"mergeAudits": []
},
{
"portalId": ******,
"companyId": ******,
"isDeleted": false,
"properties": {
"website": {
"value": "****.***.***",
"timestamp": 1512488590073,
"source": "CALCULATED",
"sourceId": null,
"versions": [
{
"name": "website",
"value": "****.***8.****",
"timestamp": 1512488590073,
"source": "CALCULATED",
"sourceVid": []
}
]
},
"i_scan": {
"value": "Yes",
"timestamp": 1543409493459,
"source": "******",
"sourceId": "**************",
"versions": [
{
"name": "i_scan",
"value": "Yes",
"timestamp": 1543409493459,
"sourceId": *****",
"source": "CRM_UI",
"sourceVid": [],
"requestId": "******"
}
]
}
},
"additionalDomains": [],
"stateChanges": [],
"mergeAudits": []
}
]
}
Here are my classes:-
public class Marketing
{
public Companies companies { get; set; }
}
public class Companies
{
public IList<string> companyId { get; set; }
public IList<Properties> properties { get; set; }
}
public class Properties
{
public IList<Website> website { get; set; }
public IList<I_Scan> i_scan { get; set; }
}
public class Website
{
public string value { get; set; }
}
public class i_Scan
{
public string value { get; set; }
}
but currently i am getting this exception, when i try to de-serialize the JSON object:-
Newtonsoft.Json.JsonSerializationException was caught
HResult=-2146233088
Message=Cannot deserialize the current JSON array (e.g. [1,2,3]) into type 'MMarketing.Companies' because the type requires a JSON object (e.g. {"name":"value"}) to deserialize correctly.
To fix this error either change the JSON to a JSON object (e.g. {"name":"value"}) or change the deserialized type to an array or a type that implements a collection interface (e.g. ICollection, IList) like List<T> that can be deserialized from a JSON array. JsonArrayAttribute can also be added to the type to force it to deserialize from a JSON array.
Path 'companies', line 1, position 49.
Source=Newtonsoft.Json
StackTrace:
so i am not sure why JSON.NET is unable to do the Deserialize correctly, as in my case the classes are compatible with the returned json object??
At a first glance it looks like you switched two properties in Making them a List and vice versa.
public class Marketing
{
public List<Companies> companies { get; set; }
}
Is "companies": [ in the json, while "companyId": *****, is the id as a string, not array. Properties is not an array also, but the property versions of properties is.
public class Companies
{
public string companyId { get; set; }
public Properties properties { get; set; }
}
If I'm coming to json blind I like to use http://json2csharp.com/ to generate my class structure for me
public class Version
{
public string name { get; set; }
public string value { get; set; }
public object timestamp { get; set; }
public string source { get; set; }
public List<object> sourceVid { get; set; }
}
public class Website
{
public string value { get; set; }
public object timestamp { get; set; }
public string source { get; set; }
public object sourceId { get; set; }
public List<Version> versions { get; set; }
}
public class Version2
{
public string name { get; set; }
public string value { get; set; }
public long timestamp { get; set; }
public int sourceId { get; set; }
public string source { get; set; }
public List<object> sourceVid { get; set; }
public int requestId { get; set; }
}
public class IScan
{
public string value { get; set; }
public long timestamp { get; set; }
public int source { get; set; }
public int sourceId { get; set; }
public List<Version2> versions { get; set; }
}
public class Properties
{
public Website website { get; set; }
public IScan i_scan { get; set; }
}
public class Company
{
public int portalId { get; set; }
public int companyId { get; set; }
public bool isDeleted { get; set; }
public Properties properties { get; set; }
public List<object> additionalDomains { get; set; }
public List<object> stateChanges { get; set; }
public List<object> mergeAudits { get; set; }
}
public class Marketing
{
public bool has_more { get; set; }
public int offset { get; set; }
public List<Company> companies { get; set; }
}
var result = JsonConvert.DeserializeObject<Marketing>(json);

ASP.NET Web API with Many-Many relationships

I'm having some trouble with ASP.NET Web API with many-many relationships between models. Here are my models (which I've simplified for brevity):
public class Model1
{
public int Model1ID { get; set; }
public string Name { get; set; }
public virtual ICollection<Model2> Model2s{ get; set; }
public string Self
{
get
{
return string.Format(CultureInfo.CurrentCulture,
"api/model1/{0}", this.Model1ID);
}
set { }
}
}
public class Model2
{
public int Model2ID { get; set; }
public string Name { get; set; }
public virtual ICollection<Model1> Model1s{ get; set; }
public string Self
{
get
{
return string.Format(CultureInfo.CurrentCulture,
"api/model2/{0}", this.Model2ID);
}
set { }
}
}
and my relevant Model1 API controller excerpt:
public class Model1sController : ApiController
{
private ApplicationDbContext db = new ApplicationDbContext();
// GET: api/Model1s
public IQueryable<Model1> GetModel1s()
{
return db.Model1s;
}
...
}
When I navigate to /api/model1s I get a long JSON nested error, here is the innermost Exception message.
There is already an open DataReader associated with this Command which must be closed first.
What I'm trying to achieve is output like this, but I cannot figure out how to get it working.
[{
"Model1ID": 1,
"Name": "Some model 2 name",
"Model2s": [{
"Model2ID": 1,
"Name": "Another model 2 name"
}, {
"Model2ID": 2,
"Name": "Some model 2 name"
}]
}, {
"Model1ID": 2,
"Name": "Another model 1 name",
"Model2s": [{
"Model2ID": 2,
"Name": "Some model 2 name"
}]
}]
What you need is called and associative entity, some devs call them a lookup table. An associative entity will hold the “association” between two other entities. In your case I believe that there is a scaffolding engine that will build the database tables for you based on the classes you create. Someone else may be able to speak to how the scaffolding engine works.
I would create a class called “TvProgramming” and give it properties Name, Id, Host_Id, Host_Name, and List. Now with this set up you can have as many hosts and as many tv shows as you want and still create unique programming schedules.
Adjust the tv show and host objects so that they only have properties that are unique to themselves ie a TvShow will have an name, id, and maybe a length. A host may have name, id, network, and location info however notice that the host object and tv show object have no knowledge of the other, only the associative entity holds knowledge of the relationship between them.
At the end of the day what your api should return is a set of TvProgramming objects that contain the hosts and for each host a list of tv shows… here is an quick example of the class structure I’m talking about, you’ll have to tweak it a bit to fit your needs but it should get started.
namespace YourProjectName.Models
{
public class TvShow
{
public int id { get; set; }
public string name { get; set; }
public TimeSpan length { get; set; }
public string rating { }
public TvShow() { }
}
public class Host
{
public int id { get; set; }
public string name { get; set; }
public string network { get; set; }
public string city { get; set; }
public string state { get; set; }
public string zip { get; set; }
public string country { get; set; }
public Host() { }
}
public class TvProgramming
{
public int id { get; set; }
public string name { get; set; }
public int host_Id { get; set; }
public string host_Name { get; set; }
public List<TvShow> shows { get; set; }
public TvProgramming()
{
this.shows = new List<TvShow>();
}
}
}
As a way of possibly preventing the error you are getting, try modifying your Controller code like this:
public class Model1sController : ApiController
{
// GET: api/Model1s
public IQueryable<Model1> GetModel1s()
{
using (var db = new ApplicationDbContext())
{
return db.Model1s;
}
}
}

Resources