Duplicate Foreign Key entries being created on insert - EF Core and Web Api 3.1 - asp.net-core

I am currently learning how to work with EF Core with a simple one to many setup, where a user can have many items. In terms of retrieving the data from the tables, this is fine with some DTO models; however, when I try and add a user with multiple items via Postman, I noticed that for each item it had duplicated the user that many times (i.e. a user with 3 items will create 3 items and 3 users):
Postman (POST)
{
"username": "xxx",
"dob": "xxx",
"location": "xxx",
"items":[{
"item": "xxx",
"category": "xxx",
"type": "xxx"
},
{
"item": "xxx",
"category": "xxx",
"type": "xxx"
},
{
"item": "xxx",
"category": "xxx",
"type": "xxx"
}]
}
Context:
namespace TestWebApplication.Database
{
public class MyContext : DbContext
{
public DbSet<User> Users { get; set; }
public DbSet<Items> Items { get; set; }
public MyContext(DbContextOptions<MyContext> options)
: base(options)
{
// erm, nothing here
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<User>()
.HasMany(i => i.Items)
.WithOne(u => u.User);
}
public override int SaveChanges()
{
var entities = from e in ChangeTracker.Entries()
where e.State == EntityState.Added
|| e.State == EntityState.Modified
select e.Entity;
foreach (var entity in entities)
{
var validationContext = new ValidationContext(entity);
Validator.ValidateObject(entity, validationContext);
}
return base.SaveChanges();
}
}
}
Controller:
[HttpPost]
[Route("insertuseranditems")]
public ActionResult InsertUserAndItems(User user)
{
if (ModelState.IsValid)
{
using (MyContext myContext = _myContext as MyContext)
{
myContext?.Users?.Add(user);
int changes = myContext.SaveChanges();
if (changes > 0)
{
return Created("User saved", user);
}
}
}
return Accepted();
}
Items:
namespace TestWebApplication.Database
{
public class Items
{
[Key]
public int ItemId { get; set; }
public string Item { get; set; }
public string Category { get; set; }
public string Type { get; set; }
public virtual User User { get; set; }
}
}
Users:
namespace TestWebApplication.Database
{
public class User
{
[Key]
public int UserId { get; set; }
public string UserName { get; set; }
public string Dob { get; set; }
public string Location { get; set; }
public ICollection<Items> Items { get; set; }
}
}

I revisited my code and changed my Items.cs model to:
namespace TestWebApplication.Database
{
public class Items
{
[Key]
public int ItemId { get; set; }
public string Item { get; set; }
public string Category { get; set; }
public string Type { get; set; }
[ForeignKey("User")] // added this
public int? UserId { get; set; } // added this
public virtual User User { get; set; }
}
}
Now, when I send the above json via Postman, I get multiple items with the same user as a Foreign Key.
The site below seemed to help:
The ForeignKey Attribute

Related

Unable to parse JSON from Telnyx Api

