Parameterize WHERE Clause in Query - c#

Environment:
C#
Visual Studio 2012
.NET Framework 3.5
Hi
Could I parameterize where clause in SQL Server?
In my scenario, once a WHERE clause String is input, application will concatenate it to other part of query and execute in SQL Server then return the result.
For example,
User inputs "[CookingTime] < 30 and [Cost] < 20"
Application creates query "select [RecipeID] from [Recipes] where [CookingTime] < 30 and [Cost] < 20" and executes in SQL Server.
Application returns result to user.
For security reason, I would like to make whole WHERE CLAUSE as parameter.
But I have no idea how to achieve.
Thanks in advance.

This is how it can be done
string commandText = "UPDATE Sales.Store SET Demographics = #demographics "
+ "WHERE CustomerID = #ID;";
using (SqlConnection connection = new SqlConnection(connectionString))
{
SqlCommand command = new SqlCommand(commandText, connection);
command.Parameters.Add("#ID", SqlDbType.Int);
command.Parameters["#ID"].Value = customerID;
// Use AddWithValue to assign Demographics.
// SQL Server will implicitly convert strings into XML.
command.Parameters.AddWithValue("#demographics", demoXml);
try
{
connection.Open();
Int32 rowsAffected = command.ExecuteNonQuery();
Console.WriteLine("RowsAffected: {0}", rowsAffected);
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}

The whole WHERE clause as parameter will be a victim of sql injection in any way. To prevent this you'd better to:
Setup proper permissions. So even in case of sql injected user can't access anything not granted. In this case sample of #Dhaval is better, because dymanic sql generation incapsulated in stored procedure requires less permissions to execute.
Check the statement for sql injection. The simplest way is to check for semicolons in order to avoid another statements in the batch. More complex and more precise way is to use t-sql DOM parser. For example:
using Microsoft.SqlServer.TransactSql.ScriptDom;
TSql110Parser parser = new TSql110Parser(true);
IList<ParseError> errors = null;
var condition = "a > 100; delete from [Recipes]";
var script = parser.Parse(new StringReader("select [RecipeID] from [Recipes] where " + condition), out errors) as TSqlScript;
if (errors.Count > 0)
{
throw new Exception(errors[0].Message);
}
foreach (var batch in script.Batches)
{
if (batch.Statements.Count == 1)
{
var select = batch.Statements[0] as SelectStatement;
if (select != null)
{
QuerySpecification query = select.QueryExpression as QuerySpecification;
if (query.WhereClause is BooleanBinaryExpression)
{
...
}
}
else
{
throw new Exception("Select statement only allowed");
}
}
else
{
throw new Exception("More than one statement detected");
}
}

You can create a dynamic query in sql server and pass the parameter from C#
Something like this
Create Procedure usp_Test
#WhereCond Varchar(max)
AS
Bgein
Set NoCount ON
Declare #SQLQuery AS Varchar(max)
Set #SQLQuery = 'Select * From tblEmployees where ' + #WhereCond
Execute sp_Executesql #SQLQuery
End
C# Code to execute the procedure
DataSet ds = new DataSet();
using(SqlConnection conn = new SqlConnection("ConnectionString"))
{
SqlCommand sqlComm = new SqlCommand("usp_Test", conn);
sqlComm.Parameters.AddWithValue("#WhereCond", WhereCond);
sqlComm.CommandType = CommandType.StoredProcedure;
SqlDataAdapter da = new SqlDataAdapter();
da.SelectCommand = sqlComm;
da.Fill(ds);
}

I guess the original question wanted to find out how to make it dynamically from user's input and then use proper sql parameter to do the query.
For the usage of sql parameter, normally what I do is to use a generic helper method, a quick example (not tested):
public static class SqlHelpers
{
public static IEnumerable<T> ExecuteAdhocQuery<T>(SqlConnection con, string sql, CommandType cmdType, Func<SqlDataReader, T> converter, params SqlParameter[] args)
{
try
{
using (SqlCommand cmd = new SqlCommand(sql, con) { CommandType = cmdType })
{
cmd.Parameters.AddRange(args);
if (con.State != ConnectionState.Open) { con.Open(); }
var ret = new List<T>();
using (SqlDataReader rdr = cmd.ExecuteReader())
{
while (rdr.Read())
{
ret.Add(converter.Invoke(rdr));
}
}
return ret;
}
}
catch (Exception e)
{
// log error?
Console.WriteLine(e.Message);
Console.WriteLine(e.StackTrace);
throw e; // handle exception...
}
}
public void Test()
{
using (SqlConnection con = new SqlConnection("connection string here"))
{
var data = ExecuteAdhocQuery(con,
"SELECT ID, Name FROM tblMyTable WHERE ID = #Id and Status = #Status;",
CommandType.Text, (x) => new { Id = x.GetInt32(0), Name = x.GetString(1) },
new SqlParameter("#Id", SqlDbType.Int) { Value = 1 },
new SqlParameter("#Status", SqlDbType.Bit) { Value = true });
Console.WriteLine(data.Count());
}
}
}
of course, this is only Reading, for Insert/Update, similar methods could be created too.
But the complicated part is how to make it dynamic with unknown number of conditions and the relationship between them. So a quick suggestion is use a delegated method or class to do the work. sample (not tested):
public static Dictionary<string, SqlParameter> GetParamsFromInputString(string inputString)
{
var output = new Dictionary<string, SqlParameter>();
// use Regex to translate the input string (something like "[CookingTime] < 30 and [Cost] < 20" ) into a key value pair
// and then build sql parameter and return out
// The key will be the database field while the corresponding value is the sql param with value
return output;
}
public void TestWithInput(string condition)
{
var parameters = GetParamsFromInputString(condition);
// first build up the sql query:
var sql = "SELECT Id, Name from tblMyTable WHERE " + parameters.Select(m => string.Format("{0}={1}", m.Key, m.Value.ParameterName)).Aggregate((m,n) => m + " AND " + n);
using (SqlConnection con = new SqlConnection("connection string here"))
{
var data = ExecuteAdhocQuery(con,
sql,
CommandType.Text,
(x) => new { Id = x.GetInt32(0), Name = x.GetString(1) },
parameters.Select(m => m.Value).ToArray());
}
}
for the static function GetParamsFromInputString, it's just a sample. actually it could be very complicated depending on your needs.
for example, you might want to include the operator (whether it's >, < or <>,...).
and you might also want to include the conjunctions between the conditions, whether it's AND or OR.
Build delegated classes to do the job if it's very complicated.

Related

DataGrid - SQL insert query error c#

I'm trying to adding to table access and datagrid row with sql query. buy without a success. Any ideas ? Thank's
My sql query :
DataBaseIkuns.Instance.InsertToDB(string.Format(DictionaryUtilsDB.dictioneary[DictionaryUtilsDB.CommendTypes.AddObserver], o.ID_Observer, o.Lat, o.Long, o.azimuth));
public static Dictionary<CommendTypes, string> dictioneary = new Dictionary<CommendTypes, string>
{
{CommendTypes.AddObserver,"Insert into ShowTableObserver(ID_Ob,Lat,Long,Azimuth)"
+"values('{0}','{1}','{2}','{3}')"},
{CommendTypes.AzimuthLongLatFromOB,"SELECT ID_Observer,Longitude,Latitude,Azimuth FROM Observer Where ID_Observer = {0}"}
};
public void InsertToDB(string sql) // It get the right values - 1,2,3,4
{
int insert = 0;
try
{
if (con.State.ToString()== "Open")
{
cmd = new OleDbCommand();
oledbAdapter = new OleDbDataAdapter();
dt = new DataTable();
cmd.Connection = con;
cmd.CommandText = sql;
insert = cmd.ExecuteNonQuery(); // Here it jump's to the catch. why ?
if (insert > 0)
{
MessageBox.Show("Your Insert successed");
}
else
{
MessageBox.Show("Your Insert failed");
}
}
}
catch (Exception ex)
{
MessageBox.Show(ex.ToString());
}
}
Have a comment in the code where the program falls.
The value of sql when it falls :
Insert into ShowTableObserver(ID_Ob,Lat,Long,Azimuth)values('3','31.4','34','150')
If you use an Access Database behind the OleDb provider then you have a problem with the word LONG. It is a reserved keyword (and probably it is the same in many other database systems). In this case you need to encapsulate the field name in square brackets
{CommendTypes.AddObserver,"Insert into ShowTableObserver(ID_Ob,Lat,[Long],Azimuth)"
Said that you need to start to use parameterized query. Your string.Format is another kind of string concatenation that leads to Sql Injection, parsing problems and subtle syntax error when you miss a single quote or other type specifier in your query string
For example
public static Dictionary<CommendTypes, string> dictioneary = new Dictionary<CommendTypes, string>
{
{CommendTypes.AddObserver,"Insert into ShowTableObserver(ID_Ob,Lat,Long,Azimuth)"
+"values(?,?,?,?)"},
{CommendTypes.AzimuthLongLatFromOB,"SELECT ID_Observer,Longitude,Latitude,Azimuth "
+"FROM Observer Where ID_Observer = ?"}
};
public void InsertToDB(string sql, List<OleDbParameter> parameters)
{
int insert = 0;
try
{
if (con.State.ToString()== "Open")
{
using(cmd = new OleDbCommand());
{
cmd.Connection = con;
cmd.CommandText = sql;
cmd.Parameters.AddRange(parameters.ToArray());
insert = cmd.ExecuteNonQuery();
}
........
}
}
......
}
Now when you call the InsertDB you write
DataBaseIkuns.Instance.InsertToDB(string.Format(DictionaryUtilsDB.dictioneary[DictionaryUtilsDB.CommendTypes.AddObserver], , o.Lat, o.Long, o.azimuth));
List<OleDbParameter> parameters = new List<OleDbParameter>();
parameters.Add(new OleDbParameter())
{
ParameterName = "#p1", OleDbType= OleDbType.VarWChar, Value = o.ID_Observer
}
parameters.Add(new OleDbParameter())
{
ParameterName = "#p2", OleDbType= OleDbType.VarWChar, Value = o.Lat
}
parameters.Add(new OleDbParameter())
{
ParameterName = "#p3", OleDbType= OleDbType.VarWChar, Value = o.Long
}
parameters.Add(new OleDbParameter())
{
ParameterName = "#p4", OleDbType= OleDbType.VarWChar, Value = o.Azimuth
}
DataBaseIkuns.Instance.InsertToDB(
DictionaryUtilsDB.dictioneary[DictionaryUtilsDB.CommendTypes.AddObserver], parameters);

Oracle stored procedure works with ADO.NET but with OrmLite throws exception?

I have a following stored procedure:
create or replace PROCEDURE PRODUCT_DETAILS(p_code IN VARCHAR2,
cursorParam OUT SYS_REFCURSOR)
IS
BEGIN
OPEN cursorParam FOR
select str_auth_code, str_name
from strs
where str_auth_code = p_code;
END;
How can I call it with OrmLite? I've tryied:
connection.SqlList<Product>(#"EXEC PRODUCT_DETAILS #p_code", new { p_code = code });
but it throws an exception ORA-01036: illegal variable name/number
I just tried to do it with plain old ADO.NET and it worked:
using (var conn = new OracleConnection(connectionString))
{
OracleCommand cmd = new OracleCommand();
cmd.Connection = conn;
cmd.CommandText = "PRODUCT_DETAILS";
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.Add("p_code", OracleType.NVarChar).Value = redemptionCode;
cmd.Parameters.Add("cursorParam", OracleType.Cursor);
cmd.Parameters["cursorParam"].Direction = ParameterDirection.Output;
conn.Open();
OracleDataReader dr = cmd.ExecuteReader();
while (dr.Read())
{
Console.WriteLine(dr["Name"]);
}
conn.Close();
}
But I can't figure out how to do the same task with OrmLite.
What you have looks good. If you were concerned about the verbosity of the code, and were using a number of stored procedures, then you could use this extension method to remove some of the repeated code:
Extension Method:
public static class StoredProcExtensions
{
public static List<T> ExecStoredProcedure<T>(this IDbConnection connection, string procedureName, object parameters = null, string outputCursor = "cursorParam")
{
return connection.Exec(c => {
c.CommandText = procedureName;
c.CommandType = CommandType.StoredProcedure;
// Create the parameters from the parameters object
if(parameters != null)
foreach(var property in parameters.GetType().GetPublicProperties())
c.Parameters.Add(new OracleParameter(property.Name, property.GetValue(parameters)));
// Add the output cursor
if(outputCursor != null)
c.Parameters.Add(new OracleParameter(outputCursor, OracleDbType.RefCursor) { Direction = ParameterDirection.Output });
// Return the result list
return c.ExecuteReader().ConvertToList<T>();
});
}
}
Usage:
var download = connection.ExecStoredProcedure<ProductDownloads>(
"PRODUCT_DETAILS",
new { p_code = redemptionCode }
);
foreach (var productDownload in download)
{
Console.WriteLine(productDownload.Name);
}
So the arguments are:
Stored procedure name i.e. PRODUCT_DETAILS
Optional An object of input parameters i.e new { p_code = redemptionCode, other = "value" }
Optional The name of the output cursor - defaults to cursorParam
Note: this code is untested, because I don't have Oracle setup, but it does compile, and hopefully goes some way to simplifying your stored procedures.
So far ended up with following code:
using (var connection = factory.Open())
{
var download =
connection.Exec(c =>
{
c.CommandText = "PRODUCT_DETAILS";
c.CommandType = CommandType.StoredProcedure;
c.Parameters.Add(
new Oracle.DataAccess.Client.OracleParameter("p_code", Oracle.DataAccess.Client.OracleDbType.NVarchar2) { Value = redemptionCode });
c.Parameters.Add(
new Oracle.DataAccess.Client.OracleParameter("cursorParam", Oracle.DataAccess.Client.OracleDbType.RefCursor) { Direction = ParameterDirection.Output });
return c.ExecuteReader().ConvertToList<ProductDownloads>();
});
foreach (var productDownload in download)
{
Console.WriteLine(productDownload.Name);
}
}
But I think there should be a better way for doing this.
Late to the party, but this problem can be solved by simply adding BEGIN and END to the statement... it will work.
In my case, I was trying to refresh a materialized view, only after adding BEGIN and END will it work, otherwise it will throw OracleException ORA-00900: invalid SQL statement...
This should work:
db.ExecuteSql("BEGIN DBMS_SNAPSHOT.REFRESH('" + materializedViewName + "'); END;");

Why is my SQL Server CE code failing?

In my WindowsCE / Compact Framework (.NET1.1) project, I need to create a new table in code. I thought I could do it this way:
if (! TableExists("table42"))
{
CreateTable42();
}
public static bool TableExists(string tableName)
{
try
{
using (SqlCeConnection sqlConn = new SqlCeConnection(#"Data Source=\my documents\Platypus.SDF"))
{
sqlConn.Open();
string qryStr = "SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = ?";
SqlCeCommand cmd = new SqlCeCommand(qryStr, sqlConn);
cmd.Parameters[0].Value = tableName;
cmd.CommandType = CommandType.Text;
int retCount = (int)cmd.ExecuteScalar();
return retCount > 0;
}
}
catch (Exception ex)
{
MessageBox.Show("TableExists ex.Message == " + ex.Message);
MessageBox.Show("TableExists ex.ToString() == " + ex.ToString());
MessageBox.Show("TableExists ex.GetBaseException() == " + ex.GetBaseException());
return false;
}
}
...but the call to TableExists() fails; and shows me:
TableExists ex.Message ==
TableExists ex.ToString() == System.Data.SqlServerCe.SqlCeException at System.Data.SqlServerCe.SqlConnection.ProcessResults(Int32 hr) at ...at Open(boolean silent) ...
TableExists ex.GetBaseException() == [same as ex.ToString() above]
"Int32 hr" ... ??? What the Hec Ramsey is that?
As documented previously in these environs, I can't step through this projct, so I rely on those calls to MessageBox.Show().
The rest of the related code, if it may be of interest, is:
public static void CreateTable42()
{
try
{
using (SqlCeConnection con = new SqlCeConnection(#"Data Source=\my documents\Platypus.SDF"))
{
con.Open();
using (SqlCeCommand com = new SqlCeCommand(
"create table table42 (setting_id INT IDENTITY NOT NULL PRIMARY KEY, setting_name varchar(40) not null, setting_value(63) varchar not null)", con))
{
com.ExecuteNonQuery();
WriteSettingsVal("table42settingname","table42settingval");
}
}
}
catch (Exception ex)
{
MessageBox.Show("CreateTable42 " + ex.Message);
}
}
public static void WriteSettingsVal(string settingName, string settingVal)
{
using (SqlCeConnection sqlConn = new SqlCeConnection(#"Data Source=\my documents\Platypus.SDF"))
{
sqlConn.Open();
string dmlStr = "insert into tabld42 (setting_name, setting_value) values(?, ?)";
SqlCeCommand cmd = new SqlCeCommand(dmlStr, sqlConn);
cmd.CommandType = CommandType.Text;
cmd.Parameters[0].Value = settingName;
cmd.Parameters[1].Value = settingVal;
try
{
cmd.ExecuteNonQuery();
}
catch (Exception ex)
{
MessageBox.Show("WriteSettingsVal " + ex.Message);
}
}
}
UPDATE
Answer to Brad Rem's comment:
I don't think it's necessary to encase the param in quotes, as other working code is like:
cmd.Parameters.Add("#account_id", Dept.AccountID);
-and:
cmd.Parameters[0].Value = Dept.AccountID;
(it does it one way the first time when in a loop, and the other way thereafter (don't ask me why).
Anyway, just for grins, I did change the TableExists() parameter code from this:
cmd.Parameters[0].Value = tableName;
...to this:
cmd.Parameters.Add("#TABLE_NAME", tableName);
...but I still get the exact same result.
UPDATE 2
Here (http://msdn.microsoft.com/en-us/library/aa237891(v=SQL.80).aspx) I found this: "Caution You must specify the SQL Server CE provider string when you open a SQL Server CE database."
They give this example:
cn.ConnectionString = "Provider=Microsoft.SQLSERVER.OLEDB.CE.2.0; data source=\Northwind.sdf"
I'm not doing that; my conn str is:
using (SqlCeConnection sqlConn = new SqlCeConnection(#"Data Source=\my documents\CCRDB.SDF"))
Could that be my problem?
UPDATE 3
I took this gent's advice (http://www.codeproject.com/Answers/629613/Why-is-my-SQLServer-CE-code-failing?cmt=487657#answer1) and added a catch for SqlCeExcpetions so that it is now:
public static bool TableExists(string tableName)
{
try
{
using (SqlCeConnection sqlConn = new SqlCeConnection(#"Data Source=\my documents\CCRDB.SDF"))
{
sqlConn.Open();
string qryStr = "SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = #TABLE_NAME";
SqlCeCommand cmd = new SqlCeCommand(qryStr, sqlConn);
cmd.Parameters.Add("#TABLE_NAME", tableName);
cmd.CommandType = CommandType.Text;
int retCount = (int)cmd.ExecuteScalar();
return retCount > 0;
}
}
catch (SqlCeException sqlceex)
{
MessageBox.Show("TableExists sqlceex.Message == " + sqlceex.Message);
MessageBox.Show("TableExists sqlceex.ToString() == " + sqlceex.ToString());
return false;
. . .
The SqlCeException message is: "There is a file sharing violation. A different process might be using the file [,,,,,]" then "...processresults ... open ... getinstance ..."
UPDATE 4
Trying to use ctacke's sample code, but: Is Transaction absolutely necessary? I had to change the code to the following for my scenario/milieu, and don't know what Transaction should be or how to build it:
public static bool TableExists(string tableName)
{
string sql = string.Format("SELECT COUNT(*) FROM information_schema.tables WHERE table_name = '{0}'", tableName);
try
{
using (SqlCeConnection sqlConn = new SqlCeConnection(#"Data Source=\my documents\HHSDB.SDF"))
{
SqlCeCommand command = new SqlCeCommand(sql, sqlConn);
//command.Transaction = CurrentTransaction as SqlCeTransaction;
command.Connection = sqlConn;
command.CommandText = sql;
int count = Convert.ToInt32(command.ExecuteScalar());
return (count > 0);
}
}
catch (SqlCeException sqlceex)
{
MessageBox.Show("TableExists sqlceex.Message == " + sqlceex.Message);
return false;
}
}
UPDATE 5
With this code, the err msg I get is, "An err msg is available for this exception but cannot be displayed because these messages are optional and are not currently insallted on this device. Please install ... NETCFv35.Messages.EN.cab"
UPDATE 6
All too typically, this legacy, ancient-technology project is giving me headaches. It seems that only one connection is allowed to be open at a time, and the app opens one from the outset; so, I have to use that connection. However, it is a DBConnection, not a SqlCeConnection, so I can't use this code:
using (SqlCeCommand com = new SqlCeCommand(
"create table hhs_settings (setting_id int identity (1,1) Primary key, setting_name varchar(40) not null, setting_value(63) varchar not null)", frmCentral.dbconn))
{
com.ExecuteNonQuery();
WriteSettingsVal("beltprinter", "ZebraQL220");
}
...because the already-open connection type passed as an arg to the SqlCeCommand constructor is DBCommand, not the expected/required SqlCeConneection.
The tentacles of this code are far too wide and entrenched to rip out by the roots and refactor to make it more sensible: a single tentative step in the foothills causes a raging avalanche on Everest.
For fun I'd try two things. First, replace the '?' parameter with a named parameter like '#tablename' and see if that changes things. Yes, I know '?' should work, but it's a confusing, ugly precedent and maybe since it's a system table it's wonky. Yes, it's a stretch, but worth a try just to know.
The second thing I'd do is something like this method from the SQLCE implementation of the OpenNETCF ORM:
public override bool TableExists(string tableName)
{
var connection = GetConnection(true);
try
{
using (var command = GetNewCommandObject())
{
command.Transaction = CurrentTransaction as SqlCeTransaction;
command.Connection = connection;
var sql = string.Format("SELECT COUNT(*) FROM information_schema.tables WHERE table_name = '{0}'", tableName);
command.CommandText = sql;
var count = Convert.ToInt32(command.ExecuteScalar());
return (count > 0);
}
}
finally
{
DoneWithConnection(connection, true);
}
}
Note that I didn't even bother parameterizing, largely because I doubt it will provide any perf benefit (queue the hordes whining about SQL injection). This way definitely works - we've got it deployed and in use in many live solutions.
EDIT
For completeness (though I'm not sure it adds to clarity).
protected virtual IDbConnection GetConnection(bool maintenance)
{
switch (ConnectionBehavior)
{
case ConnectionBehavior.AlwaysNew:
var connection = GetNewConnectionObject();
connection.Open();
return connection;
case ConnectionBehavior.HoldMaintenance:
if (m_connection == null)
{
m_connection = GetNewConnectionObject();
m_connection.Open();
}
if (maintenance) return m_connection;
var connection2 = GetNewConnectionObject();
connection2.Open();
return connection2;
case ConnectionBehavior.Persistent:
if (m_connection == null)
{
m_connection = GetNewConnectionObject();
m_connection.Open();
}
return m_connection;
default:
throw new NotSupportedException();
}
}
protected virtual void DoneWithConnection(IDbConnection connection, bool maintenance)
{
switch (ConnectionBehavior)
{
case ConnectionBehavior.AlwaysNew:
connection.Close();
connection.Dispose();
break;
case ConnectionBehavior.HoldMaintenance:
if (maintenance) return;
connection.Close();
connection.Dispose();
break;
case ConnectionBehavior.Persistent:
return;
default:
throw new NotSupportedException();
}
}
wow... still struggling... I did too when I first got started on a handheld device SQL-CE. My current project is running with C#.Net 3.5 but I think the principles you are running into are the same. Here is what is working for my system in it's close parallels to yours.
First, the connection string to the handheld. It is just
string myConnString = #"Data Source=\MyFolder\MyData.sdf";
no reference to the sql driver
Next, the TableExists
SqlCeCommand oCmd = new SqlCeCommand( "select * from INFORMATION_SCHEME.TABLES "
+ " where TABLE_NAME = #pTableName" );
oCmd.Parameters.Add( new SqlCeParameter( "pTableName", YourTableParameterToFunction ));
The "#pTableName" is to differentiate between the "TABLE_NAME" column and to absolutely prevent any issues about ambiguity. The Parameter does NOT get the extra "#". In SQL, the # indicates to look for a variable... The SqlCeParameter of "pTableName" must match as it is in the SQL Command (but without the leading "#").
Instead of issuing a call to ExecuteScalar, I am actually pulling the data down into a DataTable via
DataTable oTmpTbl = new DataTable();
SqlCeDataAdapter da = new SqlCeDataAdapter( oCmd );
da.Fill( oTmpTbl );
bool tblExists = oTbl.Rows.Count > 0;
This way, I either get records back or I dont... if I do, the number of records should be > 0. Since I'm not doing a "LIKE", it should only return the one in question.
When you get into your insert, updates and deletes, I have always tried to prefix my parameters with something like "#pWhateverColumn" and make sure the SqlCeParameter is by the same name but without the "#". I haven't had any issues and this project has been running for years. Yes it's a .net 3.5 app, but the fundamental basics of connecting and querying SHOULD be the same.
If it IS all within your application, I would try something like creating a single global static "Connection" object. Then, a single static method to handle it. Then, instead of doing a NEW connection during every "using" attempt, change it to something like...
public static class ConnectionHandler
{
static SqlCeConnection myGlobalConnection;
public static SqlCeConnection GetConnection()
{
if( myGlobalConnection == null )
myGlobalConnection = new SqlCeConnection();
return myGlobalConnection;
}
public static bool SqlConnect()
{
GetConnection(); // just to ensure object is created
if( myGlobalConnection.State != System.Data.ConnectionState.Open)
{
try
{
myGlobalConnection.ConnectionString = #"Data Source=\MyFolder\MyDatabase.sdf";
myGlobalConnection.Open();
}
catch( Exception ex)
{
// optionally messagebox, or preserve the connection error to the user
}
}
if( myGlobalConnection.State != System.Data.ConnectionState.Open )
MessageBox.Show( "notify user");
// return if it IS successful at opening the connection (or was already open)
return myGlobalConnection.State == System.Data.ConnectionState.Open;
}
public static void SqlDisconnect()
{
if (myGlobalConnection!= null)
{
if (myGlobalConnection.State == ConnectionState.Open)
myGlobalConnection.Close();
// In case some "other" state, always try to force CLOSE
// such as Connecting, Broken, Fetching, etc...
try
{ myGlobalConnection.Close(); }
catch
{ // notify user if issue}
}
}
}
... in your other class / function...
if( ConnectionHandler.SqlConnect() )
Using( SqlCeConnection conn = ConnectionHandler.GetConnection )
{
// do your stuff
}
... finally, when your app is finished, or any other time you need to...
ConnectionHandler.SqlDisconnect();
This keeps things centralized, and you don't have to worry about open/close, what the connection string is buried all over the place, etc... If you can't connect, you can't run a query, don't try to run the query if it can't even get that far.
I think it may be a permission issue on INFORMATION_SCHEMA system views. Try the following.
GRANT VIEW DEFINITION TO your_user;
See here for more details

Getting a returned value from a stored procedure in C# method

I'm using visual studios 2010 to create a c# web application with a database. My goal is to have default.aspx call a c# class which runs a stored procedure that selects an entry from the table and returns it. Here's the code:
'The stored procedure. I want it to send back the name it gets from doing
'the query to the c# class.
ALTER PROCEDURE getName (#id int)
AS
BEGIN
SET NOCOUNT ON;
--
SELECT name FROM tableA where id = #id;
END
Return
//Here's the c# class I'm using.
public class student
{
public string name;
public int id;
public student()
{ }
public String doQuery(int id)
{
SqlConnection conn = null;
try
{
conn = new SqlConnection("Server =(local); Database = Database1.mdf;
Integrated Security = SSPI");
conn.Open();
SqlCommand cmd = new SqlCommand("getName", conn);
cmd.CommandType = CommandType.StoredProcedure;
SqlParameter param = new SqlParameter("#id", SqlDbType.Int);
param.Direction = ParameterDirection.Input;
param.Value = id;
cmd.Parameters.Add(param);
//This is some code from when I tryed return value
//SqlParameter reVal = cmd.Parameters.Add("#name", SqlDbType.VarChar);
//reVal.Direction = ParameterDirection.ReturnValue;
//before using ExecuteScalar I tried ExcuteNonQuery with the commented
//out code
name = (string)cmd.ExecuteScalar();
//name = (String)cmd.Parameters["#name"].Value;
conn.Close();
}
catch(Exception)
{}
return name;
}
}
Running my program does not return errors it simply doesn't place any value in name. What am I missing to get the name that is selected in the sql procedure into the name variable in my c# class. I hope I'm conveying my problem clearly.
edit1:I didn't put anything in the catch cause hadn't decided what to use to see that it had errored out. I changed it to make name = "error" when it fails the try and that's what I get and that's what I get.
I also tried running "exec getName 5, otherstuff"in sql server management. I'm a little unclear about what to use as the second parameter when running exec getName since the second parameter is suppose to be just output but still seems to be required to run it. It just says the commands are executed successfully but doesn't display the name that goes with id 5
I would recommend using the async/await pattern for SQL statements. Fortunately, it doesn't require much refactoring.
See if this works for you:
public async Task<string> QueryGetNameAsync(int id)
{
using (var dbConn = new SqlConnection("..."))
using (var command = new SqlCommand("getName", dbConn))
{
try
{
command.CommandType = CommandType.StoredProcedure;
command.Parameters.AddWithValue("#id", id);
await dbConn.OpenAsync();
var result = await command.ExecuteScalarAsync();
dbConn.Close();
var name = result as string;
return name;
}
catch (Exception ex)
{
// Handle exception here.
}
}
}
You'd call it with something like:
private async void DoLookup_Clicked(object sender, EventArgs e)
{
var id = int.Parse(idText.Text);
var name = await QueryGetNameAsync(id);
}
Alternatively, can use OUTPUT parameters in SQL but you would have to adjust your stored procedure to something like this:
ALTER PROCEDURE getName
(
#id int,
#name varchar(100) OUTPUT
)
AS
BEGIN
SET NOCOUNT ON;
SELECT #name = name FROM tableA where id = #id;
END
Then your C# function would be something like:
public async Task<string> QueryGetNameAsync(int id)
{
using (var dbConn = new SqlConnection("..."))
using (var command = new SqlCommand("getName", dbConn))
{
try
{
command.CommandType = CommandType.StoredProcedure;
command.Parameters.AddWithValue("#id", id);
command.Parameters.Add("#name", SqlDbType.VarChar, 100);
command.Parameters["#name"].Direction = ParameterDirection.Output;
await dbConn.OpenAsync();
await command.ExecuteNonQueryAsync();
dbConn.Close();
var name = command.Parameters["#name"].Value as string;
return name;
}
catch (Exception ex)
{
// Handle exception here.
}
}
}
The problem is in your connection string: unless you have strange naming conventions, you are specifying the database file name and not the name of the database itself.
Try changing this part of the connection string: Database = Database1.mdf; to Database = Database1;.
If you are confused about what is or is not valid in the connection string, you can always use the SqlConnectionStringBuilder which will create the appropriate connection string for you after you have set the correct properties.
You can also use the list of properties specified in the SqlConnection.ConnectionString documentation as a reference that contains examples.
Finally, I strongly recommend the following best practices:
1) Use using blocks with the connection and commands to ensure they are properly closed and disposed.
2) Do not assign name directly to the result of ExecuteScalar in case it is return as DBNull.Value
3) Never ignore exceptions unless you have documented why you are doing so in the code.
Here is a quick rewrite with all of the above recommendations:
try
{
using (var conn = new SqlConnection("Server =(local); Database = Database1; Integrated Security = SSPI"))
{
conn.Open();
using (var cmd = new SqlCommand("getName", conn))
{
cmd.CommandType = CommandType.StoredProcedure;
var param = new SqlParameter("#id", SqlDbType.Int);
param.Direction = ParameterDirection.Input;
param.Value = id;
cmd.Parameters.Add(param);
var oResult = cmd.ExecuteScalar();
if ((oResult != null) && (oResult != DBNull.Value))
{
name = (string)oResult;
}
}
conn.Close();
}
}
catch (Exception)
{
// Do something with the exception here, don't just ignore it
}

how to check if stored procedure exists or not in sql server using c# code

I tried below code for cheking SP is alredy exist or not. if not exist i am creating..
But every time it is showing sp is not created.....But my database already have this sp.
Let me know where i am doing mistake.
string checkSP = String.Format(
"IF OBJECT_ID('{0}', 'U') IS NOT NULL SELECT 'true' ELSE SELECT 'false'",
"GP_SOP_AdjustTax");
SqlCommand command = new SqlCommand(checkSP, myConnection);
command.CommandType = CommandType.Text;
if (myConnection == null || myConnection.State == ConnectionState.Closed)
{
try
{
myConnection.Open();
}
catch (Exception a)
{
MessageBox.Show("Error " + a.Message);
}
}
bool Exist = false;
Exist = Convert.ToBoolean(command.ExecuteScalar());
if (Exist == false) //false : SP does not exist
{
// here i am writing code for creating SP
}
Try:
if exists(select * from sys.objects where type = 'p' and name = '<procedure name>' )
Also you can check that with c#:
string connString = "";
string query = "select * from sysobjects where type='P' and name='MyStoredProcedureName'";
bool spExists = false;
using (SqlConnection conn = new SqlConnection(connString))
{
conn.Open();
using (SqlCommand command = new SqlCommand(query, conn))
{
using (SqlDataReader reader = command.ExecuteReader())
{
while (reader.Read())
{
spExists = true;
break;
}
}
}
}
For those who use Entity Framework and a DbContext:
create an extension class for DbContext:
internal static class DbContextExtensions
{
public static bool StoredProcedureExists(this DbContext context,
string procedureName)
{
string query = String.Format(
#"select top 1 from sys.procedures " +
"where [type_desc] = '{0}'", procedureName);
return dbContext.Database.SqlQuery<string>(query).Any();
}
}
As robIII remarked, this code should not be published to the outside world as it makes the database vulnerable for hackers (thank you RobIII!). To prevent this use a parameterized statement. The problem with the above mentioned method is described here
The solution is to put procedureName as a parameter in an SQL statement. SQL will check if the string parameter has the desired format, thus inhibiting malicious calls:
public static bool ImprovedExists(this DbContext dbContext, string procedureName)
{
object[] functionParameters = new object[]
{
new SqlParameter(#"procedurename", procedureName),
};
const string query = #"select [name] from sys.procedures where name= #procedurename";
return dbContext.Database.SqlQuery<string>(query, functionParameters).Any();
}
I found this on MSDN
select * from sys.objects where type_desc = 'SQL_STORED_PROCEDURE' AND name = 'Sql_PersonInsert'
Try:
SELECT *
FROM sys.objects
WHERE object_id = OBJECT_ID(N'GP_SOP_AdjustTax') AND type in (N'P', N'PC')
My stab at it:
Reusable extension method
Minimal Sql / Minimal C#
Called from .Net as the OP implicitly requested
Could be faster because of the object_id function
public static bool StoredProcedureExists(this string source)
{
using (var conn = new SqlConnection(ConfigurationManager.ConnectionStrings["ConnectionString"].ConnectionString))
{
conn.Open();
using (var cmd = new SqlCommand($"select object_id('{source}')", conn))
return !cmd.ExecuteScalar().ToString().IsNullOrWhiteSpace();
}
}
You can check with following tsql query (suitable for SQL Server):
select * from sysobjects where ytype='P' and name='MyStoredProcedureName'
If query returns row then stored procedure named 'MyStoredProcedureName' exists.
And here is how you can use it in code:
//TODO: set connection string
string connString = "";
string query = "select * from sysobjects where ytype='P' and name='MyStoredProcedureName'";
bool spExists = false;
using (SqlConnection conn = new SqlConnection(connString))
{
conn.Open();
using (SqlCommand command = new SqlCommand(query,conn))
{
using (SqlDataReader reader = command.ExecuteReader())
{
while (reader.Read())
{
spExists = true;
break;
}
}
}
}
string checkSP = String.Format(
"IF OBJECT_ID('{0}', 'U') IS NOT NULL SELECT 'true' ELSE SELECT 'false'",
"GP_SOP_AdjustTax");
is fine if you change the 'U' to 'P'.
With 'U' you query for user-tables, where 'P' gives you stored-procedures.
private static bool StoredProcedureExists(string sp)
{
var connString = #"<your string here>";
var query = string.Format("SELECT COUNT(0) FROM INFORMATION_SCHEMA.ROUTINES WHERE ROUTINE_NAME = '{0}'", sp);
using (var conn = new SqlConnection(connString))
{
conn.Open();
using (var cmd = new SqlCommand(query, conn))
{
return Convert.ToInt32(cmd.ExecuteScalar()) > 0;
}
}
}
Handles procedure names with different schemas
Names with and without brackets ([])
Uses parameter to avoid SQL injection
Note: Caller owns SQL Connection
public static class SqlConnectionExtensions
{
public static Task<bool> StoredProcedureExistsAsync(this SqlConnection sqlConnection, string storedProcedureName)
{
string query = "SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(#storedProcedureName) AND type in (N'P', N'PC')";
using (SqlCommand command = new SqlCommand(query, sqlConnection))
{
command.Parameters.AddWithValue("#storedProcedureName", storedProcedureName);
using (SqlDataReader reader = command.ExecuteReader())
{
return reader.ReadAsync();
}
}
}
}
The following works with MySQL, SQL Server, and Oracle I think:
SELECT * FROM INFORMATION_SCHEMA.ROUTINES
WHERE ROUTINE_TYPE='PROCEDURE'
AND (ROUTINE_SCHEMA='questionnaire' OR ROUTINE_CATALOG = 'questionnaire')
AND SPECIFIC_NAME='create_question';
Usage:
string procedureName = "create_question";
using (DbConnection conn = new SqlConnection("Server=localhost;Database=questionnaire;Trusted_Connection=True;")) // Connection is interchangeable
{
conn.Open();
using (var cmd = conn.CreateCommand())
{
cmd.CommandText = $"SELECT * FROM INFORMATION_SCHEMA.ROUTINES WHERE ROUTINE_TYPE='PROCEDURE' AND (ROUTINE_SCHEMA='{conn.Database}' OR ROUTINE_CATALOG = '{conn.Database}') AND SPECIFIC_NAME='{procedureName}';";
return cmd.ExecuteScalar() != null;
}
}
If you use Microsoft.SqlServer.Management.Smo, try
private static bool CheckIfStoredProcedureExists(Database db, string spName, string schema)
{
db.StoredProcedures.Refresh();
return (db.StoredProcedures[spName, schema] != null);
}
Try this;
if object_id('YourStoredProcedureName') is null
exec ('create procedure dbo.YourSp as select 1')
go
alter procedure dbo.YourStoredProcedure
as

Categories

Resources