I'm a bit confused of how to get a data from an access database. Is it proper to gather it first in a List then get those data from your List OR it is okay to just directly get it in you database ?
My codes work perfectly fine, but I wanna know if there is a better way to do this?? :
private void button3_Click(object sender, EventArgs e)
{
OleDbConnection connection = new OleDbConnection(#"Provider=Microsoft.ACE.OLEDB.12.0;Data Source=C:\Users\redgabanan\Desktop\Gabanan_Red_dbaseCon\Red_Database.accdb");
connection.Open();
OleDbDataReader reader = null;
OleDbCommand command = new OleDbCommand("SELECT * from Users WHERE LastName='"+textBox8.Text+"'", connection);
reader = command.ExecuteReader();
listBox1.Items.Clear();
while (reader.Read())
{
listBox1.Items.Add(reader[1].ToString()+","+reader[2].ToString());
}
connection.Close();
*I'm getting my records directly from a database then display it in a listbox.
One thing that is sticking out like a sore thumb is the SQLInjection and to use Parameterised queries, eg:
OleDbCommand command = new OleDbCommand("SELECT * from Users WHERE LastName='#1'", connection);
command.Parameters.AddWithValue("#1", textBox8.Text)
What your doing is perfectly acceptable, although you would generally be better off to use a SQL Database.
Edit:
Here is how you seperate your business logic from the GUI:
Class BusLogic
{
public List<string> ListboxItems = new List<string>();
public void PopulateListBoxItems(string userName)
{
string connString = #"Provider=Microsoft.ACE.OLEDB.12.0;Data Source=C:\Users\redgabanan\Desktop\Gabanan_Red_dbaseCon\Red_Database.accdb";
using (OleDbConnection connection = new OleDbConnection(connString))
{
connection.Open();
OleDbDataReader reader = null;
OleDbCommand command = new OleDbCommand("SELECT * from Users WHERE LastName='#1'", connection);
command.Parameters.AddWithValue("#1", userName)
reader = command.ExecuteReader();
while (reader.Read())
{
ListboxItems.Add(reader[1].ToString()+","+reader[2].ToString());
}
}
}
}
GUI
private void button3_Click(object sender, EventArgs e)
{
var busLogic = new BusLogic();
busLogic.PopulateListBoxItems(textBox8.Text);
\\listBox1.Items.Clear();
ListboxItems.DataSource = busLogic.ListboxItems;
}
The beauty of this "MVC" approach is that we only really need to test the BusLogic if we rely on controls being bound using Binding.
ps Ideally ListboxItems would be an IEnumerable instead of List so that we don't expose any functionality to Add/Remove etc from the caller. This is good API design.
I would say the answer is "yes" to both.
What you're doing now is perfectly acceptable for simple cases. Just be aware that it doesn't "scale" very well. That is, loading 10 or 20 items is fine. But what happens if it becomes 10 thousand or a million?
In that case you want to look at using a Model-View-Controller (MVC) architecture. That's a topic in itself, but basically you decouple the listbox (the "view") from the data (the "model").
See this site for a C#-centric MVC discussion
In between what you're doing now and a full-blown MVC architecture, you may simply want to do as you suggest - load the list first then add them to the list box. That gains you nothing if you just load it once, but if the list is loaded "all over the place", you can save the database IO overhead each time by just accessing it once.
The fact that you thought to ask the question indicates you're on the right track.
Although your code works without any problem, I suggest you to perform some exception handling as in this example, since both OleDbConnection.Open() and OleDbCommand.ExecuteReader() might throw an InvalidOperationException.
It is also common to wrap the connection with a using statement, so in the end connection.close() is called automatically, but this is just a personal preference.
You can maybe separate your data access functions in different classes or create generic functions to retrieve records.
Related
Here is the stored procedure on my DB. I want to save data of list to my SQL database, i can save one data, but when i try to add more it caused error like this "Procedure or function Music_Add has too many arguments specified". Can someone help me how to fix that error... Thanks...
Here is the code to save the list to database:
private void btnsave_Click(object sender, EventArgs e)
{
string username = dtuser;
conn.Open();
SqlCommand cmd = new SqlCommand("Music_Add", conn);
cmd.CommandType = CommandType.StoredProcedure;
foreach (var item in fav_list.Items)
{
cmd.Parameters.AddWithValue("#username", username);
cmd.Parameters.AddWithValue("#music", item.ToString());
cmd.ExecuteNonQuery();
}
cmd.Dispose();
conn.Close();
}
Consider something more like this:
private void btnsave_Click(object sender, EventArgs e)
{
string username = dtuser;
using var cmd = new SqlCommand("Music_Add", "conn str here");
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.Add("#username", SqlDbType.VarChar, COLUMN_SIZE_HERE);
cmd.Parameters.Add("#music", SqlDbType.VarChar, COLUMN_SIZE_HERE);
cmd.Connection.Open();
foreach (var item in fav_list.Items)
{
cmd.Parameters["#username"].Value = username;
cmd.Parameters["#music"].Value = item.ToString());
cmd.ExecuteNonQuery();
}
}
Use the string/string version of SqlCommand; it's one less thing to dispose and also leads you not into the temptation of keeping ahold of SqlConnection variables; easy to forget to close it (or suffer an exception and not reach the closing code), and then you leak conenctions. You only really need to carry SqlConnections around if you're enrolling multiple commands in a transaction
Use using - it doesn't forget to dispose, even if an error occurs
Create your parameters once, and use the same size in the param as the column is wide. See Dan Guzman's blog about using AddWithValue - I'm overjoyed to read a question that actually parameterizes instead of risking SQL Injection, but be aware that there are potential performance penalties to using AddWithValue in some circumstances. It's unlikely to affect you in this case, but generally on SQLServer it's only one rule of thumb to remember, to get into the habit of avoiding AWV rather than try and work out when it's safe and when it's a problem
Repeatedly set your values in the loop and execute the query. You could even consider batching with a table valued parameter set, for better performance
I have a page load method that loads asp dropdowns with sql queries to my SQL Server 2012 database. I'm new to this and basically independently learned a lot of needed to be done for the co op project I'm working on.
I've been running into problems with connections not being closed properly and having my connection pool blow up with only moderate usage of my app so I've been trying to improve how I'm executing my queries in my c# code behind. But I am not confident in my understanding of this so I'm going to post an example of my code and hope that someone much more fluent might be able to guide me a bit.
string constr = ConfigurationManager.ConnectionStrings["CurrencyDb"].ConnectionString;
using (SqlConnection con = new SqlConnection(constr)) {
using (SqlCommand cmd = new SqlCommand("SELECT * FROM dbo.Category")) {
try {
cmd.CommandType = CommandType.Text;
cmd.Connection = con;
con.Open();
//Populate Category Dropdown
DDCategory.DataSource = cmd.ExecuteReader();
DDCategory.DataTextField = "CategoryName";
DDCategory.DataValueField = "CategoryId";
DDCategory.DataBind();
}
catch (SqlException sqlex) {
throw new Exception("SQL Exception loading data from database. " + sqlex.Message);
}
catch (Exception ex) {
throw new Exception("Error loading Category data from database. " + ex.Message);
}
}
using (SqlCommand cmd = new SqlCommand("SELECT * FROM dbo.SubCategory ORDER BY SubCategoryName")) {
try {
cmd.CommandType = CommandType.Text;
cmd.Connection = con;
//Populate SubCategory Dropdown
DDSubCategory.DataSource = cmd.ExecuteReader();
DDSubCategory.DataTextField = "SubCategoryName";
DDSubCategory.DataValueField = "SubCategoryId";
DDSubCategory.DataBind();
}
catch (SqlException sqlex) {
throw new Exception("SQL Exception loading data from database. " + sqlex.Message);
}
catch (Exception ex) {
throw new Exception("Error loading Subcategory data from database. " + ex.Message);
}
}
}
The above are two queries of about 8 on my page load method.
My latest error was
There is already an open DataReader associated with this Command which must be closed first.
This prompted me to ask the question, I set MultipleActiveResultSets=true in my Web.config connection string and my app works now, but I feel as though that's a patch to cover what is likely crappy code.
What is the best practice for doing this? Thanks so much in advance!
Well, there could be multiple way but I feel you should wrap all this queries in a stored procedure and call that SP in your code behind. Instead of using DataReader use a DataSet and fill it up with the different resultset.
You can also use NextResult() method of datareader instance to get the next SELECT result and do your processing.
To your immediate question, you are using two cmds per one connection.
You could be better off using one-trip, one command, with a multiple-result.
This will allow you to properly .Close() your datareader and your connection...."returning them back to the pool"
Since you are using SqlServer, it supports multiple resultsets in one "trip".
SELECT * FROM dbo.Category;SELECT * FROM dbo.SubCategory
Use that as your select statement.
Use an .ExecuteReader() method.
And use a .NextResult() to move from one result (category), to the next one (subcategory)
You can see a more complete sample here:
https://msdn.microsoft.com/en-us/library/system.data.idatareader.nextresult(v=vs.110).aspx
Or even with Entity Framework here:
https://msdn.microsoft.com/en-us/library/jj691402%28v=vs.113%29.aspx?f=255&MSPPError=-2147217396
Now some extra stuff.
You are mixing your datalayer and your presentation layer.
You should have your datalayer populate "Dto" sometimes referred to as "Poco" objects, and return those to the Presentation layer.
public class Category
public string CategoryKey{get;set;}
public string CategoryName{get;set;}
public ICollection<SubCategory> SubCategories {get;set;}
..
public class SubCategory
public string SubCategoryKey{get;set;}
public string SubCategoryName{get;set;}
..
public class MyDataLayerObject
public ICollection<Category> GetAllCategories()
{
ICollection<Category> categories;
ICollection<SubCategory> subcats;
// write a datareader call here, and use it to populate multiple Category and SubCategory objects
// make sure you close the datareader when done
//now "match up" the subcats to its parent category
}
Then have your presentation layer "bind" to the ICollection of Categories.
You might have a business layer between the presentation and datalayers, but at least to the presentation and datalayers.
I also answered a question at the link below...that is similar to yours. Their objects are "Question" and "Answer" where a Question has 0:N number of Answers.
Find my answer at the below question.
Return objects with populated list properties from stored procedure
Note, you can just use the 2 select queries in a string (the second line of my/this answer), aka, you don't have to create a stored procedure.
Another option is to use nuget to get Microsoft.EnterpriseLibrary.Data.
This encapsulates alot of great practices for you...including closing connections.
You'll still have to close your datareader, but the code is alot cleaner.
These two should be in different methods. One to get category and one to get sub-categories. Then each will have a different connection and you won't have this problem.
You should call Dispose on your DataReader or either put it inside a using statement so that Dispose() is called for you.
The typical IDataReader pattern looks like this:
using (IDataReader r = query.ExecuteReader())
{
while (r.Read())
{
// etc.
}
}
When you don't call Dispose() on the IDataReader he can't be cleaned up. And I expect that your connection also can't be cleaned up even when you call Dispose() on the connection, because it's stil used by your IDataReader.
And yes per Connection you can only use one IDataReader.
So as others wrote you should split this up into more than one method and use one connection per method.
Btw.:
It would be better not to use "SELECT *". Just query the fields you need.
I want to create an autocomplete textbox with my database.
I'm programming my application in a layered architecture (models, DAL, BLL, Presentation).
I've already made a method with an arraylist that reads and returns my select command in the database, which is filling (I've tested on a combobox).
But when I try to insert in the the textbox, nothing happens... it doesn't show the suggestion.
I looked for something in the forum but I just found examples with one layer and, since I'm developing in layers I cannot increment the property AutoCompleteStringCollection in my DAL to be filled by my select command.
If anyone has any idea how to solve this problem, please explain to me!
Additional information: I'm using winForm with C# and SQL Server.
I think you want to say that "But when i try to insert in the textbox, nothing happens... it doesn't show the sugestion."
well i cannot just code all layers here but can suggest in your DAL create a method which returns List and then on your form page provide code like this
txtName.AutoCompleteMode = AutoCompleteMode.Suggest;
txtName.AutoCompleteSource = AutoCompleteSource.CustomSource;
var autoCompleteCollection = new AutoCompleteStringCollection();
autoCompleteCollection.AddRange(DAL.GetMethod().ToArray());
textbox.AutoCompleteCustomSource = autoCompleteCollection;
Thanks for the help!!
I used your suggest and had made some little changes and it work just fine to me...
Turns out that the only problem was my method list, once I change it do a List < String > things got better.
For who is wondering, here is how I do it:
DAL LAYER:
public List<string> LoadList()
{
List<string> tagsList = new List<string>();
using (SqlConnection connection = new SqlConnection(ADados.StringDeConexao))
{
connection.Open();
using (SqlCommand command = connection.CreateCommand())
{
command.CommandText = "SELECT column FROM table";
using (SqlDataReader reader = command.ExecuteReader())
{
while (reader.Read())
{
if (!reader.IsDBNull(0))
tagsList.Add(reader.GetString(0));
}
reader.Close();
}
connection.Close();
return tagsList;
}
PRESENTATION LAYER (Event TextChanged):
PedidoBLL pedido = new PedidoBLL();
txtName.AutoCompleteMode = AutoCompleteMode.Suggest;
txtName.AutoCompleteSource = AutoCompleteSource.CustomSource;
AutoCompleteStringCollection popula = new AutoCompleteStringCollection();
popula.AddRange(pedido.LoadList().ToArray());
txtName.AutoCompleteCustomSource = popula;
In the BLL Layer i just call and return the DAL method LoadList...
I'm a beginner.
I already found a way to connect to SQL SERVER using the codes below:
private void getListBtn_Click(object sender, RoutedEventArgs e)
{
SqlConnection con = new SqlConnection("Data Source=.;Initial Catalog=myDB;Integrated Security=true;");
SqlDataAdapter sda = new SqlDataAdapter("SELECT ID,Date,Name,City FROM Info;", con);
DataTable dt = new DataTable();
sda.Fill(dt);
dataGridForm.ItemsSource = dt.DefaultView;
I also wanted to get number of rows from a TABLE and set it to a label, But it's not a good idea to copy and paste this code again, I want to have a method for sqlconnection so i won't rewrite this code again and again for every single query.
Sorry i'm an absolute beginner, 3 days since i started learning C# WPF.
Yes some frameworks and/or ADO's solutions are good and maybe the best "professionnal" approch, you say you're a beginner and I was it not so far ;-).
So the simpliest way is to add a new class for the sql connection. In example add a Sqlconnect.cs class.
using System.Data.SqlClient;
public class Sqlconnect
{
public SqlConnection Con { get; set; }//the object
private string conString { get; set; }//the string to store your connection parameters
}
This class will have a method to open the connection and one to close it.
public void conOpen()
{
conString = "Data Source=..."; //the same as you post in your post
Con = new SqlConnection(conString);//
try
{
Con.Open();//try to open the connection
}
catch (Exception ex)
{
//you do stuff if the connection can't open, returning a massagebox with the error, write the error in a log.txt file...
}
}
public void conClose()
{
Con.Close();//close the connection
}
In your other(s) classe(s) where you need a sql query you first instantiate an new object.
private void getListBtn_Click(object sender, RoutedEventArg e)
{
Sqlconnect con = new Sqlconnect();//instantiate a new object 'Con' from the class Sqlconnect.cs
con.conOpen();//method to open the connection.
//you should test if the connection is open or not
if(con!= null && con.State == ConnectionState.Open)//youtest if the object exist and if his state is open
{
//make your query
SqlDataAdapter sda = new SqlDataAdapter("your query", con);
//etc
con.conClose();//close your connection
}
else
{
//the connection failed so do some stuff, messagebox...as you want
return;//close the event
}
}
this example need some ameliorations, it's evident but I wrote it like this to be clearest.
First thing this is not related to WPF, this is general coding even I would not consider this to be related to .net.
For your current problem to show the count, you dont have to make a call again. You can get the count from the datatable row count. But, I would suggest few things:
You should have one or different separate layers like business, data access etc. as per your needs.
You should not give the connection as the way you have provided here.
You can choose to use any ORMs like entity framework, NHibernate etc based on your needs. This just a direction, you can choose to stick with ADO.Net as you have it your choice. But I would definitely suggest to throw in more layers to avoid duplicate codes and more structured approach.
Best choice if you don't need so much performance is ORM like Entity Framework.
Here is something of basics.
Just use it like in MVC app.
If we copy paste your code, then the error is appearing. I have corrected it and maybe others don't need to struggle like me to find this. :)
// Object exists and State is open
if (Conex != null && Conex.Con.State ==
System.Data.ConnectionState.Open)
{
// Create a String to hold the query
string query = "insert into Xray_Table values
(25,'zzz','hij',3,'uuu',6,'2012-06-18
10:34:09.000')";
// Create a SqlCommand object and pass the constructor the connection string and the query string
SqlCommand queryCommand = new SqlCommand(query, Conex.Con);
// Execute the query to update to the database
queryCommand.ExecuteNonQuery();
// method to close the connection.
Conex.conClose();
}
I am trying to get a drop down list to display data from MYSQL database, database table name is Pet and I am trying to get the information from Specie.
Now the code looks good to me but it is not running so its always best to get another pair of eyes to look over it.
Thanks,
All help is appreciated.
connection string
MySqlConnection cs = new MySqlConnection(#"Data Source = 000.000.00.000;username=benoatsc_admin;password=****; Initial Catalog = dbname; Integrated Security = true");
drop down code
protected void DropDownList1_SelectedIndexChanged(object sender, EventArgs e)
{
MySqlCommand cd = new MySqlCommand("SELECT * FROM Pet", cs);
cs.Open();
MySqlDataReader ddl = cd.ExecuteReader();
DdPetList.DataSource = ddl;
DdPetList.DataValueField = "Specie";
DdPetList.DataTextField = "Specie";
DdPetList.DataBind();
cs.Close();
cs.Dispose();
}
There are a few problems here:
You should likely move this code to the Page_Load method. I would assume that you want this DropDownList populated when the page loads for the user. Otherwise, the DropDownList is never populated with the data from your MySQL Database, hence you will not be able to trigger the SelectedIndexChanged event when there is no index to possibly change.
MySQL Connector/Net makes use of unmanaged resources. These should be disposed of when they are finished being used. The .NET Framework offers an elegant way to ensure that objects utilizing unmanaged resources are disposed of in the form of using statements. It is best practice to use them.
When MySqlConnection, MySqlDataReader, etc. should not only be disposed of when they are done being used, but they should also be closed. Thankfully, when dispose() is called on these objects their close() methods are called as well.
So piecing this all together yields this piece of code:
protected void Page_Load(object sender, EventArgs e)
{
using (var conn = new MySqlConnection(/* Your connection string here */))
{
conn.Open();
using (var cmd = new MySqlCommand("SELECT * FROM Pet", conn))
{
using (var reader = cmd.ExecuteReader())
{
if (reader.HasRows)
{
DropDownList1.DataSource = reader;
DropDownList1.DataValueField = "Specie";
DropDownList1.DataTextField = "Specie";
DropDownList1.DataBind();
}
}
}
}
}
I hope this all helps. If you need any clarifications, I will gladly provide it.
This would be a comment but i don't have the reputation,
what error message are you receiving ?
your connection string has got both trusted connection set to true and you are providing credentials,
remove one of them.