I am developing an endpoint in C# to accept JSON posted from an external provider (Telnyx). Here is a sample of the data:
{
"data": {
"event_type": "fax.received",
"id": "e15c28d4-147e-420b-a638-2a2647315577",
"occurred_at": "2021-11-19T16:37:02.863682Z",
"payload": {
"call_duration_secs": 35,
"connection_id": "1771912871052051547",
"direction": "inbound",
"fax_id": "2a168c93-3db5-424b-a408-b70a3da625bc",
"from": "+12399999999",
"media_url": "https://s3.amazonaws.com/faxes-prod/999",
"page_count": 1,
"partial_content": false,
"status": "received",
"to": "+12399999999",
"user_id": "dc6e79fa-fe3b-462b-b3a7-5fb7b3111b8a"
},
"record_type": "event"
},
"meta": {
"attempt": 1,
"delivered_to": "https://webhook.site/27ef892c-c371-4976-ae22-22deea57080e"
}
}
I have verified this is valid JSON through https://jsonlint.com/. I created a model:
public class myDeserializedClass
{
public class Payload
{
public int call_duration_secs { get; set; }
public string connection_id { get; set; }
public string direction { get; set; }
public string fax_id { get; set; }
public string from { get; set; }
public string media_url { get; set; }
public int page_count { get; set; }
public bool? partial_content { get; set; }
public string status { get; set; }
public string to { get; set; }
public string user_id { get; set; }
}
public class Data
{
public string event_type { get; set; }
public string id { get; set; }
public DateTime occurred_at { get; set; }
public Payload payload { get; set; }
public string record_type { get; set; }
}
public class Meta
{
public int attempt { get; set; }
public string delivered_to { get; set; }
}
public class Root
{
public Data data { get; set; }
public Meta meta { get; set; }
}
}
The controller being posted to looks like:
[HttpPost]
public IActionResult InboundFax(myDeserializedClass json)
{
try
{
Root myDeserializedClass = JsonConvert.DeserializeObject<Root>(json.ToString().Trim());
return Content("OK");
}
catch(Exception ex)
{
return Content(ex.ToString());
}
}
I am receiving the error: Newtonsoft.Json.JsonReaderException: Unexpected character encountered while parsing value: K. Path '', line 0, position 0. each time the API tries to post to my endpoint. I have also tried posting data using Postman and receive the same error message. Additionally, there are examples of JSON posting on the API website at https://developers.telnyx.com/docs/v2/programmable-fax/tutorials/receive-a-fax-via-api. Since my application fails with both postman and real-time API calls, I'm am working on the assumption the problem is my code, but can't be 100% certain and don't know how to fix it. This is a mission critical problem that I need to solve. Any help would be appreciated.
First of all, the class is bad. Should be:
public class Payload
{
public int call_duration_secs { get; set; }
public string connection_id { get; set; }
public string direction { get; set; }
public string fax_id { get; set; }
public string from { get; set; }
public string media_url { get; set; }
public int page_count { get; set; }
public bool? partial_content { get; set; }
public string status { get; set; }
public string to { get; set; }
public string user_id { get; set; }
}
public class Data
{
public string event_type { get; set; }
public string id { get; set; }
public DateTime occurred_at { get; set; }
public Payload payload { get; set; }
public string record_type { get; set; }
}
public class Meta
{
public int attempt { get; set; }
public string delivered_to { get; set; }
}
public class myDeserializedClass
{
public Data data { get; set; }
public Meta meta { get; set; }
}
It depends on the data that you are getting, but if you are getting the object, you don't need to convert it to work:
[HttpPost]
public IActionResult InboundFax(myDeserializedClass json)
{
try
{
//Work directly with json as object, forget "root" is: myDeserializedClass
return Content("OK");
}
catch(Exception ex)
{
return Content(ex.ToString());
}
}
or if you are getting the json as string:
[HttpPost]
public IActionResult InboundFax(string json)
{
try
{
//Work directly with json as object
myDeserializedClass myInstance= JsonConvert.DeserializeObject<myDeserializedClass>(json);
return Content("OK");
}
catch(Exception ex)
{
return Content(ex.ToString());
}
}
UPDATE AFTER TESTING IT:
I test it with a dummy controller:
[HttpPost]
public ActionResult InboundFax(myDeserializedClass json)
{
try
{
//Just dummy test
if (json.meta.attempt == 1)
{
return Content("OK");
}
else {
return Content("NO");
}
//Work directly with json as object, forget "root" is: myDeserializedClass
}
catch (Exception ex)
{
return Content(ex.ToString());
}
}
in a HomeController (blank template from MVC Web just dummy)
So posting to:
https://localhost:44334/Home/InboundFax
METHOD: POST
With the following data:
{
"data": {
"event_type": "fax.received",
"id": "e15c28d4-147e-420b-a638-2a2647315577",
"occurred_at": "2021-11-19T16:37:02.863682Z",
"payload": {
"call_duration_secs": 35,
"connection_id": "1771912871052051547",
"direction": "inbound",
"fax_id": "2a168c93-3db5-424b-a408-b70a3da625bc",
"from": "+12399999999",
"media_url": "https://s3.amazonaws.com/faxes-prod/999",
"page_count": 1,
"partial_content": false,
"status": "received",
"to": "+12399999999",
"user_id": "dc6e79fa-fe3b-462b-b3a7-5fb7b3111b8a"
},
"record_type": "event"
},
"meta": {
"attempt": 1,
"delivered_to": "https://webhook.site/27ef892c-c371-4976-ae22-22deea57080e"
}
}
Little quickwatch you see it map everything:
Could mean the POSTMAN is wrong configurated?
I use the following header:
Content-Type: application/json
I'm using TALEND API TESTER for Chrome, but every REST client is similar
With POSTMAN, same result, OK. Check for body: raw, type JSON, and header with the content type applicantion/json
Well, I am not sure if I have an answer or not, however, I did manage to get the application working by changing the endpoint to a WebApi instead of a MVC controller. I was under the impression a MVC controller could accept json data, however, I was unable to ever get it working. Once I changed it, everyting worked perfectly.

