IDataReader for Generic Access - c#

I'm trying to make a "Helper" class where you simply pass the driver and the parameters and the class does the connection and connection string assembly for you.
I've been using the interfaces from System.Data like IDataReader, IDbConnection.
Now after testing it with MySQL the code is working perfectly but as soon as I point and configure it for SQL Server (Microsoft) it does not return any processed rows. I have done some debugging and the info from the SQL server is appearing in the IDataReader but it seems I can't Iterate over it ?
My Current Code:
Connect Method in Helper Class
try
{
factory = System.Data.Common.DbProviderFactories.GetFactory(driver);
_con = factory.CreateConnection();
_con.ConnectionString = buildConnectionString.ToString();
_con.Open();
}
catch (System.Data.Common.DbException ex)
{
_con = null;
throw ex;
}
catch (Exception ex)
{
_con = null;
throw ex;
}
return _con;
A the moment I'm passing my driver as System.Data.SqlClient for SQL Server and MySql.Data.MySqlClient for MySQL.
System.Data.IDataReader reader = cmd.ExecuteReader(System.Data.CommandBehavior.CloseConnection);
while (reader.Read())
{
System.Data.DataRow row = table.NewRow();
// Insert info from Reader into the Row
table.Rows.Add(row);
}
reader.Close();
I suspects it has something to do with how IDataReader is trying to handle the types but can't find any documentation on this as it's working perfectly for MySQL but not for SQL Server? Any Help?

You aren't giving many clues here, since most of the interesting code here is probably around the command setup. If, at execution, it never enters the while (reader.Read()) {...} block, then it is probably TSQL or parameter related (especially nulls, which can easily result in no rows).
Since your data is DataTable-centric and you already have the provider-factory, another possibility here is to use CreateDataAdapter() from the factory, and let the factory worry about the binding of TSQL to a DataTable. Otherwise, treble-check that the TSQL you are providing is valid, sensible, and correctly parameterised.
Ultimately, the Read() loop itself is fine, and is pretty-much what all materialization routines do. It is, for example, very close to how dapper works, and that works fine over a range of databases.

Related

Simple SqlCacheDependency

