I have an application that I am using as a file uploader with an admin panel on the backend of things. I have most of everything completed on it, but I'm running into a wall where I can't delete the physical file from the server. Permissions are correct to allow such action.
On the click of a delete button next to the entry I'm calling the primary ID of the row and as such I'm able to call from the SQL the stored filePath. Here's my code to do so:
DbConn dbConnx = new DbConn();
SQL = "SELECT filePath FROM database WHERE id='"+ primaryID +"'";
myReader = dbConnx.createDataReader(SQL);
string fn44 = Convert.ToString(myReader.Read());
string url = fn44; //I know redundant
System.IO.File.Delete(url);
All I'm able to gather is that the only information that is pulled is 'true'. I believe this is because I'm trying to convert the information to a string and it doesn't like that. How would I go about taking the value stored in SQL and using it with a variable to perform the delete?
Any help/resources would be greatly appreciated.
I don't know the datatype of myReader, but assuming that is a DataReader of some kind then calling
myReader.Read();
returns a boolean value that tells you if the datareader is positioned on a valid row or not.
To get the content of the record on which the reader is positioned (assuming the previous call returns true) you need to write
myReader = dbConnx.createDataReader(SQL);
if(myReader.Read())
{
string fn44 = Convert.ToString(myReader[0]);
....
}
Your code has another problem called Sql Injection.
You should not use string concatenation with user input when building a sql command.
You use a parameterized query like this
SQL = "SELECT filePath FROM database WHERE id=#p1";
using(SqlConnection con = new SqlConnection(......))
using(SqlCommand cmd = new SqlCommand(SQL, con))
{
con.Open();
cmd.Parameters.AddWithValue("#p1",primaryID);
using(SqlDataReader myReader = cmd.ExecuteReader())
{
.....
}
}
yyy
Having fixed the reading from the database, now you need to check what kind of string is stored in the FilePath field in the database. Remember that every file IO operation on a web site should get to the effective file name using the Server.MapPath method
Related
I can Insert, Update, Delete and Search the MySql Database
I wish to know how to check if a Song Exists in MySql DataBase ...
void CheckIfFileExists(String file, String dir)
{
// $mysqli = new mysqli(SERVER, DBUSER, DBPASS, DATABASE);
string song = Path.GetFileName(file);
song = MySql.Data.MySqlClient.MySqlHelper.DoubleQuoteString(song);
string ConString = " datasource = localhost; port = *;
username = ***; password = *****";
string sql = "SELECT id FROM music WHERE song = " + song);
using (MySqlConnection cn = new MySqlConnection(ConString))
{
cn.Open();
using (MySqlCommand cmd = new MySqlCommand(sql, cn))
{
//if id exixts?
if (???????????)
{
// "Song exists";
}
else
{
// "Song does not exist";
InsertIntoDataBase(String file, String dir);
}
}
}
}
I'll go straight to the real issue, how to upsert correctly, before the question gets closed. Use MySQL's custom INSERT IGNORE and parameterized queries. Using Dapper makes this a one-liner:
var sql=#"INSERT IGNORE INTO music
(ID,song,folder)
VALUES (1, #song,#folder)";
using (var cn= new MySqlConnection(ConString))
{
cn.Execute(sql,new {song=song,folder=#dir});
}
Dapper will open the connection to execute the command and close it afterwards. It will generate a MySqlCommand, generate parameters for every property in the supplied object (#song for song, #folder for folder).
INSERT IGNORE will try to insert a new row to the table. If a row with the same primary key already exists, the INSERT will be ignored.
Generating SQL strings by concatenating input is very dangerous. No amount of quoting can prevent SQL injection attacks, or more benign conversion errors. What would happen if the song was named '0'; DROP TABLE music;-- ? What if the value was a date or number? The query string would end up with invalid characters.
Parameterized queries on the other hand are like C# functions. The parameters are passed to the server outside the string itself, as part of the RPC call. The server generates an execution plan from the query and feeds the parameter values to the compiled execution plan.
The values never change to text or get combined with the query, so there's no way to inject SQL or end up with type conversion issues
The application I am developing is meant to be a quick and easy tool to import data to our main app. So the user loads in a CSV, meddles with the data a little and pushes it up to the database.
Before the data is pushed to the database, I have a verification check going on which basically says, "Does a customer exist in the database with the same name, account and sort codes? If so, put their guid (which is known already) into a list."
The problem is, the result variable is always 0; this is despite the fact that there is duplicate test data already in my database which should show a result. Added to that, using SQL Profiler, I can't see a query actually being executed against the database.
I'm sure that the ExecuteScalar() is what I should be doing, so my attention comes to the Parameters I'm adding to the SqlCommand... but I'll be blowed if I can figure it... any thoughts?
string connectionString = Generic.GetConnectionString("myDatabase");
using (SqlConnection conn = new SqlConnection(connectionString))
{
conn.Open();
SqlCommand check = new SqlCommand("select COUNT(*) from Customers C1 INNER JOIN CustomerBank B1 ON C1.Id = B1.CustomerId WHERE C1.Name = #Name AND B1.SortCode = #SortCode AND B1.AccountNumber = #AccountNumber", conn);
foreach (DataRow row in importedData.Rows)
{
check.Parameters.Clear();
check.Parameters.Add("#Name", SqlDbType.NVarChar).Value = row["Name"].ToString();
check.Parameters.Add("#SortCode", SqlDbType.NVarChar).Value = row["SortCode"].ToString();
check.Parameters.Add("#AccountNumber", SqlDbType.NVarChar).Value = row["AccountNumber"].ToString();
Object result = check.ExecuteScalar();
int count = (Int32)result;
if (count > 0)
{
DuplicateData.Add((Guid)row["BureauCustomerId"]);
}
}
}
Clarification: importedData is a DataTable of the user's data held in this C# application. During the ForEach loop, each row has various columns, a few of those being Name, SortCode and AccountNumber. The values seem to get set in the parameters (but will verify now).
i have tried this
byte[] byteArrayFile = File.ReadAllBytes(#""+listBox1.SelectedItem);
SqlCommand com = new SqlCommand("INSERT INTO tblVoice(flbVoice) VALUE (byteArrayFile");
i also want to put it on a sever
and this
byte[] byteArrayFile = File.ReadAllBytes(listBox1.SelectedItem);
SqlCommand com = new SqlCommand("INSERT INTO tblVoice(flbVoice) VALUE (byteArrayFile");
but Failed Please Help Thank You
There are a few things that could be wrong with your existing code.
Using File.ReadAllBytes()
You are using the File.ReadAllBytes() method which actually expects a file path. If your actual ListBox consists of file paths, then this may work just fine, but if it consists of other types of values it may fail. So just make sure that you are actually using a file path here :
// If your selection is a file path, then this should work
byte[] byteArrayFile = File.ReadAllBytes(Convert.ToString(listBox1.SelectedItem));
If you aren't using a file path, you may need to elaborate on what your actual ListBox contents looks like to determine the best way to convert it to a byte[].
Performing a Database Query (And Check Your Syntax)
The command that you supplied on its own will not execute a database query. Database calls generally require a connection, which you will need to create that points to your target location :
using(var connection = new SqlConnection("Your Connection String"))
{
var query = "INSERT INTO tblVoice(flbVoice) VALUE (#file)";
using(var command = new SqlCommand(query, connection))
{
connection.Open();
// Pass your byte data as a parameter
command.Parameters.AddWithValue("#file",byteArrayFile);
// Execute your parameter
command.ExecuteNonQuery();
}
}
Additionally, you were missing a 'T' in your INSERT statement, which has been corrected in the example above.
I want to attach a DataSet with parameterized query. Something like a user entering a value in a text box then hit submit button.
I have created a Text Field and a click button event something like :
private void Btn_GetProjDetails_Click(object sender, EventArgs e)
{
string userEnteredProjId = tab3ProjIdInput.Text;
}
but now don't know how to use this userEnteredProjId variable in my query?
I haven't tried the manually coding all the data-connections path. Instead added the GUI in VS2012 to add a data source. Then using this data source I have learned we can add datasets, and then use these DataSets to just drag and drop in our form. So I created a dataset and then dataset toolbox, I added my table and created a query but don't know how to use the userEnteredProjId in my query here.
You never want to just insert a value from a user into an SQL query because that is a huge SQL injection risk. It is better to use parameters, and better still if you do some validation on the parameters before using them. Here is a basic example of using a command parameter.
using (cmd command = new SqlCommand())
{
string sql = "Select * from table where projid=#UserEnteredProjid";
cmd.Connection = conn;
cmd.CommandType = CommandType.Text;
cmd.CommandText = sql;
cmd.Parameters.AddWithValue("UserEnteredProjid", your_value_here);
SqlDataReader reader = command.ExecuteReader();
while (reader.Read())
{
//do something;
}
}
Well, your query is just a string variable I'm guessing, like "select * from table". You just want to take some user entered data to augment your query:
string query = "select * from table where projid = " + UserEnteredProjid;
Updated Question: Is there a way to force dataadapter accept only commands which do not include any update/drop/create/delete/insert commands other than verifying the command.text before sending to dataadapter (otherwise throw exception). is there any such built-in functionality provided by dot net in datareader dataadapter or any other?
Note: DataReader returns results it also accepts update query and returns result. (I might be omitting some mistake but I am showing my update command just before executing reader and then show message after its success which is all going fine
Could you search the string for some keywords? Like CREATE,UPDATE, INSERT, DROP or if the query does not start with SELECT? Or is that too flimsy?
You might also want to create a login for this that the application uses that only has read capability. I don't know if the object has that property but you can make the server refuse the transaction.
All you need to do is ensure there are no INSERT, UPDATE, or DELETE statements prepared for the DataAdapter. Your code could look something like this:
var dataAdapter = new SqlDataAdapter("SELECT * FROM table", "connection string");
OR
var dataAdapter = new SqlDataAdapter("SELECT * FROM table", sqlConnectionObject);
And bam, you have a read-only data adapter.
If you just wanted a DataTable then the following method is short and reduces complexity:
public DataTable GetDataForSql(string sql, string connectionString)
{
using(SqlConnection connection = new SqlConnection(connectionString))
{
using(SqlCommand command = new SqlCommand())
{
command.CommandType = CommandType.Text;
command.Connection = connection;
command.CommandText = sql;
connection.Open();
using(SqlDataReader reader = command.ExecuteReader())
{
DataTable data = new DataTable();
data.Load(reader);
return data;
}
}
}
}
usage:
try{
DataTable results = GetDataForSql("SELECT * FROM Table;", ApplicationSettings["ConnectionString"]);
}
catch(Exception e)
{
//Logging
//Alert to user that command failed.
}
There isn't really a need to use the DataAdapter here - it's not really for what you want. Why even go to the bother of catching exceptions etc if the Update, Delete or Insert commands are used? It's not a great fit for what you want to do.
It's important to note that the SelectCommand property doesn't do anything special - when the SelectCommand is executed, it will still run whatever command is passed to it - it just expects a resultset to be returned and if no results are returned then it returns an empty dataset.
This means that (and you should do this anyway) you should explicitly grant only SELECT permissions to the tables you want people to be able to query.
EDIT
To answer your other question, SqlDataReader's are ReadOnly because they work via a Read-Only firehose style cursor. What this effectively means is:
while(reader.Read()) //Reads a row at a time moving forward through the resultset (`cursor`)
{
//Allowed
string name = reader.GetString(reader.GetOrdinal("name"));
//Not Allowed - the read only bit means you can't update the results as you move through them
reader.GetString(reader.GetOrdina("name")) = name;
}
It's read only because it doesn't allow you to update the records as you move through them. There is no reason why the sql they execute to get the resultset can't update data though.
If you have a read-only requirement, have your TextBox use a connection string that uses an account with only db_datareader permissions on the SQL database.
Otherwise, what's stopping the developer who is consuming your control from just connecting to the database and wreaking havoc using SqlCommand all on their own?