Convert DbContext way to SqlCommand way - asp.net

Today I have an App which work using EF Core, with the DbContext class and looks like this:
public class RcoreContext : DbContext
{
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseSqlServer(#"Server=(localdb)\mssqllocaldb;Database=rCoreData.TestDb;Trusted_Connection=True;");
}
public DbSet<Hosts> Hosts { get; set; }
public DbSet<Route> Route { get; set; }
}
public class Hosts
{
public int HostsId { get; set; }
public string Host { get; set; }
[NotMapped]
public Dictionary<object, object> Datas { get; set; }
public string DatasJson { get { return JsonConvert.SerializeObject(Datas); } set { Datas = JsonConvert.DeserializeObject<Dictionary<object, object>>(value); } }
}
public class Route
{
public int RouteId { get; set; }
public int HostId { get; set; }
public string RealRoute { get; set; }
public string alias { get; set; }
}
I very like this way because the requests returns are based on existing classes (Hosts and Route in this code).
The SqlCommand way seems more comfortable to use (it seems to be more PHP-like) but seems to return the entries as object. (I read this article)
I would like to know if is it possible to make a mix between the 2 ways and to send a request as a StoredProcedure like SqlCommand do BUT still be based on existing classes for the request return.
Thanks

if you want to use ADO.NET in EF, you just need to get the current connection string and create the sqlcommand as below:
using (var context = new RcoreContext())
{
// creates a Command
var command = context.Database.Connection.CreateCommand();
command.CommandType = commandType;
command.CommandText = sql;
using (SqlDataReader reader = command.ExecuteReader())
{
while (reader.Read())
{
for (int i = 0; i < reader.FieldCount; i++)
{
Console.WriteLine(reader.GetValue(i));
}
Console.WriteLine();
}
}
}
and if you just want to execute sql or stored procedure, you can also use below in EF
_dbContext.Database.ExecuteSqlCommand(sql, parameters);
_dbContext.Database.SqlQuery<TEntity>(sql, parameters);

using (var connection = context.Database.GetDbConnection())
using (var command = context.Database.GetDbConnection().CreateCommand())
{
command.CommandText = string.Format(sqlQuery, id);
connection.Open();
using (var reader = command.ExecuteReader()){}
}

using (var connection = new SqlConnection(_dbContext.Database.Connection.ConnectionString))
using (var cmd = new SqlCommand())
{
cmd.Connection = connection;
cmd.CommandText = "your query here";
cmd.CommandType = CommandType.Text;
cmd.Parameters.Add("#param1", SqlDbType.Int);
cmd.Connection.Open();
cmd.ExecuteNonQuery();
}

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>

Passing an instance with list to an API

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.

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" />

DataTable Loop Within a Loop C#

I have a nice dataset loop working, but I need to run another loop based on an ID in the parent loop.
I set up a generic list in a separate class, but I'm totally stumped on how to actually call it. I've Googled it but can't find an example I understand.
EDIT:
List Code...
public class BinList
{
public static List<Bin> GetById(int binOrderSiteID)
{
List<Bin> bins = new List<Bin>();
SqlConnection conn;
SqlCommand comm;
SqlDataReader reader;
using (conn = new SqlConnection(myConnectionHere))
{
comm = new SqlCommand("dbo.sl_BinsBySite", conn);
comm.CommandType = CommandType.StoredProcedure;
comm.Parameters.Add(new SqlParameter("#binOrderSiteID", SqlDbType.Int));
comm.Parameters["#binOrderSiteID"].Value = binOrderSiteID;
try
{
conn.Open();
reader = comm.ExecuteReader();
if (reader.HasRows)
{
while (reader.Read())
{
Bin b = new Bin();
b.BinQty = reader["binQty"].ToString();
b.BinType = reader["binType"].ToString();
b.BinWasteGroupName = reader["binWasteGroupName"].ToString();
b.BinCollectionFrequencyType = reader["binCollectionFrequencyType"].ToString();
b.BinDeliveryStartDate = reader["binDeliveryStartDate"].ToString();
b.BinEmptyCharge = reader["binEmptyCharge"].ToString();
b.BinRentalCharge = reader["binRentalCharge"].ToString();
b.BinDutyOfCareCharge = reader["binDutyOfCareCharge"].ToString();
b.BinDeliveryCharge = reader["binDeliveryCharge"].ToString();
bins.Add(b);
}
}
}
finally
{
conn.Close();
}
}
return bins;
}
}
This is a repository for each field
public class Bin
{
public string BinQty { get; set; }
public string BinType { get; set; }
public string BinWasteGroupName { get; set; }
public string BinCollectionFrequencyType { get; set; }
public string BinDeliveryStartDate { get; set; }
public string BinEmptyCharge { get; set; }
public string BinRentalCharge { get; set; }
public string BinDutyOfCareCharge { get; set; }
public string BinDeliveryCharge { get; set; }
}
Code to that calls the loops
public class PDFCreator
{
public static int BinOrderID { get; set; }
private int binOrderID = 0;
public PDFCreator(int intBinOrderID)
{
//Lots of code here
//Data conenection/datatable code here
foreach (DataRow row in dt.Rows)
{
//lots of code here
//dont know how to connect up or call the List
something?? = BinList.GetById(Convert.ToInt32(row["binOrderSiteID"].ToString()));
foreach (//somnething here)
}
}
}
Sorry I didn't add my code initially. I didn't want to show it cos I thought it was pants.
Any ideas?
Cheers,
Numb
To call BinList GetById
var binList = BinList.GetById((int)row["binOrderSiteID"]);
foreach (var bin in binList)
{
// do what you need to do with bin variable
}

Resources