Converting C# DateTime to T-SQL Time in SelectCommand - c#

I'm trying to return a query (in a gridview in ASP.NET) WHERE Time >= DateTime.Now.Add(-60). The WHERE clause has been giving me no end of difficulties.
DateTime pastTime = DateTime.Now.Add(-60);
ds_DB.SelectCommand = "SELECT * FROM [vPurchaseTotals] WHERE [TimeOfTransaction] >= " + pastTime;
My issue is getting pastTime to convert properly, so it only returns the newer data. [TimeOfTransaction] is a time(7) data type in the table.
How do I parse C#'s DateTime to SQL Server's Time?

Here, try this:
using(SqlConnection conn = new SqlConnection(yourConnectionString))
{
DateTime pastTime = DateTime.Now.Add(-60);
ds_DB.SelectCommand = #"SELECT * FROM [vPurchaseTotals]
WHERE [TimeOfTransaction] >= #PastTime";
SqlCommand cm = conn.CreateCommand();
cm.CommandText = ds_DB.SelectCommand;
cm.Parameters.Add("#PastTime", SqlDbType.Time).Value = pastTime.TimeOfDay; //For comparison with TSQL TIME type
try
{
conn.Open();
// Do what you need to do here.
}
catch(SqlException e)
{
// Handle Exception
}
finally
{
conn.Close();
}
}
Just for future reference, you should always parameterize your queries. It ends up being a lot safer and cleaner/easier to read and adjust.
EDIT: Are you using a SqlDataAdapter class? Is that what ds_DB is an instance of? I would personally just use a string value for your query and then implement the SqlDataAdapter like this:
try
{
conn.Open();
using(SqlDataAdapter da = new SqlDataAdapter(cm))
{
da.Fill(DataTable dt);
}
}

Related

Comparing SQL Server DATETIME with DateTime.NOW in C# / ASP.NET

How do I compare a SQL Server DATETIME with the DateTime.Now value? As you can see I assigned it to a Session and tried comparing it with DateTime.Now.
string timestamp = #"SELECT sr.*, ud.* FROM SuspensionRecord sr, UserData ud WHERE sr.User_ID=#User_ID AND ud.User_ID=#User_ID";
using (SqlCommand cmd2 = new SqlCommand(timestamp, con))
{
cmd2.Parameters.AddWithValue("#User_ID", Session["UserID"].ToString());
using (SqlDataReader dr = cmd2.ExecuteReader())
{
if (dr.HasRows)
{
while (dr.Read())
{
Session["suspensiondate"] = dr["End_Date_Suspension"].ToString();
}
if (Convert.ToDateTime(Session["supensiondate"]) >= DateTime.Now.Date)
{
lblMessage.Text = "The account's status is suspended.";
lblMessage.Visible = true;
}
}
}
}
You should pass in the date and do the comparison in the query instead of in c#. That is one less step. If you do want to do it in c# then use the appropriate types, do not convert the DateTime to a string and then convert it back again.
There is no need for the join (2nd table) in your query
You do not have to use a DataReader for this, you can use ExecuteScalar which returns 1 value instead.
Use Add so you can specify the correct schema types with SqlDbType and not AddWithValue
string timestamp = #"SELECT 1 FROM SuspensionRecord sr WHERE sr.User_ID = #User_ID AND supensiondate > #now";
using (SqlCommand cmd2 = new SqlCommand(timestamp, con))
{
cmd2.Parameters.Add("#User_ID", SqlDbType.Int).Value = Session["UserID"]; // do not convert to string
cmd2.Parameters.Add("#now", SqlDbType.DateTime).Value = DateTime.Now.Date;
var result = cmd2.ExecuteScalar();
if(result != null) // if null then there were no records so account is not suspended
{
lblMessage.Text = "The account's status is suspended.";
lblMessage.Visible = true;
}
}
First, your SQL is terrible.
You are returning way too much data, and you are using an implicit join (when explicit joins are a part of ANSI-SQL for almost 30 years now!)
Second, Can we stop using AddWithValue() already?
Instead of all this code you can do the entire test on SQL and return a single value:
string sql =
#"SELECT CASE WHEN EXISTS
(
SELECT 1
FROM SuspensionRecord
WHERE User_ID = #User_ID
AND End_Date_Suspension >= CAST(GETDATE() AS DATE)
) THEN 1 ELSE 0 END";
Then you can use ExecuteScalar instead of ExecuteReader, and you don't need to loop through all the irrelevant data:
using (SqlCommand cmd2 = new SqlCommand(timestamp, con))
{
cmd2.Parameters.Add("#User_ID", SqlDbType.Int).Value = Session["UserID"];
if ((int)cmd2.ExecuteScalar() == 1)
{
lblMessage.Text = "The account's status is suspended.";
lblMessage.Visible = true;
}
}