How Web API Return Nested JSON Values by HttpPost call from database?

I dont have any problems but i don't know how API call data Nested Values
My Web API have Controller, Models and DataAccess that i call by use sql to call data
I want API call JSON Data format similar as below :
[
{
"Agent_Code": "123456",
"Name": "Miss Sara Manger",
"NickName": "Sara",
"BirthDay": "19690825",
"CardID": "9999999999",
"Address": "870 Goldleaf Lane Lyndhurst NJ New Jersey 07071",
"Mobile": "000000000",
"Email": "utv9hgn3h0k#classesmail.com",
"Bank": [
{
"AcctName": "Miss Sara Manger",
"Acctno": "9999999999",
"Bank": "KBANK"
}
]
}
]
Controller:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Web.Http;
namespace Testdata.APIs
{
public class DashController : BaseApiController
{
[HttpPost]
public List<Models.AgentDto> TestJSON(Models.CountDashReq model)
{
DataAccess.DashDAL dal = new DataAccess.DashDAL();
List<Models.AgentDto> models = dal.TestJSON(model);
return models;
}
}
Model AgentDto And BankDto:
public class AgentDto
{
public string Agent_Code { get; set; }
public string Name { get; set; }
public string NickName { get; set; }
public string BirthDay { get; set; }
public string CardID { get; set; }
public string Address { get; set; }
public string Mobile { get; set; }
public string Email { get; set; }
public string Bank { get; set; }
}
public class BankDto
{
public string AcctName { get; set; }
public string Acctno { get; set; }
public string Bank { get; set; }
}
DataAccess:
public List<Models.AgentDto> TestJSON(Models.CountDashReq model)
{
string sql = "[dbo].[tb_Json_Get]";
List<Models.AgentDto> result = new List<Models.AgentDto>();
if (model != null)
{
List<SqlParameter> reqParam = new List<SqlParameter>();
//reqParam.Add(new SqlParameter("#usrLogin", model.usrLogin));
DataSet ds = this.Execute(sql, CommandType.StoredProcedure, reqParam.ToArray(), false);
if (ds.Tables.Count > 0)
{
foreach (DataRow row in ds.Tables[0].Rows)
{
Models.AgentDto paramslist = DataAccessUtility.DataTableExtensions.AsEnumerable<Models.AgentDto>(row);
result.Add(paramslist);
}
}
}
return result;
}
Please help me to use Case-when Thank you very much
If you getting request JSON as:
[
{
"Agent_Code": "123456",
"Name": "Miss Sara Manger",
"NickName": "Sara",
"BirthDay": "19690825",
"CardID": "9999999999",
"Address": "870 Goldleaf Lane Lyndhurst NJ New Jersey 07071",
"Mobile": "000000000",
"Email": "utv9hgn3h0k#classesmail.com",
"Bank": [
{
"AcctName": "Miss Sara Manger",
"Acctno": "9999999999",
"Bank": "KBANK"
}
]
}
]
Then the CountDashReq class should be:
public class Bank
{
public string AcctName { get; set; }
public string Acctno { get; set; }
public string Bank { get; set; }
}
public class CountDashReq
{
public string Agent_Code { get; set; }
public string Name { get; set; }
public string NickName { get; set; }
public string BirthDay { get; set; }
public string CardID { get; set; }
public string Address { get; set; }
public string Mobile { get; set; }
public string Email { get; set; }
public List<Bank> Bank { get; set; }
}
In controller method:
[HttpPost]
public List<Models.AgentDto> TestJSON(IList<Models.CountDashReq> model)
{
// Your code
}

Asp.net core 2 webapi post related data insert

How can I tell EF Core to not insert related data on post ?
this are my models for Deposito and Sucursal
public class IDeposito
{
[Key]
public int Id { get; set; }
public string Descrip { get; set; }
public string Alias { get; set; }
public Boolean Activo { get; set; }
public ISucursal Sucursal { get; set; }
}
public class ISucursal
{
[Key]
public int Id { get; set; }
public string Descrip { get; set; }
public string Alias { get; set; }
public Boolean Activo { get; set; }
}
This is my controller
[HttpPost]
public IActionResult Create([FromBody] IDeposito postData)
{
if (postData == null)
{
return BadRequest();
}
_context.Depositos.Add(postData);
_context.SaveChanges();
return CreatedAtRoute("GetDeposito", new { ID = postData.Id }, postData);
}
When I post this model
{
"Descrip": "Neuquen",
"Alias": "NQN",
"Activo": true,
"Sucursal": {
"Id": 1,
"Descrip": "Comodoro Rivadavia",
"Alias": "CR",
"Activo": true
}
}
It fails because it tries to insert into table "Sucursal", although there already exists a "Sucursal" with id: 1.
Is there anyway I can tell EF Core to not update the related tables ? thank you very much
First approach if you want connect new IDeposito to existing ISucursal with Id = 1 then use:
postData.Sucursal = _context.Sucursals.Find(postData.Sucursal.Id);
_context.Depositos.Add(postData);
_context.SaveChanges();
return CreatedAtRoute("GetDeposito", new { ID = postData.Id }, postData);
If you want add only IDeposito without any sucursal:
postData.Sucursal = null;
_context.Depositos.Add(postData);
_context.SaveChanges();
return CreatedAtRoute("GetDeposito", new { ID = postData.Id }, postData);

NullObjectReference when performing a PUT in Web Api

I am trying to update a variable inside a class "Conversation" put the parameter objects are not binding for some reason. I don't know why not. As a result I am getting this error:
"Object reference not set to an instance of an object.",
Controller:
[HttpPut]
public async Task<IHttpActionResult> AddMember(string key, User user)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
Conversation conver = await db.Conversations.FindAsync(key); //THIS IS NULL
if (conver == null)
{
return NotFound(); //METHOD IS ENDING HERE
}
conver.Members.Add(user); //THIS IS NULL
db.Entry(conver).State = EntityState.Modified;
try
{
await db.SaveChangesAsync();
}
catch (DbUpdateConcurrencyException)
{
return NotFound();
}
return StatusCode(HttpStatusCode.NoContent);
}
Model class User:
namespace AcademicAssistant.Models
{
[DataContract]
public class User
{
[Key]
[DataMember]
public string Email { get; set; }
[DataMember]
public string Password { get; set; }
[DataMember]
public bool Admin { get; set; }
}
}
Model Class Conversation:
[DataContract]
public class Conversation
{
[Key]
[DataMember]
public string Key { get; set; }
[DataMember]
public string ConversationName { get; set; }
[DataMember]
public string Administrator { get; set; }
[DataMember]
public virtual ICollection<User> Members { get; set; }
[DataMember]
public virtual ICollection<Message> Messages { get; set; }
}
I am testing this in Postman like this:
ROUTE = api/Conversations/AddMember/?key="1LM4"
JSON = {"user": { "Email": "56#56.com", "Password" : "Passw-1", "Admin" : true } }
SOLUTION:
The Route should have been
api/Conversations/AddMember/?key=1LM4
and the JSON formatted like this:
{
"Email": "56#56.com",
"Password" : "Passw-1",
"Admin" : true
}

