Saving data in many to many relationship using Entity Framework - c#

Please i am new to C# and entity framework, I am working on a projects using web api. I also use postman to test my data and validate them before inserting to database.
My controller Create will accept a json shown below. The JSON object is mapped to my person model, Assets element of the json is a collection from asset model. what i want to do is retrieve all the asset name in the json and check whether they exist in the asset table. if they exist, get the IDs of the asset and save them all to "PersonAsset" table.
NOTE that "PersonAsset" contains "PersonID" & "AssetId"
I have spent over 24 hours trying to solve this problem, i need help please
{
"Id": 0,
"FirstName": "stringFine",
"MiddleName": "test-Sesan",
"LastName": "stringOkay",
"Gender": "Male",
"DateOfBirth": "10-10-2017",
"BirthCertificate": 0,
"Asset": 0,
"WorkTypeId": 2,
"DwellingId": 2,
"HouseholdRoleId": 2,
"HealthFacility": 1,
"Relationship": "string",
"Education": 0,
"MaritalStatusId": 2,
"MobileNumber": "080099813501",
"SettlementTypeId": 2,
"CommunityId": 3,
"SocialGroup": 0,
"Address": "string",
"IsInSchool": 0,
"ReferenceNumber": "100/NSIO/NSR/345660",
"DateInterviewed": "10-10-2017",
"IsActive": true,
"Assets": [
{
"Name": "Testing"
}
],
"SocialGroups": [
{
"Name": "string"
}
]
}
[ResponseType(typeof(PersonModel))]
[ModelValidator]
[HttpPost]
public IHttpActionResult Create(PersonModel model)
{
try
{
var person = Factory.Persons.Create(model);
Service.Persons.Insert(person);
person = Service.Persons.Get(person.Id);
var dto = Factory.Persons.Create(person);
return CreatedAtRoute("DefaultApi", new { id = dto.Id },dto);
}
catch(dbexception){}
How do i accept the values in below JSON and use it in my controller endpoint
"Assets": [
{
"Name": "Testing"
}
],

What does your PersonModel look like? See if the code below helps.
Create Asset model
public class Asset
{
public string Name { get; set; }
}
and then in PersonModel
public class PersonModel
{
public List<Asset> Assets { get; set; }
}

Related

Why is dotnet not reading my request body correctly?

I have a ForumController where I have a function ChangeOrder with a ChangeOrderDto. Here is the code:
[HttpPost("change-order")]
public async Task<ActionResult> ChangeOrder([FromBody] ChangeOrderDto[] forums)
{
foreach (var dto in forums)
{
var forum = await context.Forums.FindAsync(dto.Id);
if (forum is not null)
{
forum.Order = dto.Order;
forum.ParentId = dto.ParentId;
}
}
await context.SaveChangesAsync();
return Ok();
}
public class ChangeOrderDto
{
public int Id { get; set; }
public int Order { get; set; }
public int ParentId { get; set; }
}
However when I post to /api/forum/change-order with json
{
"forums": [
{id: 3, order: 1, parent_id: 1},
{id: 4, order: 2, parent_id: 1}
]
}
I get this error in response:
Validation failed, forums field is required.
I tried adding [FromBody("forums")] but it did not work.
If I understand your requirement correctly, your JSON should look like this:
[
{"id": 3, "order": 1, "parent_id": 1},
{"id": 4, "order": 2, "parent_id": 1}
]
You may need to change "parent_id" to "parentId" as well since you don't specify any custom property name for the JSON parser.
It then would look like this (also formatted for legibility):
[
{
"id": 3,
"order": 1,
"parentId": 1
},
{
"id": 4,
"order": 2,
"parentId": 1
}
]
If you want to validate your JSON, you can also use a service like this one: https://jsonformatter.curiousconcept.com/
Extrapolating on the answers from below your post (Jon Skeet and ewerspej), lets build this payload:
First, You want to send an array of data:
[
]
This array have in your case 2 objects:
[
{
},
{
}
]
Objects you transferring have some properties with values:
[
{
"id": 3,
"order": 1,
"parentId": 1
},
{
"id": 4,
"order": 2,
"parentId": 1
}
]
Take a look especially at the name of parrentId property - I think this should be named like this in the payload instead of parent_id that You used in your payload.

How to read and write to an existing json file using webapi in ASP.NET Web API?

I'm trying to read and write data to an existing JSON fil using an ASP.NET Web API. And then will consume this API in an ASP.NET MVC application.
JSON file:
{
"Students": [
{
"id": 1,
"name": "Ravi",
"department": "IT"
},
{
"id": 2,
"name": "Raj",
"department": "hr"
},
{
"id": 3,
"name": "avi",
"department": "it"
},
{
"id": 4,
"name": "rome",
"department": "HR"
},
{
"id":5,
"name": "virat",
"department": "HR"
},
{
"id":6 ,
"name": "Tushar",
"department": "RM"
}
]
}
Classes:
public class Student
{
public int Id { get; set; }
public string Name { get; set; }
public string Department { get; set; }
}
public class Students
{
public List<Student> students { get; set; }
}
ApiController is getting data from an existing JSON file. If there is any another way do let me know:
[Route("api/[controller]")]
[ApiController]
public class StudentsController : ControllerBase
{
[HttpGet("JSONFile")]
public IEnumerable<Students> Getdata()
{
var webclient = new WebClient();
var json = webclient.DownloadString("C:/Users/tanmay_pawar/source/repos/CRUDAPI/CRUDAPI/output.json");
var student = JsonConvert.DeserializeObject<Students>(json);
yield return student;
}
}
Using this I'm able to fetch the data from the file. But how can I add new data using post method not able to get it.

Deserialize certain nested properties into class

I have a large json file and only want to use certain nested properties, here is the file
{
"type": "champion",
"format": "standAloneComplex",
"version": "6.24.1",
"data": {
"Aatrox": {
"version": "6.24.1",
"id": "Aatrox",
"key": "266",
"name": "Aatrox",
"title": "the Darkin Blade",
"blurb": "Aatrox is a legendary warrior, one of only five that remain of an ancient race known as
the Darkin. He wields his massive blade with grace and poise, slicing through legions in a style
that is hypnotic to behold. With each foe felled, Aatrox's ...",
"info": {
"attack": 8,
"defense": 4,
"magic": 3,
"difficulty": 4
},
"image": {
"full": "Aatrox.png",
"sprite": "champion0.png",
"group": "champion",
"x": 0,
"y": 0,
"w": 48,
"h": 48
},
"tags": [
"Fighter",
"Tank"
],
},
"Ahri": {
"version": "6.24.1",
"id": "Ahri",
"key": "103",
"name": "Ahri",
"title": "the Nine-Tailed Fox",
"blurb": "Unlike other foxes that roamed the woods of southern Ionia, Ahri had always felt a
strange connection to the magical world around her; a connection that was somehow incomplete.
Deep inside, she felt the skin she had been born into was an ill fit for ...",
"info": {
"attack": 3,
"defense": 4,
"magic": 8,
"difficulty": 5
},
"image": {
"full": "Ahri.png",
"sprite": "champion0.png",
"group": "champion",
"x": 48,
"y": 0,
"w": 48,
"h": 48
},
"tags": [
"Mage",
"Assassin"
],
},
It is a very long json file. I have a class that is
public class Champion
{
public string id { get; set; }
public string key { get; set; }
public string name { get; set; }
public string title { get; set;}
}
public class ChampionRoot
{
public Dictionary<string, Champion> champions { get; set; }
}
What I am trying to do is start at the properties inside "Data" and only get "id", "key","name" and "title", and Deserialize those properties into my ChampionRoot class. I have searched and tried many things and cannot get it to work. Here is some of what I've tried in my controller
public IActionResult Champions()
{
var url = #"url.......";
WebClient client = new WebClient();
var download = client.DownloadString(url);
var champions = Newtonsoft.Json.JsonConvert.DeserializeObject<ChampionRoot>(download);
return Json(champions); //This returns null in the view
}
I have also tried using JObject a few different ways and that doesn't work for me either
JObject obj = JObject.Parse(download);
var json = obj["key"]["id"]["name"]["title"]; //throws object reference error
Then I've tried this
JObject obj = JObject.Parse(download);
var champions = Newtonsoft.Json.JsonConvert.DeserializeObject<ChampionRoot>(obj.ToString());
//This also returns null in the view
I have searched and read many answers but I cannot figure it out. The question is how can I only Deserialize those 4 nested properties into an object? The url is http://ddragon.leagueoflegends.com/cdn/6.24.1/data/en_US/champion.json for full json file.
You can use Dictionary<string, Champion> for data property:
public class ChampionRoot
{
public Dictionary<string,Champion> data { get; set; }
}
var champions = Newtonsoft.Json.JsonConvert.DeserializeObject<ChampionRoot>(download);

Removing elements from a dynamic JSON

I have a JSON used as base template which is uploaded in a web platform by the administrator:
{
"age": 0,
"name": "string",
"interest": "string",
"address": "string",
"personalId": 0
}
Then users can create their own JSON schema based on the base template and upload them. These JSON files are all different by each other, but they have in common all the fields in the base template they are derived. The fields can also be in a different order.
In example:
{
"age": 23,
"weight":65,
"name": "Emily",
"gender":"Female",
"interest": "graphic design",
"address": "Elm street",
"personalId": 916742
}
...another:
{
"age": 39,
"name": "John",
"weight": 77,
"interest": "graphic design",
"address": "Elm street",
"gender": "Male",
"personalId": 916742,
"education": "University",
"children": [{
"name": "Katie",
"gender": "Female"
}, {
"name": "Greg",
"gender": "Male"
}]
"someOtherInfo": "lorem ipsum"
}
what I'm trying to do is to remove, from each of the JSON I will receive, all the field which are not present in the base template.
In example from the first entry I will have:
{
"age": 23,
"name": "Emily",
"interest": "graphic design",
"address": "Elm street",
"personalId": 916742
}
The number of fields in the JSON can reach also 300~400 different fields, and the base template contains ~200 fields.
The fields of the base template are all at the first level, no nested fields, and, as I've stored in a database table I could have them also as a list of strings.
Can be achieved using a dynamic object in an efficient way?
Deserialize the json to a class with the variables you are interested in. Anything not part of the class will be ignored
public class Rootobject
{
[JsonProperty("age")]
public int Age { get; set; }
[JsonProperty("name")]
public string Name { get; set; }
[JsonProperty("interest")]
public string Interest { get; set; }
[JsonProperty("address")]
public string Address { get; set; }
[JsonProperty("personalId")]
public int PersonalId { get; set; }
}
Deserialize to the above class. You dont have to worry about removing anything from the json you are getting. once deserialized to the above class, you have the data you are interested in.
Update
You can use JObject.Parse if you dont want to create a class. You can look up the values (check if they exist as well) and create a new object that you can then save to wherever you need.
var obj = JObject.Parse(json);
dynamic objectToSaveToDB = new ExpandoObject();
objectToSaveToDB.age = obj.GetValue("age");
objectToSaveToDB.name = obj.GetValue("name");
objectToSaveToDB.interest = obj.GetValue("interest");
objectToSaveToDB.address = obj["address"].ToString();
objectToSaveToDB.personalId = obj["personalId"].ToString();
// String version would be JsonConvert.SerializeObject(objectToSaveToDB);

PgAdmin Import/Export did not keep foreign keys with Entity Framework

I am including a bunch information but feel free to skip the heading "The Problem" to read the actual issue.
Introduction
In my local database, I built out a schema of tables and populated them with data.
Basic flow:
FORMS table has a one-to-many relationship with the QUESTIONS table
QUESTIONS has a one-to-many relationship with the ANSWERS table.
The QUESTIONS table references the FORMS table with a FormId.
The ANSWERS table references the QUESTIONS with a QuestionId
Here is the code for the FORMS and QUESTIONS table.
public class AppointmentForm
{
[Key]
public long Id { get; set; }
public string FormName { get; set; }
public int Order { get; set; }
public List<AppointmentQuestion> Questions { get; set; }
}
public class AppointmentQuestion
{
[Key]
public long Id { get; set; }
[ForeignKey("FormId")]
public virtual AppointmentForm Form { get; set; }
public long FormId { get; set; }
public int Order { get; set; }
public bool? Required { get; set; } = false;
public string Question { get; set; }
public virtual List<AppointmentAnswer> Answers { get; set; }
}
So, I used PostMan to populate my local database.
Now, this worked out because everything was referenced and maintained with the Entity Framework.
I wrote the code you will see below to send this JSON structure to the client.
{
"forms": [
{
"id": 1,
"formName": "Inclusion",
"order": 1,
"questions": [1, 2]
}
],
"questions": [
{
"id": 1,
"formId": 1,
"order": 1,
"required": true,
"question": "Are you able to go for a walk of at least 15 minutes?",
"answers": [1, 2, 3, 4, 5]
}
],
"answers": [
{
"id": 1,
"questionId": 1,
"typeId": 2,
"label": "Unable to do",
"order": 1
},
{
"id": 2,
"questionId": 1,
"typeId": 2,
"label": "Without much difficulty",
"order": 2
},
{
"id": 3,
"questionId": 1,
"typeId": 2,
"label": "With some difficulty",
"order": 3
},
{
"id": 4,
"questionId": 1,
"typeId": 2,
"label": "With a little difficulty",
"order": 4
},
{
"id": 5,
"questionId": 1,
"typeId": 2,
"label": "Without any difficulty",
"order": 5
}
],
"types": [
{
"id": 1,
"type": "Manual enter"
},
{
"id": 2,
"type": "Multiple choice"
}
]
}
To create this, I would use the following script (or else everything would have been nested)
public async Task<IActionResult> GetModelNormalized()
{
AppointmentModelNormalized Model = new AppointmentModelNormalized();
List<AppointmentForm> Forms = await _formManager.GetAppointmentFormsAsync();
List<AppointmentAnswerType> Types = await _typeManager.GetAppointmentAnswerTypesAsync();
foreach(AppointmentForm f in Forms)
{
AppointmentFormReference _f = new AppointmentFormReference() {
Id = f.Id,
FormName = f.FormName,
Order = f.Order
};
foreach(AppointmentQuestion q in f.Questions)
{
_f.Questions.Add(q.Id);
AppointmentQuestionReference _q = new AppointmentQuestionReference()
{
Id = q.Id,
Question = q.Question,
FormId = q.FormId,
Order = q.Order,
Required = q.Required
};
foreach(AppointmentAnswer a in q.Answers)
{
_q.Answers.Add(a.Id);
AppointmentAnswerReference _a = new AppointmentAnswerReference()
{
Id = a.Id,
Label = a.Label,
Order = a.Order,
QuestionId = a.QuestionId,
TypeId = a.TypeId
};
Model.Answers.Add(_a);
}
Model.Questions.Add(_q);
}
Model.Forms.Add(_f);
}
Model.Types = Types;
return Ok(Model);
}
In my local env, this all worked perfectly.
The Problem
When I went to test QA, I exported my data using PgAdmin4 and then imported the csv file into the QA database.
Now, the line of code with foreach(AppointmentQuestion q in f.Questions) does not work because the FK did not get transferred with the Export/Import.
I do not understand why this happened because the FKs are all the same.
Is there a better way to export and import the data that will keep the FK relationship?
If necessary, I can grab all the questions and answers like I am grabbing the Forms. List<AppointmentForm> Forms = await _formManager.GetAppointmentFormsAsync();
But doesn't linq do that for me? I mean that is the point of a virtual method? It gets created when it gets called, I think so at least. Should I write custom getter and setters?
Attached is the stack trace of the exception. It is a NullReferenceException.
you don't include your related object use following code and for more info read this answer
_context.AppointmentForms.Include(x => x.Questions ).Include(x => x.Questions.Select(q => q.Answers)).ToListAsync();
Edit from Christian4423:
I was able to get this to do the same thing with the syntax.
List<AppointmentForm> Forms = await _context.AppointmentForms
.Include("Questions.Answers")
.ToListAsync();

Categories