Getting null value from Web api - c#

I am trying to store a List of strings that are members of a 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 List<string> Members { get; set; }
public Conversation(string key, string name, string admin, List<string> members)
{
Key = key;
ConversationName = name;
Administrator = admin;
Members = members;
}
public Conversation()
{
}
}
I am using Postman to make a Post request to the URI which seems to work fine, returning 201 Created and giving a json object containing the correct info.
This is the json I am posting:
{
"Key": "123",
"ConversationName": "Test",
"Administrator": "Ken",
"Members": ["y#12.com", "f#78.com"]
}
However, when I try to get the conversation in a GET method this is the result:
{
"Key": "123",
"ConversationName": "Test",
"Administrator": "Ken",
"Members": null
}
These are my functions in my controller:
Post:
[HttpPost]
//[ResponseType(typeof(Conversation))]
public async Task<IHttpActionResult> PostConversation(Conversation convo)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
db.Conversations.Add(convo);
await db.SaveChangesAsync();
return CreatedAtRoute("DefaultApi", new { name = convo.Key }, convo);
}
GET:
[HttpGet]
public IQueryable<Conversation> GetConversations()
{
return db.Conversations;
}
Any help would be much appreciated. Thanks!

The post basically returns the entity as you offer it, which is with Members, apparently.
The get loads a Conversation from the database. It can't have any Members at that moment, because Members is a List<string>, which can't possibly be mapped to a database column.
You probably expect the post to store the Members into the database. This doesn't happen, because you should use Member entities in stead of strings. Member can be a simple class having an Id and a string property.

Related

JsonIgnore attribute is not working in asp.net

My model is
using Newtonsoft.Json;
...
public class User
{
public int Id { get; set; }
public string Login { get; set; }
public string Password { get; set; }
[JsonIgnore]
public string Email { get; set; }
public User()
{
}
}
and controller method is
[HttpPost]
public async Task<ActionResult<User>> PostUser(User user)
{
_context.Users.Add(user);
await _context.SaveChangesAsync();
return CreatedAtAction("GetUser", new { id = user.Id }, user);
}
I am using adding newtonsoft to a project in Program.cs like that:
builder.Services.AddControllers()
.AddNewtonsoftJson(options =>
options.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore
);
And when i try to post user in postman, i getting validation error neither i am including email field or not:
"errors": {
"Email": [
"The Email field is required."
]
},
I tried different ways of including newtonsoft in Program.cs, also i tried to make my model with [DataContract] excluding email field. If i remove builder.Services.AddMvc().AddNewtonsoftJson() then with email field validation is passing, but without it still failing. using System.Text.Json.Serialization not working too. What can i do?
I fixed validation problem after making string nullable,
public string? Email { get; set; }

Q: How to form LINQ query to access nested attributes within class to display on Blazor WASM application

