Confused between SqlCommand & SqlDataAdapter - c#

everyone I am a student and new to .NET and specially MVC3 development but for one of my project I’ve to work over it and so going through the learning phase
Issue and confusion I am facing is regarding DB-Connectivity, whast I leanree d regarding retrieving records from a database is something like this:
//Method One:
var conn = new SqlConnection(conString.ConnectionString);
const string cmdString = "Select * FROM table";
var cmd = new SqlCommand(cmdString, conn);
var mySqlDataAdapter = new SqlDataAdapter(cmd);
mySqlDataAdapter = new SqlDataAdapter(cmd);
mySqlDataAdapter.Fill(myDataSet, "design");
// making a new SqlCommand object with stringQuery and SqlConnection object THEN a new SqlDataAdapter object with SqlCommand object and THEN filling up the table with the resulting dataset.
But while I was checking out MSDN Library i found out that SqlDataAdapter offers a constructors SqlDataAdapter(String, String) that directly takes a SelectCommand and a connection string to initiate thus skipping the role of SqlCommand in between, like this:
//Method Two:
var conn = new SqlConnection(conString.ConnectionString);
const string cmdString = "Select * FROM table";
var mySqlDataAdapter = new SqlDataAdapter(cmdString, conn);
mySqlDataAdapter.Fill(myDataSet, "design");
Looks short and pretty to me, But I am confused here that if this is possible in this way then why most of the books/Teachers goes by earlier (SqlCommand’s way).
What’s actually the difference between SqlCommand and SqlDataAdapter?
Which method is better One or Two?
Am afraid of I am using a shortcut in method two that could affect security or performance wise?
Apologising in advance if I sound very newbie or blurred! Will appreciate any help that could clear my concepts up! Thankyou! :)

Errorstacks summed it right:
SqlAdapter is used to fill a dataset.
SqlCommand can be used for any purpose you have in mind related to Create/Read/Update/Delete operations, stored procedure execution and much more.
In addition:
SqlCommand CAN have one big advantage against usage of raw strings in regards of security - they CAN protect you from Sql Injections. Just use parameters for values provided by the user instead of string.Format(...).
My personal preference is to wrap ANY sql strings in SqlCommand and add SqlParameters to it in order to avoid Sql Injection by malicious users.
Regarding performance of the two approaches - I don't expect that there is any difference. (If someone can prove me wrong - do it!).
So I would suggest to stick with the longer variant 1 and use commands plus parameters if necessary.
A bit of a side note - Datasets and DataTables are a bit out of game recently due to Linq2Sql and Entity Framework.
But of course the knowledge of plain old SqlCommands/Adapters/Readers is welcome :)

Hurry-up! Turn your attention to LINQ!!!
No more gran'ma stuff like SQLDataset or TableAdapters, no open connection.
Everything gets smoother with LINQ.
LINQ sample:
dim result = from emp in myDataContext.Employees
where emp.Salary > 10000
Select emp.ID, emp.SurName, ....
myDatagrid.datasource = result.toList
With LINQ, you don't have to worry about single quotes or crlf within your queries...
And you'll even have intellisense on the SQL tables, columns and objects!

Related

WPF DataGrid - MS Access Table - Column headings display but not data is pulled

I followed the information found here
https://learn.microsoft.com/en-us/dotnet/desktop/wpf/controls/walkthrough-display-data-from-a-sql-server-database-in-a-datagrid-control?view=netframeworkdesktop-4.8
This article is for using SQL server as the source but I'm using Access accdb instead. This seemed to be the most straightforward article that referenced pulling data. I'm very new to WPF/C# but trying to learn as I think I can build a better interface for a tool that is built in Access right now since there are more UI options and I want to expand a bit outside VBA which I know very well but is a less than ideal language.
Anyhow, below I think is the relevant code. If I preview the data set in designer, I can see it does pull back data into a control that is a data grid itself and it mentions this Fill, GetData() so maybe there is somewhere I'm supposed to call that.
If I can figure out where/how I'll answer my own question and close this.
XAML:
<DataGrid Name="processScheduleGrid" />
C#:
var query = from ps in dataSet.PROCESS_SCHEDULES
orderby ps.SCHEDULE_NAME
select new { ps.SCHEDULE_NAME, ps.PROCESS_NAME, ps.DAY_OF_MONTH, ps.DAY_OF_WEEK, ps.START_TIME, ps.END_TIME, ps.STATUS, ps.STATUS_TM, ps.PARALLEL, ps.MINUTES_BEFORE_REPEAT, ps.ENABLED, ps.ERR_NUM };
processScheduleGrid.ItemsSource = query.ToList();
I was able to get this working with another object model that is a lot more familiar looking to me being used to working in access with both DAO and ADO. It's more similar to the latter
The revised code is much simpler, and it doesn't do any of the auto-generating that I really am not liking while learning to use C# and Visual Studio more. I think I could have made the above work if I had gone through every column and added handling for nulls but this code handles that with no issue, so it seems like this is the right way to go vs the other object model (System.Windows.Data vs System.Data/System.Data.OleDb) and syntax.
OleDbConnection con = new OleDbConnection(provider);
OleDbCommand cmd = new OleDbCommand(sql, con);
con.Open();
cmd.CommandType = System.Data.CommandType.Text;
OleDbDataAdapter da = new OleDbDataAdapter(cmd);
DataSet ds = new DataSet();
da.Fill(ds, "PROCESS_SCHEDULES");
processScheduleGrid.ItemsSource = ds.Tables["PROCESS_SCHEDULES"].DefaultView;