Box V2 API - Where is lock/unlock?

There is a note in the developer road map from December of 2013 saying, "Lock/Unlock – We’ve added support for locking and unlocking files into the V2 API."
I've been all through the V2 API (for c#) and cannot find it anywhere. I expected to find something in the BoxFilesManager class or as something you would pass to UpdateInformationAsync within the BoxFileRequest class.
So is there a way to lock/unlock a file?
Great question. In order to see the current lock status of a file do a
GET https://api.box.com/2.0/files/7435988481/?fields=lock
If there is no lock on the file, you'll get something like this back:
{
"type": "file",
"id": "7435988481",
"etag": "0",
"lock": null
}
If you want to lock a file, you need to do a PUT (update) on the /files/ endpoint with a body that tells us what type of lock, and when to release it. Like this:
PUT https://api.box.com/2.0/files/7435988481/?fields=lock
{"lock": {
"expires_at" : "2014-05-29T19:03:04-07:00",
"is_download_prevented": true
}
}
You'll get a response confirming your lock was created:
{
"type": "file",
"id": "7435988481",
"etag": "1",
"lock": {
"type": "lock",
"id": "14516545",
"created_by": {
"type": "user",
"id": "13130406",
"name": "Peter Rexer gmail",
"login": "prexer#gmail.com"
},
"created_at": "2014-05-29T18:03:04-07:00",
"expires_at": "2014-05-29T19:03:04-07:00",
"is_download_prevented": true
}
}
Since there isn't a lock/unlock yet, I created a Lock Manager based on the existing managers:
class BoxCloudLockManager : BoxResourceManager
{
#region Lock/Unlock Classes
[DataContract]
internal class BoxLockRequestInfo
{
[DataMember(Name = "status")]
public string Status { get; set; }
//[DataMember(Name = "expires_at")]
//public string ExpiresAt { get; set; }
[DataMember(Name = "is_download_prevented")]
public bool IsDownloadPrevented { get; set; }
}
[DataContract]
internal class BoxLockRequest
{
[DataMember(Name = "lock")]
public BoxLockRequestInfo Lock { get; set; }
}
#endregion
const string LockFileString = "{0}/?fields=lock";
public BoxCloudLockManager(IBoxConfig config, IBoxService service, IBoxConverter converter, IAuthRepository auth)
: base(config, service, converter, auth)
{
}
public async Task<BoxLockInfo> LockAsync(string documentId,bool isDownloadPrevented = true)
{
var lockRequest = new BoxLockRequest { Lock = new BoxLockRequestInfo { Status = "lock", IsDownloadPrevented = isDownloadPrevented } };
BoxRequest request = new BoxRequest(_config.FilesEndpointUri, string.Format(LockFileString, documentId))
.Method(RequestMethod.Put)
.Payload(_converter.Serialize(lockRequest));
IBoxResponse<BoxLockInfo> response = await ToResponseAsync<BoxLockInfo>(request).ConfigureAwait(false);
return response.ResponseObject;
}
public async Task<BoxLockInfo> UnlockAsync(string documentId)
{
BoxRequest request = new BoxRequest(_config.FilesEndpointUri, string.Format(LockFileString, documentId))
.Method(RequestMethod.Put)
.Payload("{\"lock\":null}");
IBoxResponse<BoxLockInfo> response = await ToResponseAsync<BoxLockInfo>(request).ConfigureAwait(false);
return response.ResponseObject;
}
public async Task<BoxLockInfo> GetLockInfoAsync(string documentId)
{
BoxRequest request = new BoxRequest(_config.FilesEndpointUri, string.Format(LockFileString, documentId))
.Method(RequestMethod.Get);
IBoxResponse<BoxLockInfo> response = await ToResponseAsync<BoxLockInfo>(request).ConfigureAwait(false);
return response.ResponseObject;
}
}
I derived a class from BoxClient, adding a LockManager and instantiate it within the Constructor.
Here is the Lock Info:
[DataContract]
public class BoxLockedBy
{
[DataMember(Name = "type")]
public string Type { get; set; }
[DataMember(Name = "id")]
public string Id { get; set; }
[DataMember(Name = "name")]
public string Name { get; set; }
[DataMember(Name = "login")]
public string Login { get; set; }
}
[DataContract]
public class BoxLockDetails
{
[DataMember(Name = "type")]
public string Type { get; set; }
[DataMember(Name = "id")]
public string Id { get; set; }
[DataMember(Name = "created_by")]
public BoxLockedBy CreatedBy { get; set; }
[DataMember(Name = "created_at")]
public string CreatedAt { get; set; }
[DataMember(Name = "expires_at")]
public string ExpiresAt { get; set; }
[DataMember(Name = "is_download_prevented")]
public bool IsDownloadPrevented { get; set; }
}
[DataContract]
public class BoxLockInfo
{
[DataMember(Name = "type")]
public string Type { get; set; }
[DataMember(Name = "id")]
public string Id { get; set; }
[DataMember(Name = "etag")]
public string Etag { get; set; }
[DataMember(Name = "lock")]
public BoxLockDetails LockDetails { get; set; }
}