Passing an instance with list to an API - asp.net

There is the complete code of the test.
When posting JSON data to the API, the instance person is null.
Can any one tell me why
Model
public class Diploma
{
public int PeronId { get; set; }
public string Tile { get; set; }
public string Organism { get; set; }
}
public class Person
{
public int Id { get; set; }
public string Name { get; set; }
public string Function { get; set; }
public List<Diploma> Diplomas { get; set; }
public Person()
{
Diplomas = new List<Diploma>();
}
}
Controller
public class PersonnesController : ApiController
{
private const string cs = "Data Source=.;Initial Catalog=test;Integrated Security=True";
// POST : Person
[HttpPost]
public int Post([FromBody] Person person)
{
bool res = false;
int id;
SqlCommand cmd = new SqlCommand();
using (SqlConnection connection = new SqlConnection(cs))
{
cmd = new SqlCommand("insert dbo.personnes (Name, Function) output INSERTED.ID values (#name, #function)", connection);
cmd.Parameters.AddWithValue("#nam", person.Name);
cmd.Parameters.AddWithValue("#function", person.Function);
connection.Open();
id = (int)cmd.ExecuteScalar();
foreach (Diploma diploma in person.Diplomas)
{
cmd = new SqlCommand("insert diplomes values(#id, #title, #organism)", connection);
cmd.Parameters.AddWithValue("#id", id);
cmd.Parameters.AddWithValue("#title", diploma.Tile);
cmd.Parameters.AddWithValue("#organism", diploma.Organism);
cmd.ExecuteNonQuery();
}
return id;
}
}
}
I'm testing with RESTClient for Firefox,
In header section:
Content-type : application/x-www-form-urlencoded

The data is being sent using the wrong Content-Type.
It was indicated that data was sent using Content-type : application/x-www-form-urlencoded
while the shown data is JSON
{ "Name": "my name", "Function": "my function", "diplomas": [{ "title": "diploma 1", "organism": "university 1" }, { "title": "diploma 2", "organism": "university 2" }] }
which would use Content-type : application/json content type
The model binder was unable to parse the content using the provided content type, so it default the model to null.

Related

The ConnectionString property has not been initialized.' ASP.NET Core 6