What is the better way of making an SQL statement in Visual Studio?

I was looking up how to insert into my database via sql and I noticed the way I had seen a person do an sql statement was different from the way I had done it and and now I'm wondering which way is better.
An example of what I had done in a previous (select) statement.
SqlConnection conn = new SqlConnection(Variables.Default.sqlConString);
conn.Open();
string builtCmd = Variables.Default.returnUserNameSql1 + usersInput + Variables.Default.returnUsernameSql2;
SqlCommand cmd = new SqlCommand(builtCmd, conn);
usersInput is a string.
Variables.Default.returnUserNameSql1 = SELECT [Username] from [dbo].[LoginDetails] WHERE [Username] = '
returnUsernameSql2 = '
What I have seen online (not my query):
cmd.CommandText = "INSERT INTO klant(klant_id,naam,voornaam) VALUES(#param1,#param2,#param3)";
cmd.Parameters.Add(new SqlParameter("#param1", klantId));
cmd.Parameters.Add(new SqlParameter("#param2", klantNaam));
cmd.Parameters.Add(new SqlParameter("#param3", klantVoornaam));
Is the use of the Parameters function (?) better? If so in what way?
Thanks for your time.
I modified my original query thanks to the help of some of the comments here. I'll post it if anyone's interested:
using (SqlConnection conn = new SqlConnection(Variables.Default.sqlConString))
{
conn.Open();
using (SqlCommand cmd = new SqlCommand(Variables.Default.returnUserNameSql, conn))
{
cmd.Parameters.Add(new SqlParameter(Variables.Default.param1, usersInput));
SqlDataReader reader = cmd.ExecuteReader();
usernameTaken = reader.Read();
cmd.Dispose();
}
conn.Close();
}
Look up "SQL Injection attack" on google. Bobby Tables says hello. And then realize that your way is not bad, it is a security nightmare because everyone with access to your program can execute whatever SQL he wants.
SQL Injection is certainly an important reason not to use string concatenation, but there are a few others:
string delimiters - you'd need to include string delimiters in your SQL statement, and if the values you concatenate include them as well, you'll likely get syntax errors. With parameters you don't need string delimiters, and values with apostro[phes or quotes don't affect the SQL syntax.
string conversion of values - you'd need to convert all non-string values (numbers, dates, etc.) to strings, and ensure that their string represenatations are exactly reversible by the server. This is especially problematic for dates since the same string can represent two different dates depending on the culture of the server. With parameters, the values are passed without translation, so there's no risk that the server misinterprets values.
pre-compilation - with concatenation, the server must reanalyze each query to determine the "best" plan. With parameters, the server can re-use a cached plan since the actual query has been issued before, just with different parameters. This doesn't mean that queries will always perform faster, and in some cases can actually cause bad plans to be used, but it is a consideration if you issue millions of queries that only differ in parameter values.

SQLCommandType.View - why is not this provided by MS?

We see that System.Data.CommandType has only 3 enums: "StoredProcedure", "TableDirect" and "Text". Why don't we see "View" as an option? What is the Framework specific reason Microsoft does not provide this option?
I am specifically talking about the SQL Server Views here.
Your answer will be highly appreciated.
Because a View is accessible via TableDirect or Text. The most common access is Text anyway because you're generally doing something like this:
DataTable dt = new DataTable();
using (SqlConnection c = new SqlConnection(cString))
{
using (SqlDataAdapter sda = new SqlDataAdapter(sql, c))
{
sda.SelectCommand.Parameters.Add("parm1", value1);
c.Open();
sda.Fill(dt);
}
}
Finally, a view is actually represented as a table in SQL Server. It has a real table definition, it just happens to be built with a query.
Most of the times View is not used by itself but rather as a part of a query or SP. Hence it's not necessary to provide a dedicated CommandType. Same thing goes for Functions - there's no CommandType.Function.

Do I have to worry about inserts/updates/deletes/injection attacks when I use the following SqlDataAdapter?