Almost every tutorial I have read seems to incorrectly setup SqlCacheDependency. I believe they normally mix up the outdated polling method with the query notification method.
Here are two of many examples:
Web Caching with SqlCacheDependency Simplified (non-microsoft)
SqlCacheDependency Class (Microsoft)
Based on my testing, if you are using the broker (MSSQL 2015+) you don't need to make any .config changes nor do you need to make any SqlCacheDependencyAdmin calls (Don't need to define tables, etc).
I simplify just do this...
SqlDependency.Start(connString)
...
queryString = "SELECT ...";
cacheName = "SqlCache" + queryString.GetHashCode();
...
using (var connection = new SqlConnection(connString))
{
connection.Open();
var cmd = new SqlCommand(queryString, connection)
{
Notification = null,
NotificationAutoEnlist = true
};
var dependency = new SqlCacheDependency(cmd);
SqlDataReader reader = cmd.ExecuteReader();
try
{
while (reader.Read())
{
// Set the result you want to cache
data = ...
}
}
finally
{
reader.Close();
}
HostingEnvironment.Cache.Insert(cacheName, data, dependency);
}
(The code that checks if the cache is null or not is not included, as that's all just setup. I just want to show the setting of the cache)
This seems to work without the need to define which tables are involved in the query and make complicated triggers on each table. It just works.
More surprising to me is that the rules for making a query have notification :
Creating a Query for Notification (Can't find documentation newer than 2008) don't seem to apply. I purpose to do a TOP in my SQL and it still works.
For a test, I have it run a query 1000 times involving a table named "Settings". Then I update a value in the table and repeat the query.
I watch the Profiler for any queries involving the word "Settings" and I see the query is executed just 1 time (to set the cache) and then the update statement occurs, and then the query is re-executed one more time (the cache was invalidated and the query ran again)
I am worried that in my 2-3 hours of struggling with the proper way to do this I am missing something and it really is this simple?
Can I really just put any query I want and it'll just work? I am looking for any pointers where I am doing something dangerous/non-standard or any small print that I am missing
var dependency = new SqlCacheDependency(cmd);
when you write query like this you autiomatically define table name in it.Your connection already has db name.
It is non explicit way to do same.
Explicit way to catch exception and to know what went wrong is this.
// Declare the SqlCacheDependency instance, SqlDep.
SqlCacheDependency SqlDep = null;
// Check the Cache for the SqlSource key.
// If it isn't there, create it with a dependency
// on a SQL Server table using the SqlCacheDependency class.
if (Cache["SqlSource"] == null) {
// Because of possible exceptions thrown when this
// code runs, use Try...Catch...Finally syntax.
try {
// Instantiate SqlDep using the SqlCacheDependency constructor.
SqlDep = new SqlCacheDependency("Northwind", "Categories");
}
// Handle the DatabaseNotEnabledForNotificationException with
// a call to the SqlCacheDependencyAdmin.EnableNotifications method.
catch (DatabaseNotEnabledForNotificationException exDBDis) {
try {
SqlCacheDependencyAdmin.EnableNotifications("Northwind");
}
// If the database does not have permissions set for creating tables,
// the UnauthorizedAccessException is thrown. Handle it by redirecting
// to an error page.
catch (UnauthorizedAccessException exPerm) {
Response.Redirect(".\\ErrorPage.htm");
}
}
// Handle the TableNotEnabledForNotificationException with
// a call to the SqlCacheDependencyAdmin.EnableTableForNotifications method.
catch (TableNotEnabledForNotificationException exTabDis) {
try {
SqlCacheDependencyAdmin.EnableTableForNotifications("Northwind", "Categories");
}
// If a SqlException is thrown, redirect to an error page.
catch (SqlException exc) {
Response.Redirect(".\\ErrorPage.htm");
}
}
// If all the other code is successful, add MySource to the Cache
// with a dependency on SqlDep. If the Categories table changes,
// MySource will be removed from the Cache. Then generate a message
// that the data is newly created and added to the cache.
finally {
Cache.Insert("SqlSource", Source1, SqlDep);
CacheMsg.Text = "The data object was created explicitly.";
}
}
else {
CacheMsg.Text = "The data was retrieved from the Cache.";
}
As documented in https://learn.microsoft.com/en-us/dotnet/api/system.web.caching.sqlcachedependency?view=netframework-4.8 "Using a SqlCacheDependency object with SQL Server 2005 query notification does not require any explicit configuration."
So, the CMD has explicit table names in it, and ADO.net is issuing the correct Service Broker configuration commands for you. When the table is updated, SQL Server posts a Service Broker message saying the table has been updated. When ADO.net validates the CMD it checks the explicit tables in the broker for updates.
This is why the SQlCacheDependency associated CMD must use explicit tables.

How avoid SQL Injection without stored procedure In C# Desktop Application

I am developing C# desktop appllication using MS SQL server database.
I Keep different class as follow connect to database.
using System.Data.Odbc;
class DataBaseConnection
{
private OdbcConnection conn1 = new OdbcConnection(#"FILEDSN=C:/OTPub/Ot.dsn;" + "Uid=sa;" + "Pwd=otdata#123;"); //"DSN=Ot_DataODBC;" + "Uid=sa;" + "Pwd=otdata#123;"
//insert,update,delete
public int SetData(string query)
{
try
{
conn1.Open();
OdbcCommand command = new OdbcCommand(query, conn1);
int rs = command.ExecuteNonQuery();
conn1.Close();
return rs;
}
catch (Exception ex)
{
conn1.Close();
throw ex;
}
}
//select
public System.Data.DataTable GetData(string sql)
{
try
{
conn1.Open();
OdbcDataAdapter adpt = new OdbcDataAdapter(sql, conn1);
DataTable dt = new DataTable();
adpt.Fill(dt);
conn1.Close();
return dt;
}
catch (Exception ex)
{
conn1.Close();
throw ex;
}
}
}
in my reqierd place i make object to that DatabaseConnection class and call to get and set method as requirment.
as an example ----
DataBaseConnection db = new DataBaseConnection();
string SaveNewEmp = "INSERT INTO Employee (Service_ID, Title, Name, Initials, ) VALUES ('" + servicenumber + "','" + title + "','" + fullname + "','" + initials + "')";
int returns = db.SetData(SaveNewEmp);
am i allow to SQl injection from this method?
how avoid sql injection without using stored procedure?
You avoid SQL Injection the same way as you would anywhere else - by keeping SQL code separate from data. You can't do that if you insist on having the interface be based on just passing in a string.
I'd get rid of your wrapper class (it's just obscuring things) and make use of Parameters to pass the data alongside your query.
(I'd also recommend that you just use using statements around the various database objects rather than your current manual efforts to ensure Close is called which is also slightly breaking good error handling by re-throwing exceptions)
(Also, I'd recommend using new OdbcConnection objects wherever you need them rather than trying to share a single one - you'll be thankful you've done this as soon as any notion of multi-threading enters your codebase, which is practically inevitable these days)
Most important technique is to used bind variables like this:
string SaveNewEmp =
"INSERT INTO Employee (Service_ID, Title, Name, Initials) VALUES (?, ?, ?, ?)";
command.Parameters.Add("#servicenumber", OdbcType.Int).Value = ...;
command.Parameters.Add("#title", OdbcType.VarChar).Value = ...;
command.Parameters.Add("#fullname ", OdbcType.VarChar).Value = ...;
command.Parameters.Add("#initials ", OdbcType.VarChar).Value = ...;
Usually this lead also into a performance gain and you don't have to take care about quoting, imagine the title would be It's your day - this would fail with your approach.
Update
Using a list of parameters is straight forward:
public int SetData(string query, OdbcParameterCollection parList)
{
...
OdbcCommand command = new OdbcCommand(query, conn1);
OdbcCommand.Parameters.Add(parList);
}
var parList = new OdbcParameterCollection();
parList.Add("#servicenumber", OdbcType.Int);
parList.Add("#title", OdbcType.VarChar);
...
int ret = SetData(query, parList);
However, I did not test it perhaps you have to run
foreach ( OdbcParameter aPar in parList ) {
OdbcCommand.Parameters.Add(aPar);
}
Using List<>
Damien_The_Unbeliever's answer is mostly good, but I would like to improve it/ version it.
You can also change the SetData and GetData methods and add them an array of parameters (although I do share his/her thoughts in getting rid of the class, you could make it abstract to make more specifica DAL classes).
The only requirement to avoid SQL Injection is using parameters, either using stored procedures (impossible due to question requirements) or queries written in your code.
There is a slight chance of getting into SQL Injection even if using parameters, if the SQL executed (in coded query or in the stored procedure) does use sp_execute_sql or similar. In case of using sp_execute_sql in your query, make sure to avoid writting it with user provided info. You can set parameters to the sp_execute_sql function as a second optional parameter.
This is a recurring (for the last 20 years at least) question, but I earnestly believe I have a new answer... use QueryFirst. You get the advantages of stored procedures, but your SQL lives in .sql files in your application, versioned with your app. You create parameters just by referencing them in your SQL. All the parameter handling code is generated for you. You (and your team) have to use parameters because there's no other way. The possibility of doing something unsafe is removed. And there are tons of other advantages: you edit your sql in a real environment, with syntax validation and intellisense. Your queries are continually integration tested against your db, and their wrapper code regenerated. All errors are trapped as you type, or when you save a .sql, or, last resort, when you build. In theory, there are no runtime errors from data access.
Usually rigour comes at the cost of simplicity/ease-of-dev. This approach is much more rigorous and much easier to use than the traditional sql-in-string-literals approach. Coming soon, language and platform portability. Drag and drop a sql query from C# project on windows into a Node express app on linux or mac, rebuild, and you get a typescript wrapper instead of a C# one. That should get folk's attention.
disclaimer: I wrote QueryFirst.
Download here.
Little blog here.

VSTO Addin Connecting to MSSQL

I have a VSTO addin. Right now I am hard coding two dictionaries for development. They need to persist so I am going to use mssql. I have created the tables and all, so everything is done on that end. But I'm having some trouble getting a few things to work so I have a few questions:
First, is it possible to use Windows Forms in VSTO to create a CRUD form for the database tables? There are 2 tables and they aren't too long.
Next, when connecting with SqlConnection, the example connection strings I am seeing don't make any sense. Here is an example: tmpConn = new SqlConnection("server=(local)\\SQLEXPRESS;Trusted_Connection=yes"); I'm not sure how to make the connection string. Right now my db is a local mssql instance, but in the future it will be hosted on a local network server. It says SQLEXPRESS now, is that the same as mssql? If not how can I change it to mssql? How do I construct the correct string for mssql and how do I authenticate? Is it done in the connection string or somewhere else?
Next to Last, is there an easy way to query a database with 3 columns, ID (PK), domain, and dirname. And place domain and dirname into a dict where domain is the key and dirname the value, the ID is uneeded for this.
Here is what I have so far:
public void retrieveClient(SqlConnection conn)
{
try
{
using (conn)
{
SqlCommand command = new SqlCommand(
"SELECT ClientDirName, ClientEmailDomain FROM ClientTable;",
conn);
conn.Open();
SqlDataReader reader = command.ExecuteReader();
if (reader.HasRows)
{
while (reader.Read())
{
string clientDir = reader.GetString(0);
string clientEmail = reader.GetString(1);
clientDict.Add(clientEmail, clientDir);
}
}
else
{
MessageBox.Show("No rows found in ClientTable", "Rows Not Found", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
reader.Close();
}
}
catch (InvalidOperationException ex)
{
MessageBox.Show(String.Format("Exception while accessing ClientTable: {0}", ex), "Exception", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
catch (SqlException ex)
{
MessageBox.Show(String.Format("Exception while accessing ClientTable: {0}", ex), "Exception", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
And finally, the part where it says string clientDir = reader.GetString(0) and string clientEmail = reader.GetString(1) Will it return the correct values here? The table has 3 values as I said, ID, Dir, and Email. But as you can see my SqlCommand only selects ClientDirName and ClientEmailDomain. Does this mean the GetString() methods will return those with 0 and 1? Or would it be 1 and 2 since ID is 0? I'm not sure how it works, does reader.GetString() base the indexes off the actual table or the SqlCommand?
I also wanted to know if the while (reader.Read()) part will add each row one by one so that my clientDict.Add() would work properly or not.
If it possible to do CRUD with a database with windows forms please give me some guidance or a small example. Other than that I just need my code examined to make sure it's doing what I want. I've been trying to test it but there are a lot of other missing pieces at the moment so I need assistance.
Any assistance would be much appreciated. And if someone could provide a few small example connection strings for both local and network mssql servers that require authentication it would be a huge help. I've been looking everywhere and can't get this working. I've heard EF could do it but I can't find a single example or guide that does what I need. If someone could share one that may help too, if you with EF is the way to go.
Thanks in advance!
You may treat your VSTO based add-in as a regular .Net application. There is no anything specific from the Db perspective. The only difference is that your VSTO assembly is run within the host application (not standalone). So, I'd suggest reading any good book on how to use SQL databases (ADO.NET) in .net applications or use EF (Entity Frameworks) first. It will give you the answer to all your questions.

preventing my application from sql injection?

I am making a project in which i have a login page.
i am restricting user to enter
AND OR NOT XOR & | ^
is this enough to prevent my application from SQL Injection?
No, not at all.
For example, I could still enter my username as:
; DELETE FROM Users --
Which could still, depending on your DB structure and application code, wipe your entire Users table.
To adequately protect yourself from SQL Injection attacks you should escape any user input and use either parameterized queries or stored procedures (and if you're using stored procedures, be sure you don't have dynamically generated SQL inside the stored procedure) to interact with the database.
You shouldn't bother looking for special words / characters in their username /password. Because you will ALWAYS miss something.
Instead, if you have embedded SQL you should be using parameterized queries. If you do that for all of your queries then you'll be safe from sql injection. Now, XSS is whole other matter.. ;)
This has been covered in depth on this site, just search for sql injection.
Using Stored Procedures or parameterized queries will prevent SQL injection.
1) In addition to that, if you are using ASP.NET, you can enable the page level attribute "ValidateRequest = True" which can validate if any of the input string can lead to Script injection
2) Make sure you dont display the actual system generated error to the end user. That will give a lead to the hacker to probe further and break the system.
3) If you are using a webservice to consume and sync the data to your database, validate all the necessary fields before persisting the data.
Definitely not!
The simplest possible way to avoid SQL injection is by using parameterized queries.
See this SO question: Preventing SQL Injection in ASP.Net VB.Net and all of its answers to give you an idea.
In short, I never use concatenated string queries, but ALWAYS parameters. This way, there is no danger at all, and this is the most secure way to prevent SQL injection.
Here is a good stack overflow link: What is SQL injection?
Secondly, don't forget that it doesn't matter what validation you do in the UI, people can always construct custom HTTP requests and send them to your server (trivial as editing using firebug).
Like others said. Parameterized inputs.
Here's a snipit from some code I wrote at work(removed work specific code). It's not perfect, but my main job is not programming and I was still researching on C# when I wrote this. If I wrote this now, I would have used a datareader instead of a dataset.
But notice how I use variables in the actual SQL string and assign the variables using "da.SelectCommand.Parameters.AddWithValue"
public Boolean Login(string strUserName, string strPassword)
{
SqlConnection sqlConn = new System.Data.SqlClient.SqlConnection();
DataSet ds = null;
SqlDataAdapter da = null;
sqlConn.ConnectionString = strConnString;
try
{
blnError = false;
sqlConn.Open();
ds = new DataSet();
da = new SqlDataAdapter("select iuserid from tbl_Table where vchusername = #vchUserName and vchpassword = #vchPassword", sqlConn);
da.SelectCommand.Parameters.AddWithValue("#vchUserName", strUserName);
da.SelectCommand.Parameters.AddWithValue("#vchPassword", strPassword);
da.SelectCommand.CommandTimeout = 30;
da.Fill(ds);
if (ds.Tables[0].Rows.Count > 0)
{
iUserId = (int)ds.Tables[0].Rows[0]["iuserid"];
}
}
catch (Exception ex)
{
blnError = true;
Log("Login: " + ex.Message);
}
finally
{
if (sqlConn.State != ConnectionState.Closed)
sqlConn.Close();
if (da != null)
da.Dispose();
if (ds != null)
ds.Dispose();
}
if (blnError)
return false;
if (iUserId > 0)
return true;
return false;
}
You should pass the values as parameters to a stored procedure. This way whatever the user enters is just treated as a value rather than appended to the statement and executed

Checking whether a database is available?

My problem involves checking if I have a valid database connection before reading from the database. If the database is down I'd like to write to a xml file instead. I have the location of the database (if it's up) at runtime so if the database was working I can create a new sqlConnection to it.
Use a typical try...catch...finally structure, and based on the specific exception type and message, decide whether you want to write to xml or not.
try
{
SqlConnection connection = new SqlConnection(DB("Your DB Name"));
connection.Open();
}
catch (Exception ex)
{
// check the exception message here, if it's telling you that the db is not available. then
//write to xml file.
WriteToXml();
}
finally
{
connection.Close();
}
I would just use something like:
using(SqlConnection conn = new SqlConnection(c)) {
conn.Open();
}
It will throw an exception if invalid. You could write to the xml in the exception.
An easy way would be to execute a simple query and see if an error occurs:
For Oracle:
SELECT * FROM DUAL
For SQL Server
SELECT 1
Basicly just some kind of relatively "free" query that will let you know that the database is up and running and responding to requests and your connection hasn't timed out.
You cannot really tell whether the DB is up and running without actually opening a connecting to it. But still, connection might be dropped while you're working with it, so this should be accounted for.

Categories