I am trying to connect my ASP.NET Core 6 application to SQL Server, but I get an error on the connection string. Does anyone have experience with .NET 6 that could help?
This is my code in my class (book.cs). I get the ERROR where I commented in the code. And yes I have the right string in the app.json file
public class Book
{
public int BookId { get; set; }
public int Pagecount { get; set; }
public string Title { get; set; }
public string TypeName { get; set; }
public string AutherFirstName { get; set; }
public string AutherLastName { get; set; }
public List<Book> GetBooks(string connectionString)
{
List<Book> bookList = new List<Book>();
SqlConnection con = new SqlConnection (connectionString);
string selectSQL = "select BookId, Title, Isbn, PublisherName, AuthorName, CategoryName from GetBookData";
con.Open(); // Here i get my error
SqlCommand cmd = new SqlCommand(selectSQL, con);
SqlDataReader dr = cmd.ExecuteReader();
if (dr != null)
{
while (dr.Read())
{
Book book = new Book();
book.BookId = Convert.ToInt32(dr["BookId"]);
book.Pagecount = Convert.ToInt32(dr["pagecount"]);
book.Title = dr["Title"].ToString();
book.TypeName = dr["TypeName"].ToString();
book.AutherLastName = dr["AutherLastName"].ToString();
book.AutherFirstName = dr["AutherFirstName"].ToString();
}
}
return bookList;
}
I would say configurations in .NET are annoying stuff but, let's skip that ;)
The usage of options is done after some DI logic. So basically, your problem probably is in how you are trying to get the value from appsettings.{ENV}.json.
You haven't attached this part of the code but when you are calling your method from the Book.cs class, you should do something like that:
new Book().GetBooks(_configuration.GetConnectionString("connStrKey"));
BTW this is an anti-pattern and I strongly recommend you start using the Options Pattern to inject options and settings into your code.
https://learn.microsoft.com/en-us/dotnet/core/extensions/options
So, your code should be changed to something like that:
public class ConnectionStrings
{
public ConnectionStrings()
{
ConnectionString = string.Empty;
}
public string ConnectionString { get; set; }
}
public class BooksRepository
{
private readonly ConnectionStrings connectionStringsOptions;
public BooksRepository(IOptions<ConnectionStrings> connectionOptions)
{
connectionStringsOptions = connectionOptions.Value;
}
public List<Book> GetBooks()
{
List<Book> bookList = new List<Book>();
SqlConnection con = new SqlConnection(connectionStringsOptions.ConnectionString);
string selectSQL = "select BookId, Title, Isbn, PublisherName, AuthorName, CategoryName from GetBookData";
con.Open();
SqlCommand cmd = new SqlCommand(selectSQL, con);
SqlDataReader dr = cmd.ExecuteReader();
if (dr != null)
{
while (dr.Read())
{
bookList.Add(new Book
{
BookId = Convert.ToInt32(dr["BookId"]),
Pagecount = Convert.ToInt32(dr["pagecount"]),
Title = dr["Title"].ToString(),
TypeName = dr["TypeName"].ToString(),
AutherLastName = dr["AutherLastName"].ToString(),
AutherFirstName = dr["AutherFirstName"].ToString()
});
}
}
return bookList;
}
}
public class Book
{
public int BookId { get; set; }
public int Pagecount { get; set; }
public string Title { get; set; }
public string TypeName { get; set; }
public string AutherFirstName { get; set; }
public string AutherLastName { get; set; }
}
Since you are using .net 6, update your program.cs class to add the connection strings as DI Options adding the following line before the "var app = builder.Build();" statement.
builder.Services.Configure<ConnectionStrings>(builder.Configuration.GetSection("ConnectionStrings"));
Example of a appsettings.json section
{
"ConnectionStrings": {
"ConnectionString": "localhost:1433"
}
}
I eventually came to the answer.
Had to change the query in SQL and put part of the code into a try/catch block and it worked.
string ConnectionString = "Data Source=localhost\\SQLEXPRESS;Initial Catalog=RFID_Library;Integrated Security=True; TrustServerCertificate=True";
string errormsg = null;
List<Book> bookList = new List<Book>();
try
{
SqlConnection con = new SqlConnection(ConnectionString);
string selectSQL = "select BookId, Title, pagecount, AutherFirstName, AutherLastName, TypeName from GetBookData";
con.Open();
SqlCommand cmd = new SqlCommand(selectSQL, con);
SqlDataReader dr = cmd.ExecuteReader();
if (dr != null)
{
while (dr.Read())
{
Book book = new Book();
book.BookId = Convert.ToInt32(dr["BookId"]);
book.Pagecount = Convert.ToInt32(dr["pagecount"]);
book.Title = dr["Title"].ToString();
book.TypeName = dr["TypeName"].ToString();
book.AutherLastName = dr["AutherLastName"].ToString();
book.AutherFirstName = dr["AutherFirstName"].ToString();
bookList.Add(book);
}
}
return bookList;
}
catch (Exception)
{
throw;
}

Passing lists to the view .Net Core

Im looping through all the results from the SQL query in a .Net Core project. here is Model
public class Mymessagesinfo
{
public int MyMessagesCount { get; set; }
public List<int> MessagesIDs { get; set; }
public List<int> MessagesSendersid { get; set; }
public List<string> MessagesSenders { get; set; }
public List<string> MessagesTitles { get; set; }
public List<string> MessagesInformation { get; set; }
public List<string> MessagesStatus { get; set; }
}
I loop through the users messages in my controller then i pass that model to the view
sqlcon.Open();
int? userid = HttpContext.Session.GetInt32("UserID");
SqlCommand sqlcom = new SqlCommand("select * from messages where Messagereceiver=" +userid , sqlcon);
SqlDataReader reader = sqlcom.ExecuteReader();
if(reader.HasRows)
{
int index = 0;
while(reader.Read())
{
string s;
s = reader[0].ToString();
Mymessages.MessagesIDs.Add(int.Parse(s));
Mymessages.MessagesSendersid.Add(int.Parse(reader[1].ToString()));
Mymessages.MessagesTitles.Add(reader[3].ToString());
Mymessages.MessagesInformation.Add(reader[4].ToString());
Mymessages.MessagesStatus.Add(reader[5].ToString());
index++;
}
Mymessages.MyMessagesCount = index;
}
the very first line Mymessages.MessagesIDs.Add(int.Parse(s)); it throws an exception saying System.NullReferenceException: 'Object reference not set to an instance of an object
i wanted to make sure that reader was holding the results so i added int s and checked on it and it was holding the value it was supposed to.
whats going wrong here? is this how we are supposed to pass list-like data to the view?
You need to initlize MessagesIDs in entity Mymessages, like this:
var Mymessages = new Mymessagesinfo()
{
MessagesIDs = new List<int>()
};
Mymessages.MessagesIDs.Add(id);
Or just define the class like this,
public class Mymessagesinfo
{
public int MyMessagesCount { get; set; }
public List<int> MessagesIDs { get; set; } = new List<int>();
public List<int> MessagesSendersid { get; set; } = new List<int>();
public List<string> MessagesSenders { get; set; } = new List<string>();
public List<string> MessagesTitles { get; set; } = new List<string>();
public List<string> MessagesInformation { get; set; } = new List<string>();
public List<string> MessagesStatus { get; set; } = new List<string>();
}
Here is how I would restructure what you have to make it work.
First, your model class:
public class Mymessagesinfo
{
public List<MessageInfo> Messages { get; set; } = new List<MessageInfo>();
}
public class MessageInfo
{
public int ID { get; set; }
public int Senderid { get; set; }
public string Sender { get; set; }
public string Title { get; set; }
public string Information { get; set; }
public string Status { get; set; }
}
With this approach you have a list of message objects, instead of a bunch of lists containing property data.
Here is how I would suggest you load it from SQL Server:
var data = new Mymessagesinfo();
int? userid = HttpContext.Session.GetInt32("UserID");
var messagesTable = new System.Data.DataTable("messages");
using (var sqlcom = sqlcon.CreateCommand())
{
sqlcom.CommandText = $"select * from messages where Messagereceiver='{userid}'";
using (var adapter = new SqlDataAdapter(sqcom))
{
adapter.Fill(messagesTable);
}
}
// we are now done with SQL and have the data in memory...
foreach(DataRow row in messagesTable.Rows)
{
data.Messages.Add( new MessageInfo {
ID = row.Field<int>(0),
Senderid = row.Field<int>(1),
Sender = row.Field<string>(2),
Title = row.Field<string>(3),
Information = row.Field<string>(4),
Status = row.Field<string>(5),
});
}
return View(data);
This is a lot cleaner and by using a DataAdapter and DataTable you minimize the amount of time that the connection to the database is connected.
Here is how you would use this model in an MVC View:
#Model Mymessagesinfo
<div>
<!-- This is where you can display the properties of the message. //-->
<ul>
#foreach(var message in Model.Messages)
{
<li> #message.Title - #message.Id </li>
}
<ul>
<div>

how to pass json array information to net core web api

I am building a web api with net core, I need to use the post method to save the information of a json file, the drawback is that the json file has an array and I do not know how to send the information to the stored procedure in sql so that it save the information the json file is this
"buyer": { //
"firstName": "fabio",
"lastName": "gomez",
"documentType": 0, // Puede ser 0: DNI, 1: Pasaporte o 2: Carnet de Extranjeria
"documentNumber": "01234567", // Permitir letras para pasaportes extranjeros
"phoneNumber": "51912345678",
"email": "hnos#gmail.com"
},
"passengers": [{ // En el primer pasajero, pueden repetirse los datos del comprador
"seat": 1,
"firstName": "pedro",
"lastName": "peres", // Considerar nueva campo
"secondLastName": "martinez",
"documentType": 0, // Puede ser 0: DNI, 1: Pasaporte o 2: Carnet de Extranjeria
"documentNumber": "15588",
"age": 42,
"gender": 0, // Puede ser 0: Masculino, 1: Femenino
},
{
"seat": 2,
"firstName": "Adriana",
"lastName": "gomez",
"secondLastName": "lopez",
"documentType": 1, // Puede ser 0: DNI, 1: Pasaporte o 2: Carnet de Extranjeria
"documentNumber": "XY01234567",
"age": 40,
"gender": 1, // Puede ser 0: Masculino, 1: Femenino
}
],
This json must enter the system via post, which I use the following driver
[HttpPost]
public async Task<IActionResult> Post([FromBody] List<string> valores)
{
await _repository.Insert(valores);
return Ok();
}
try doing something with ado.net but it doesn't work
public async Task Insert(List<string> parametros)
{
using (SqlConnection sql = new SqlConnection(_connectionString))
{
using (SqlCommand cmd = new SqlCommand("sp_insertarpasajero", sql))
{
cmd.CommandType = System.Data.CommandType.StoredProcedure;
//cmd.Parameters.Add(new SqlParameter("#Id", parametros));
await sql.OpenAsync();
await cmd.ExecuteNonQueryAsync();
return;
}
}
}
I do not know how to pass the information from the controller that receives the post to an object or datatable and then use it in a stored procedure I appreciate your collaboration
For you json file , you should create a model object to accept the json data .The following is my working demo , you could refer to and make the modification on your project:
Model
public class Buyer
{
public int Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public int DocumentType { get; set; }
public string DocumentNumber { get; set; }
public string PhoneNumber { get; set; }
public string Email { get; set; }
}
public class Passenger
{
public int Id { get; set; }
public int Seat { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string SecondLastName { get; set; }
public int DocumentType { get; set; }
public string DocumentNumber { get; set; }
public string Age { get; set; }
public int Gender { get; set; }
}
public class OrderInfo
{
public Buyer Buyer { get; set; }
public List<Passenger> Passengers { get; set; }
}
Stored procedure , use OPENJSON (Transact-SQL)
CREATE PROCEDURE [dbo].[sp_insertpassenger]
#json NVARCHAR(max)
AS
INSERT INTO Passenger(
Seat,FirstName,LastName,SecondLastName,
DocumentType,DocumentNumber,Age,Gender)
Select *
FROM OPENJSON(#json ,'$.Passengers')
WITH(
Seat int '$.Seat',
FirstName nvarchar(50) '$.FirstName',
LastName nvarchar(50) '$.LastName',
SecondLastName nvarchar(50) '$.SecondLastName',
DocumentType int '$.DocumentType',
DocumentNumber nvarchar(50) '$.DocumentNumber',
Age int '$.Age',
Gender int '$.Gender' );
RETURN 0
Controller and repository method
[HttpPost]
public async Task<IActionResult> Post([FromBody] OrderInfo values)
{
await _repository.Insert(values);
return Ok();
}
public async Task Insert(OrderInfo parameter)
{
using (SqlConnection sql = new SqlConnection(connectionString))
{
using (SqlCommand cmd = new SqlCommand("sp_insertpassenger", sql))
{
cmd.CommandType = System.Data.CommandType.StoredProcedure;
var json = JsonConvert.SerializeObject(parameter);
cmd.Parameters.Add(new SqlParameter("#json", json));
await sql.OpenAsync();
await cmd.ExecuteNonQueryAsync();
return;
}
}
}
Result

ASP.NET & Json - simple hierarchy

I have a simple ASP.NET Class with 4 properties. What I'd like to to is package this class into a JSON string and add a "data" parent at the top level. What's the best way to add in the "data" element at the top level?
using Newtonsoft.Json;
[DataContract]
public class Task
{
[DataMember(Name = "name")]
public string Name { get; set; }
[DataMember(Name = "notes")]
public string Notes { get; set; }
}
static void Main(string[] args)
{
var task = new Task();
task.Name = "Test Task";
task.Notes = "Test task created by ASP.NET";
HttpClient client = new HttpClient();
client.BaseAddress = new Uri("https://example.com/api");
HttpResponseMessage response = client.PostAsJsonAsync<Task>("tasks", task).Result;
response.EnsureSuccessStatusCode();
string responseBody = response.Content.ReadAsStringAsync().Result;
Console.WriteLine(responseBody);
Console.ReadLine();
}
The Json I am getting back from this code is the following:
{
"name": "Test Task",
"notes": "Test task created by ASP.NET",
}
What I'd like is the following:
{
"data": {
{
"name": "Test Task",
"notes": "Test task created by ASP.NET",
}
}
You could create a simple dictionary:
public class Task
{
public Task
{
Data = new Dictionary<string,string>();
}
[DataMember(Name = "data")]
public Dictionary<string,string> Data { get; private set; }
}
Populate like:
Task t =new Task();
t.Data.Add("name","test task");
t.Data.Add("notes","teet");
Yes you could create another class and embed it like this also:
public class Data
{
[DataMember(Name = "name")]
public string Name { get; set; }
[DataMember(Name = "notes")]
public string Notes { get; set; }
}
public class Task
{
public Task
{
Data = new Data();
}
[DataMember(Name = "data")]
public Data Data { get; private set; }
}
You could use generics for an abstracted Data
public class Task<T> where T: class , new()
{
public Task
{
Data = new T();
}
[DataMember(Name = "data")]
public T Data { get; private set; }
}
If your service provider is wcf (i guess it is wcf), change the BodyStyle of the method to WebMessageBodyStyle.Bare as mentioned below.
BodyStyle = WebMessageBodyStyle.Bare
it will not wrap the result.

How to retrieve the text from a cell in a dynamically generated gridview

I want to get the book name which is in the first column of gridview. I used Template field and added a link button in it. Book name is retrieved from database, thus dynamically wriiten. I want open the selected book in pdf in another page.
public class Book
{
public string Bookn{get;set;}
public string Auth { get; set; }
public string Category { get; set; }
public string Edi { get; set; }
public string Yopub { get; set; }
public string Avai { get; set; }
public string img { get; set; }
}
public class GetData
{
public static List<Book> Getallrecord(string author)
{
List<Book> ob1 = new List<Book>();
SqlConnection con;
SqlCommand com;
string cs = ConfigurationManager.ConnectionStrings["SDL_DBConnectionString"].ConnectionString;
using (con = new SqlConnection(cs))
{
com = new SqlCommand("searchByAuthor3", con);
com.CommandType = CommandType.StoredProcedure;
com.Parameters.AddWithValue("#SAuthor", author + "%");
con.Open();
SqlDataReader rd = com.ExecuteReader();
while (rd.Read())
{
Book bo = new Book();
bo.Bookn = rd["BookName"].ToString();
bo.Auth = rd["Author"].ToString();
bo.Category = rd["Category"].ToString();
bo.Yopub = rd["Yopublication"].ToString();
bo.Edi = rd["Edition"].ToString();
bo.Avai = rd["Available"].ToString();
bo.img = rd["Images"].ToString();
ob1.Add(bo);
}
}
return ob1;
}
}
protected void SAButton_Click(object sender, EventArgs e)
{
string author = TAuthor.Text;
if (Session["ClientLogin"] != null)
{
SAGridView.DataSource=GetData.Getallrecord(author);
SAGridView.DataBind();
}
else
{
Server.Transfer("Login.aspx");
}
}
You can add a hyperlink field in your gridview and redirect to your other page like
<asp:HyperLinkField DataNavigateUrlFields="Bookn"
DataNavigateUrlFormatString="View_Book.aspx?BookNo={0}" Text="View Book" />

Resources