Sql DataReader Has Rows but Returns With Empty data

I am using a query to select data from sql database. It has rows but data raader.Read() function returns false and rows are empty as I have checked in debugging
Code that i have been using is
public void getSale()
{
DB db = new DB();
try
{
db.cmd.CommandText = "select * from Sale where date is not null and (date between '"+StartDate+"' and '"+EndDate+"') order by date";
db.cmd.Connection = db.con;
db.con.Open();
if(db.con.State == System.Data.ConnectionState.Open)
{
db.dataReader = db.cmd.ExecuteReader();
if(db.dataReader.HasRows)
{
while(db.dataReader.Read())
{
SaleModel sm = new SaleModel();
sm.SaleId = long.Parse(db.dataReader["Id"].ToString());
sm.UserName = db.dataReader["UserName"].ToString();
sm.ItemsQuantity = int.Parse(db.dataReader["ItemsQuantity"].ToString());
sm.TotalAmount = double.Parse(db.dataReader["TotalAmount"].ToString());
sm.SubTotal = double.Parse(db.dataReader["SubTotal"].ToString());
sm.Discount = double.Parse(db.dataReader["Discount"].ToString());
sm.Completed = bool.Parse(db.dataReader["Completed"].ToString());
sm.Date = DateTime.Parse(db.dataReader["Date"].ToString());
sm.CustomerPhone = long.Parse(db.dataReader["CustomerPhone"].ToString());
SalesList.Add(sm);
}
db.con.Close();
}
}
}
catch(Exception ex)
{
MessageBox.Show(ex.Message, "Exception", MessageBoxButton.OK, MessageBoxImage.Error, MessageBoxResult.OK);
}
}
And When I tested this query on Query editor in Visual studio rows were returned
If Anyone can help?
Why you concatenate strings to build your sql query? NEVER do that. It is a source for sql-injection and can cause issues like this. Instead use parameterized queries.
Also don't use SqlConnection wrappers like your DB class. That can cause several other issues. Instead create, open, close and dispose them where you need them, best by using the using-statament. The connection-pooling will manage the rest for you.
public List<SaleModel> GetSale(DateTime startDate, DateTime endDate)
{
string sql = #"select * from Sale
where date is not null
and date between #StartDate and #EndDate
order by date";
var salesList = new List<SaleModel>();
try
{
using (var con = new SqlConnection("insert your connection string"))
using (var cmd = new SqlCommand(sql, con))
{
cmd.Parameters.Add("#StartDate", SqlDbType.DateTime).Value = startDate;
cmd.Parameters.Add("#EndDate", SqlDbType.DateTime).Value = endDate;
con.Open();
using (var reader = cmd.ExecuteReader())
{
while (reader.Read())
{
SaleModel sm = new SaleModel();
sm.SaleId = long.Parse(reader["Id"].ToString());
sm.UserName = reader["UserName"].ToString();
sm.ItemsQuantity = int.Parse(reader["ItemsQuantity"].ToString());
sm.TotalAmount = double.Parse(reader["TotalAmount"].ToString());
sm.SubTotal = double.Parse(reader["SubTotal"].ToString());
sm.Discount = double.Parse(reader["Discount"].ToString());
sm.Completed = bool.Parse(reader["Completed"].ToString());
sm.Date = DateTime.Parse(reader["Date"].ToString());
sm.CustomerPhone = long.Parse(reader["CustomerPhone"].ToString());
salesList.Add(sm);
}
}
}
}
catch (Exception ex)
{
MessageBox.Show(ex.Message, "Exception", MessageBoxButton.OK, MessageBoxImage.Error, MessageBoxResult.OK);
}
return salesList;
}
I'm pretty sure that this works(f.e. can be a localization issue).
Side-note: a method GetSale should return a List<SaleModel> but not fill one. You should also pass the parameters as DateTime to the method. I've changed it in my code sample.
This is a much better way to structure you code, and there's a good chance it will fix your issue, too:
//accept the date values as parameter arguments, return the result.
// Do NOT mess about with variables at the global or class scope.
public IEnumerable<SalesModel> getSale(DateTime StartDate, DateTime EndDate)
{
string sql = "select * from Sale where date is not null and (date between #StartDate and #EndDate) order by date";
//DON'T abstract SqlCommand/SqlConnection. DO abstract your connection string.
//Also, don't bother with the try/catch at this level. You can't really do anything with it here, so worry about the exception in calling code.
using (var cn = new SqlConnection(DB.ConnectionString))
using (var cmd = new SqlCommand(sql, cn))
{
cmd.Parameters.Add("#StartDate", SqlDbType.DateTime).Value = StartDate
cmd.Parameters.Add("#EndDate", SqlDbType.DateTime).Value = EndDate
cn.Open();
using (SqlDataReader rdr = cmd.ExecuteReader())
{
while(rdr.Read())
{
var sm = new SaleModel();
//If you have good schema design, these values are **already** in the correct type.
// The old code forces an expensive conversion to string, following by an expensive parse back to the type it already had.
// We can do MUCH better.
sm.SaleId = (long)rdr["Id"];
//but it is okay for types that are *already* strings
sm.UserName = rdr["UserName"].ToString();
sm.ItemsQuantity = (int)rdr["ItemsQuantity"];
sm.TotalAmount = (double)rdr["TotalAmount"]);
sm.SubTotal = (double)rdr["SubTotal"];
sm.Discount = (double)rdr["Discount"];
sm.Completed = (bool)rdr["Completed"];
sm.Date = (DateTime)rdr["Date"];
sm.CustomerPhone = (long).rdr["CustomerPhone"];
yield return sm;
}
}
}
}
Here it is again without all the extra comments. The point here is this is still less code than the original that used string concatenation, and it took less than 10 minutes to write. Good code doesn't necessarily take longer.
public IEnumerable<SalesModel> getSale(DateTime StartDate, DateTime EndDate)
{
string sql = "select * from Sale where date is not null and (date between #StartDate and #EndDate) order by date";
using (var cn = new SqlConnection(DB.ConnectionString))
using (var cmd = new SqlCommand(sql, cn))
{
cmd.Parameters.Add("#StartDate", SqlDbType.DateTime).Value = StartDate
cmd.Parameters.Add("#EndDate", SqlDbType.DateTime).Value = EndDate
cn.Open();
using (SqlDataReader rdr = cmd.ExecuteReader())
{
while(rdr.Read())
{
var sm = new SaleModel();
sm.SaleId = (long)rdr["Id"];
sm.UserName = rdr["UserName"].ToString();
sm.ItemsQuantity = (int)rdr["ItemsQuantity"];
sm.TotalAmount = (double)rdr["TotalAmount"]);
sm.SubTotal = (double)rdr["SubTotal"];
sm.Discount = (double)rdr["Discount"];
sm.Completed = (bool)rdr["Completed"];
sm.Date = (DateTime)rdr["Date"];
sm.CustomerPhone = (long).rdr["CustomerPhone"];
yield return sm;
}
}
}
}
Note that I return an IEnumerable rather than a List. If you really need a List (tip: you probably don't, and sticking with IEnumerable is faster), you can just call ToList() on the result.
don't see any issue except that you are using date which is a reserve word and not your actual column name. Change your query to be
db.cmd.CommandText = "select * from Sale where [date] is not null and ([date] between '"+StartDate+"' and '"+EndDate+"') order by [date]";

How to get values from table valued function in C#

I have a program where i have to display
The Event Description (OpisDogodka)
Location (Lokacija)
Time (ura)
My table valued function:
[dbo].[DobiDogodek](
#Ime nvarchar(100), #Datum date)
RETURNS TABLE AS RETURN (SELECT OpisDogodka AS 'Opisdogodka',Lokacija, Ura FROM Dogodek WHERE Ime=#Ime AND Datum=#Datum)
My method to connect to the server:
public string Dobi_dogodek(string ime,string datum)
{
string a="";
cmd = new SqlCommand("SELECT * FROM dbo.DobiDogodek(#Ime,#Datum)",povezava); //povezava = connectio and it succeeds to connect to the server.
cmd.CommandType = CommandType.Text;
cmd.Parameters.AddWithValue("#Ime", ime);
cmd.Parameters.AddWithValue("#Datum", datum); //how to pass date only?
try
{
SqlDataReader Reader = cmd.ExecuteReader();
while(Reader.Read())
{
a = Reader.GetString(0)+" "+Reader.GetString(1)+" "+Reader.GetString(3).ToString(); // get what?
}
Uspeh = true;
}
catch (Exception e)
{
ex = e;
}
finally
{
povezava.Close();
}
return a;
}
I tried also using Datatable and datarow. I am also unsure how to work with Date. I know how to work with DateTime, but I need Date and Time separate. What I am doing wrong?
4.6.2017 (11.40 am CET)Update:
It seems I get the desired result
public List<string> Dobi_dogodek(string ime,string datum)
{
s = new List<string>();
cmd = new SqlCommand("SELECT * FROM dbo.DobiDogodek(#Ime,#Datum)",povezava);
cmd.CommandType = CommandType.Text;
cmd.Parameters.AddWithValue("#Ime", ime);
cmd.Parameters.AddWithValue("#Datum", Convert.ToDateTime(datum));
dt = new DataTable();
da = new SqlDataAdapter(cmd);
da.Fill(dt);
try
{
foreach (DataRow dr in dt.Rows)
{
s.Add(dr["Opis dogodka"].ToString() + "\\" + dr["Lokacija"].ToString() + "\\" + dr["Ura"].ToString());
}
Uspeh = true;
}
catch (Exception e)
{
ex = e;
}
finally
{
povezava.Close();
}
return s;
}
Now I just need to split the strings according to my requirements, but is the a better (not necessarily an easy) way?
Try this:
cmd.Parameters.AddWithValue("#Datum", Convert.ToDateTime(datum));
See also https://msdn.microsoft.com/en-us/library/cc716729(v=vs.110).aspx .
what is happening when you run it? are you getting an error message? is it getting it as an int? did you see what the sql server is getting from application by using sql profiler?
I will double check but I think your problem is you are not putting quotes around your variables in our statement so when it runs it is evaluating them as ints. try "SELECT * FROM dbo.DobiDogodek('#Ime','#Datum')". It been a long time since I havnt used something like EF...

Use wildcards in ado.net sybase parameter

Does some one know, how to use wildcards with ado.net parameter in sybase sql anywhere?
For example i want to search for all names, starting with Se. In a normal query i would use select * from names where name like 'Se%'. But in ADO.Net my query looks like SELECT * from names where name like ? and the question mark will be set over SAParameter.
SACommand command = new SACommand(SqlStatement, cConnection);
command.Parameters.Add(new SAParameter() { Value = "Se%" });
The problem is, Value could not contains any wildcards.
Thank you very much!
here's my solution example
using (SAConnection con = new SAConnection(DBConnStr))
{
con.Open();
try
{
string sql = "select * from names where name like ?";
SACommand cmd = new SACommand(sql, con);
cmd.Parameters.Add("#p1","Se%");
SADataReader rdr = cmd.ExecuteReader();
while (rdr.Read())
{
Console.WriteLine(rdr["name"].ToString());
}
rdr.Close();
}
finally
{
con.Close();
}
}
Console.ReadLine();

Query & generic

I'm developing a C# solution with data access to Oracle.
And would like to have a generic solution about query.
Here is a part of my code :
public DataTable GetData(string query)
{
DbProviderFactory factory = DbProviderFactories.GetFactory("System.Data.OracleClient");
using (DbConnection conn = factory.CreateConnection())
{
try
{
DbConnectionStringBuilder csb = factory.CreateConnectionStringBuilder();
csb["Data Source"] = #"Northwind";
csb["User Id"] = #"Northwind";
csb["Password"] = #"Northwind";
conn.ConnectionString = csb.ConnectionString;
conn.Open();
using (DbCommand cmd = conn.CreateCommand())
{
cmd.CommandText = query;
using (DataTable dt = new DataTable())
{
DbDataAdapter da = factory.CreateDataAdapter();
cmd.CommandType = CommandType.Text;
da.SelectCommand = cmd;
da.Fill(dt);
return dt;
}
}
}
catch (Exception ex)
{
throw new Exception("Error", ex);
}
finally
{
if (conn.State != ConnectionState.Closed)
conn.Close();
}
}
}
And I call my method like this :
DataAccess.Provider data = new DataAccess.Provider();
DataTabel dt = dt.GetData("select * from myTable);
This works pretty good but this is not my aim.
I have a second class called CL_mpg with all my SQL queries.
class CL_MPG
{
public string rq_sql;
public string selectParam(string param)
{
this.rq_sql = "select * from myTable where id = '" + param + "';";
return this.rq_sql;
}
public string select()
{
this.rq_sql = "select * from myTable";
return this.rq_sql;
}
//...
}
And I would like to use my methods selectParam and/or select to fill my datatable, but I don't know how to do that.
Although others complain at your learning attempt, everyone has to start somewhere. Your method is actually an ok start, but I would change the parameter from a string to a DbCommand object. Then, you can create your methods to properly build the command and set proper parameters. Then pass the entire prepared command to your wrapper method (that creates connection, tests open successful, queries data, etc) and have your method return a DataTable object as you have... something like
public class CL_MPG
{
private DataTable GetData(DbCommand cmd )
{
// do all the same as you have with exception of your USING DBCOMMAND.
// just set the connection property of the incoming command to that of
// your connection created
// AT THIS PART --
// using (DbCommand cmd = conn.CreateCommand())
// {
// cmd.CommandText = query;
// just change to below and remove the closing curly bracket for using dbcommand
cmd.Connection = conn;
}
// Now, your generic methods that you want to expose for querying
// something like
public DataTable GetAllData()
{
DbCommand cmd = new DbCommand( "select * from YourTable" );
return GetData( cmd );
}
public DataTable GetUser( int someIDParameter )
{
DbCommand cmd = new DbCommand( "select * from YourTable where ID = #parmID" );
cmd.Parameters.Add( "#parmID", someIDParameter );
return GetData( cmd );
}
public DataTable FindByLastName( string someIDParameter )
{
DbCommand cmd = new DbCommand( "select * from YourTable where LastName like #parmTest" );
cmd.Parameters.Add( "#parmTest", someIDParameter );
return GetData( cmd );
}
}
Notice the command is being built and fully prepared and parameterized vs concatination of strings as prior comment was made which could expose you to SQL-injection. As for the parameters, and not querying Oracle, they may need to be tweaked some. Different engines use slightly different conventions. If connecting to SQL-Server database, it uses "#" to identify a parameter. In SyBase Advantage Database, it uses ":". Using Visual FoxPro, a simple "?" placeholder is used.
Also, if your query has many criteria, just keep adding additional "#parm" type placeholders, then add your parameters in the same order as they appear in your query just to make sure you didn't miss any. Some functions could have none, one or more based on your needs. Then, in the samples provided, its as simple as doing something like
DataTable whoIs = yourCL_MPGObject.GetUser( 23 );
if( whoIs.Rows.Count > 0 )
MessageBox.Show( whoIs.Rows[0]["WhateverColumnName"] );

Categories