My apologies for the vast amount of code, but it is necessary for the context of the problem. I am faced with an interesting dilemma that I have not been able to solve. I am trying to access information from model called Repository. Repository contains nested classes and lists, and looks like this:
{
public User User { get; set; }
}
public class User
{
public PinnedItems PinnedItems { get; set; }
}
public class PinnedItems
{
public List<Nodes> Nodes { get; set; }
}
public class Nodes
{
public string Name { get; set; }
public string Description { get; set; }
public string Url { get; set; }
public RepositoryTopics RepositoryTopics { get; set; }
}
public class RepositoryTopics
{
public List<TopicNodes> Nodes { get; set; }
}
public class TopicNodes
{
public Topic Topic { get; set; }
}
public class Topic
{
public string Name { get; set; }
}
I have the following method within a web api controller. It is responsible for grabbing my github repositories using graphql. This method looks like this:
{
var request = new GraphQLHttpRequest
{
Query = #"query($username: String!){
user(login: $username) {
pinnedItems(first: 6, types: REPOSITORY) {
nodes {
... on Repository {
name
description
url
repositoryTopics(first:6){
nodes{
topic{
name
}
}
}
}
}
}
}
}
",
Variables = new
{
username = _configuration.GetSection("GithubUserName").Value
}
};
var graphQlResponse = await CreateClient().SendQueryAsync<Repository>(request);
var repo = new Repository
{
User = graphQlResponse.Data.User
};
return Ok(repo);
}
repo is of type Repository.
This is an example piece of JSON that comes back from testing the controller in swagger.
"pinnedItems": {
"nodes": [
{
"name": "personal-website",
"description": "My personal website",
"url": "https://github.com/personal-website",
"repositoryTopics": {
"nodes": [
{
"topic": {
"name": "blazor-webassembly"
}
},
{
"topic": {
"name": "web-api"
}
},
{
"topic": {
"name": "contentful-api"
}
},
{
"topic": {
"name": "contentful"
}
}
]
}
}
I am accessing the code in my blazor component with the following:
Repository SoftwareRepos = new Repository();
protected async override Task OnInitializedAsync()
{
SoftwareRepos = await graphQLquery.GetRepositories();
}
}
And some example code such as this gets me the list of projects as a name.
#foreach(var name in SoftwareRepos.User.PinnedItems.Nodes.Select(x => x.Name).ToArray())
{
#name
}
PRINTS OUT: name, name, name, name
Ideally I would want something that looks like this:
Project One, Description, URL, html, css, react, javascript (a list of tags)
I am having trouble trying to construct LINQ queries to access this nested information (particularly repositoryTopic -> TopicNodes -> Nodes -> Topics -> Name.
I am seeking advice on how to approach this situation, or maybe some alternative solutions to what I am doing as I suspect I am a little out of my depth here. I am using graphql.client to send and retrieve information from github.
first thing to do is to deserialize that JSON into a class structure that it represents.
public class GitResponse{
public Node[] PinnedItems {get;set;}
}
public class Node{
public string Name {get;set};
public string Description {get;set;}
....
}
etc. Once this is done the rest is easy , you just walk that tree
deserialize with
System.Text.Json.Serailizer.Deserialize<GitResponse>(json);

POST Request of a JSON Array

I am able to write a controller for posting 1 object to the database in my controller. However I want to now post multiple objects in 1 API call using JSON
My Entity
public class CustomerInvoiceLine: BaseEntity
{
public SchoolFee SchoolFee { get; set; }
public int SchoolFeeId { get; set; }
public string Description { get; set; }
public int Quantity { get; set; }
public int Amount { get; set; }
public int CustomerInvoiceId { get; set; }
}
I have a DTO as follows:
public class CustomerInvoiceLineAddDto
{
public int SchoolFeeId { get; set; }
public string Description { get; set; }
public int Quantity { get; set; }
public int Amount { get; set; }
public int CustomerInvoiceId { get; set; }
}
I have created a Repository:
public async Task AddCustomerInvoiceLineAsync(CustomerInvoiceLine customerInvoiceLine)
{
_context.CustomerInvoiceLines.Add(customerInvoiceLine);
await _context.SaveChangesAsync();
}
And finally the Controller
[HttpPost]
public async Task<ActionResult> AddCustomerInvoiceLine(CustomerInvoiceLineAddDto invoiceLineAddDto)
{
CustomerInvoiceLine invoiceLineDetails = new CustomerInvoiceLine();
_mapper.Map(invoiceLineAddDto, invoiceLineDetails);
await _unitOfWork.CustomerInvoiceLineRepository.AddCustomerInvoiceLineAsync(invoiceLineDetails);
return Ok();
}
The above controller works fine for posting just 1 item in the JSON request.
I then tried to change this to receive a JSON Array.
public async Task<ActionResult> AddCustomerInvoiceLine(CustomerInvoiceLineAddDto invoiceLineAddDto)
{
string json = "";
CustomerInvoiceLine invoiceLineDetails = new CustomerInvoiceLine();
CustomerInvoiceLineAddDto invoiceLines = JsonConvert.DeserializeObject<CustomerInvoiceLineAddDto>( json );
_mapper.Map(invoiceLineAddDto, invoiceLines);
await _unitOfWork.CustomerInvoiceLineRepository.AddCustomerInvoiceLineAsync(invoiceLineDetails);
return Ok();
}
My JSON Request:
[
{
"schoolFeeId": 1,
"customerInvoiceId": 18,
"description": "School Fees for 2022",
"quantity": 1,
"amount": 5000
},
{
"schoolFeeId": 1,
"customerInvoiceId": 18,
"description": "School Fees for 2021",
"quantity": 1,
"amount": 3000
}
]
Could somebody please assist with understanding how to deserialise the JSON body and then process to the database?
If you want to accept an array, make sure the controller accepts an array. Then automapper is smart enough to convert an array of one type to an array of another type.
public async Task<ActionResult> AddCustomerInvoiceLine(CustomerInvoiceLineAddDto[] invoiceLinesAddDto)
{
var invoiceLineDetails = _mapper.Map<List<CustomerInvoiceLine>>(invoiceLinesAddDto);
await _unitOfWork.CustomerInvoiceLineRepository.AddCustomerInvoiceLinesAsync(invoiceLineDetails);
return Ok();
}
In your unit of work, add a function to handle the adding of a list
public async Task AddCustomerInvoiceLinesAsync(IEnumerable<CustomerInvoiceLine> customerInvoiceLines)
{
_context.CustomerInvoiceLines.AddRange(customerInvoiceLines);
await _context.SaveChangesAsync();
}
Be advised, you can't make a single controller method to handle both the array request and the single item request. I would suggest to define two distinct endpoint so consumers of your api knows which needs an array and which one not.

Web API HTTPGet for multiple attributes?

We have a Web API written in DotNet Core 3.1.402 (I am new to DotNet Core and WebAPI).
We use SqlKata for Database processing.
We have an Account model that has AccountID, AccountName, AccountNumber, etc.
We would like to get an Account by different attributes, for ex: by AccountID, by AccountName, by AccountNumber.
How can we do that so that we don't need a separate HttpGet for each attribute (so we don't have to repeat the same code for different attributes) ?
This is our HttpGet in the AccountsController to get the account by AccountID
public class AccountsController : ControllerBase
{
private readonly IAccountRepository _accountRepository;
[HttpGet("{AccountID}")]
public Account GetAccount(int AccountID)
{
var result = _accountRepository.GetAccount(AccountID);
return result;
}
This is the code in the AccountRepository.cs
public Account GetAccount(int accountID)
{
var result = _db.Query("MyAccountTable").Where("AccountID", accountID).FirstOrDefault<Account>();
return result;
}
This is the Account class
namespace MyApi.Models
{
public class Account
{
public string AccountID { get; set; }
public string AccountName { get; set; }
public string AccountNumber { get; set; }
// other attributes
}
}
Thank you.
Doing it with GET can be a pain, there are ways to pass on the path/query arrays and complex objects but are ugly, the best you can do is to use POST instead of GET and pass an object with the filters that you want.
//In the controller...
[HttpPost]
public Account GetAccount([FromBody]Filter[] DesiredFilters)
{
var result = _accountRepository.GetAccount(DesiredFilters);
return result;
}
//Somewhere else, in a shared model...
public class Filter
{
public string PropertyName { get; set; }
public string Value { get; set; }
}
//In the repository...
public Account GetAccount(Filter[] Filters)
{
var query = _db.Query("MyAccountTable");
foreach(var filter in Filters)
query = query.Where(filter.PropertyName, filter.Value);
return query.FirstOrDefault<Account>();
}
Now you can send a JSON array on the request body with any filters that you want, per example:
[
{ "PropertyName": "AccountID", "Value": "3" },
{ "PropertyName": "AccountName", "Value": "Whatever" }
]

Return JSON from API method

I am using ASP.net Core web api (c#) here
I have a JSON string as:
{
"userId":321,
"account":"new
"fname":"Adam",
"lname":"Silver"
"features":[
{
"available":true,
"status":open,
"admin":false
}
]
}
I want to test this data in my angular code so wanted to hardcode this into my API; then I want my API to return this back. What I am finding it hard is how to return this. Shall I return this as a string or need to parse it?
I have this method in my API:
[HttpGet]
public ActionResult<IEnumerable<string>> Get()
{
return new string[] { "value1", "value2" };
}
Do I need to represent this into string or parse it someway?
Your JSON is invalid. We need to correct it. JSONLint can be helpful for that. I took your JSON and corrected the syntax errors until I got this:
{
"userId": 321,
"account": "new",
"fname": "Adam",
"lname": "Silver",
"features":[
{
"available": true,
"status": "open",
"admin": false
}
]
}
Then I need to generate a C# class structure to represent this JSON. I could manually create it, but the excellent json2csharp.com can generate it for me quickly. I fed this JSON into and received the following classes back:
public class Feature
{
public bool available { get; set; }
public string status { get; set; }
public bool admin { get; set; }
}
public class RootObject
{
public int userId { get; set; }
public string account { get; set; }
public string fname { get; set; }
public string lname { get; set; }
public List<Feature> features { get; set; }
}
I put these class definitions into my application. Then I need to modify my action method to create an instance of this RootObject class (you should change the name to actually match what it's intended for).
[HttpGet]
public ActionResult<RootObject> Get()
{
// Create an instance of our RootObject and set the properties
var myRootObject = new RootObject();
myRootObject.userId = 321;
myRootObject.account = "new";
myRootObject.fname = "Adam";
myRootObject.lname = "Silver";
myRootObject.features = new List<Feature>();
// Create an instance of a feature and set its properties
var feature = new Feature();
feature.available = true;
feature.status = "open";
feature.admin = false;
// Add the new feature to the features collection of our RootObject
myRootObject.features.Add(feature);
// Return the instance of our RootObject
// The framework will handle serializing it to JSON for us
return myRootObject;
}
Note that I changed the signature of your method. I made it no longer accept an IEnumerable because it wasn't clear why you had that. And I changed it to return an ActionResult after checking Microsoft's documentation.
Hi Please find correct JSON format for above one:
{
"userId": 321,
"account": "new",
"fname": "Adam",
"lname": "Silver",
"features": [{
"available": true,
"status": "open",
"admin": false
}]
}
you can use below class in your web API to pass respective data
public class Feature
{
public bool available { get; set; }
public string status { get; set; }
public bool admin { get; set; }
}
public class RootObject
{
public int userId { get; set; }
public string account { get; set; }
public string fname { get; set; }
public string lname { get; set; }
public List<Feature> features { get; set; }
}
then at the end, while returning data, convert the respective class object into JSON by serializing that into JSON format.
Hope it will fulfill your requirement.
Putting the comments into an answer:
If you are using ActionResult, I'll assume you are using asp.net mvc. What you want is JsonResult.
[HttpGet]
public JsonResult Get()
{
return new JsonResult
{
Data = new
{
userId = 321,
account = new
{
fname = "Adam",
lname = "Silver",
features = new object[]{
new
{
available = true,
status = "open",
admin = false
}
}
}
},
JsonRequestBehavior = JsonRequestBehavior.AllowGet
};
}

Categories