Do I need to do anything in order to prevent inserts/updates/deletes/injection attacks when I'm using the following code?
public static DataSet getReportDataSet(string sqlSelectStatement)
{
SqlDataAdapter da = new SqlDataAdapter(sqlSelectStatement, new SqlConnection(GlobalVars.ConnectionString));
DataSet reportData = new DataSet();
da.Fill(reportData, "reportData");
return reportData;
}
The idea behind this is that I'll be extracting the sql from a series of Crystal Reports, pulling the data for each report from the MS SQL Server, binding the data to the reports and then exporting the filled reports to PDF.
I know that you can use the built in functionality to get the reports to pull their own data, but my tests have shown that pushing the data to the reports is a whole bunch faster. My only issue with this is that I have no control over the reports that will be ran.
People will be required to provide their own login credentials for the SQL Server, so they will only be able to see data from the databases that they have permissions to... but some of the users have write permissions, and I'm worried that blindly running an sql string pulled from a Crystal Report could potentially allow for an insert/update/delete/injection attack...
I think that I might be worrying for nothing, but I can't find anything that outright states if this could be used for things aside from selects.
Edit:
So from the initial comments, I think that I do have to worry about SQL statements aside from SELECTs. So my question now becomes; is there some whay to specify that an SqlConnection can only be used for 'reads' (i.e. Selects).
The problem is not the adapter. The problem is, how you pass parameters to your sql command. You should not do things like
string sql = "SELECT * FROM t WHERE name='" + name +"'";
Instead use parameters:
SqlCommand cmd = new SqlCommand(SELECT * FROM t WHERE name = #name", conn);
SqlParameter param = new SqlParameter();
param.ParameterName = "#name";
param.Value = "John Doe";
cmd.Parameters.Add(param);
In general I would say: Yes, you have to.
But maybe Crystal Reports quotes the SQL-String already. Try an "attack" by yourself and see what sqlSelectStatement contains.

Best practices for using SqlCommand

I would like to make sure when using SqlCommand that I am using best practices, particularly with regards to security.
Considerations that I am not sure about:
Is it ok to manually build the string by appending? If not, how should I do it?
What classes should I be looking at using instead?
If your first question is talking about building SQL by including the values directly, that's almost certainly not okay. It opens you up to SQL injection attacks, as well as issues with conversions (e.g. having to get the right date/time format).
Instead, you should use a parameterized query, and set the values in the parameters. See the docs for SqlCommand.Parameters for an example.
Out of interest, do you have a particular reason for using SQL directly instead of using one of the many ORMs around? (LLBL, Entity Framework, NHibernate, LINQ to SQL, SubSonic, Massive, SimpleData, Dapper...)
I would say use of parameters is one of the most important aspects for security. This will prevent SQL Injection into your database. The following SQLCommand is an example of how I would construct one (in VB.NET, apologies - no C# knowledge - yet ;))
Dim cmd as New SqlCommand("sp_StoredProcedure", Conn)
cmd.commandType = commandtypes.storedprocedure
cmd.parameters.add("#ID",sqldbtype.int).value = myID
cmd.executenonquery
And an example of an inline SqlCommand:
Dim cmd as New SqlCommand("SELECT Name, Message FROM [Table] WHERE ID=#ID", Conn)
cmd.commandType = commandtypes.storedprocedure
cmd.parameters.add("#ID",sqldbtype.int).value = myID
cmd.executenonquery
My advice: be lazy. Writing voluminous code is a good way to make brain-dead errors (wrong data type, null checks, missing Dispose(), etc), and it has zero performance advantage over many of the helper tools.
Personally, I'm a big fan of dapper (but I'm somewhat biased), which makes things easy:
int customerId = ...
var orders = connection.Query<Order>(
#"select * from Customers where CustomerId = #customerId",
new { customerId });
Which will do parameterisation and materialisation for you without pain, and stupidly fast.
For other scenarios, and in particular when you want to use OO techniques to update the system, an ORM such as EF or L2S will save you work while (and giving you better type-checking via LINQ).
I think this is the best solution
SqlConnection cn = new SqlConnection(strCn);
try
{
using (SqlCommand cmd = new SqlCommand("select * from xxxx", cn))
{
cn.Open();
//do something
cn.Close();
}
}
catch (Exception exception)
{
cn.Close();
throw exception;
}
Depending, when I do POC or personnal small projects I normally build my strings manually but when im in a project for work we have a template for DB usage and connection that I must use and can't reveal here obviously.
But I think it's ok to manually build for basic operations in small project or POC.
Edit : Like Jon said though you should always use somehting like SqlCommand.Parameters when building your own command. Sorry if I wasn't clear.
If you're concerned about security, I recommend create your queries as Stored Procedures inside the SQL Server Database instead (to prevent worrying about SQL injection), and then from the front end code, just generate a SQL Stored Procedure, add the parameters, and do it that way.
This article should help you out with setting this up.
You should always use the practice of applying the least privileges to database connections by communicating with the database through stored procedures.
Store Procs
using System.Data;
using System.Data.SqlClient;
using (SqlConnection connection = new SqlConnection(connectionString))
{
DataSet userDataset = new DataSet();
SqlDataAdapter myCommand = new SqlDataAdapter("LoginStoredProcedure", connection);
myCommand.SelectCommand.CommandType = CommandType.StoredProcedure;
myCommand.SelectCommand.Parameters.Add("#au_id", SqlDbType.VarChar, 11);
myCommand.SelectCommand.Parameters["#au_id"].Value = SSN.Text;
myCommand.Fill(userDataset);
}
The credentials for the connection string to the database:
A. Integrated Security for corporate/intranet
B. Keep it encrypted in the registry or a file in Hosting Providers.
Always be on the lookout for hackers trying to upload cross scripting into your forms.
Always check the input for SQL injection.

Categories