I need some guidance on the following if possible please
Explanation
I have a main project.cs file in the App_Code which contains main functions. One of these functions is a SQL_Inject which inserts data into the database.
I then have multiple pages that utilize this function from multiple client machines at the same time.
Question
The answer i am after is, is this a safe method of choice? Or should i be creating a new connection separately on each .cs page.
Reason/Problem
Reason this is becoming a concern, we are currently a small company but growing. It has happened that a page crashes due to the SQL Connection is still open. I am worried its due to two connections trying to be made at the same time. I am not sure if this is the issue or if it comes from something else.
//GLOBAL DECLARATIONS
//DB CONNECTIONS - retrieve from config file
public static string ConProjectms = System.Configuration.ConfigurationManager.ConnectionStrings["conProject"].ConnectionString;
//DB CONNECT TO SQL
public static SqlConnection SqlConn = new SqlConnection();
public static SqlCommand SqlCmd = new SqlCommand();
public static SqlDataReader SqLdr;
public static string SqlStr;
public static string ConnString;
public static void SqlInject(string query, string dataBase)
{
SqlConn.ConnectionString = ConProjectms;
//Set the Connection String
SqlConn.Open();
//Open the connection
SqlCmd.Connection = SqlConn;
//Sets the Connection to use with the SQL Command
SqlCmd.CommandText = query;
//Sets the SQL String
SqlCmd.ExecuteNonQuery();
//put Data
SqlClose();
}
public static void SqlClose()
{
if (SqlConn.State != ConnectionState.Open) return;
SqlConn.Close();
SqlCmd.Parameters.Clear();
}
SQL can handle multiple connections at the same time. However, you're code is very likely to be be run by two clients at the same time, and they'll be using the same connection not two separate connections. That's bad thing #1.
SQL Server does a fantastic job of connection pooling - and I assume other DBs have similar capabilities. In such a world, you shouldn't try to keep and reuse any of your data-related objects around - but create them as you need them and when SQL sees that you're using a connection it's created before and since freed up, it'll use that. You don't have to do anything weird to get this functionality.
With that in mind, your static objects should mostly go away, and your SQLInject method might look something like this:
public static void SqlInject(string query, string dataBase)
{
var connectionString =
System
.Configuration
.ConfigurationManager
.ConnectionStrings["conProject"]
.ConnectionString;
using ( var connection = new SqlConnection( connectionString ) )
{
connection.Open( );
using ( var command = connection.CreateCommand( ) )
{
command.CommandText = query;
command.CommandType = CommandType.Text;
command.ExecuteNonQuery( );
}
}
}
Notice that you don't have to worry about closing the connection per se; the using blocks handle the disposition of your open, active objects. This is largely how folks are doing direct SQL from c#. By the way, neither your code nor mine uses the dataBase argument. Maybe you're supposed to edit the base connection string with it??
But wait - there's more!
Having said all that, and since you raised a concern about security, you should know that this isn't safe code at all - yours or mine. SqlInject is probably a good name, because it allows pretty much anything in the query argument (which, BTW, if you're doing ExecuteNonQuery, then maybe query isn't a good name).
You're far far better allowing arguments to a library of known statements (maybe stored procedures), validating those arguments, and using SQL Injection attack mitigation to parameterize your known statements (look up that phrase and you'll find an abundance of examples and advice).
Just for yuks, here's a scaffold of what you might consider:
public static void SqlInject(string commandName, params[] object commandArgs )
{
//--> no point in going on if we got no command...
if ( string.IsNullOrEmpty( commandName ) )
throw new ArgumentNullException( nameof( commandName ) );
var connectionString =
System
.Configuration
.ConfigurationManager
.ConnectionStrings["conProject"]
.ConnectionString;
using ( var connection = new SqlConnection( connectionString ) )
{
connection.Open( );
using ( var command = connection.CreateCommand( ) )
{
command.CommandType = CommandType.Text;
command.CommandText = "select commandText from dbo.StatementRepository where commandName = #commandName";
command.Parameters.AddWithValue( "#commandName", commandName );
var results = command.ExecuteScalar( );
if ( results != null && results != DbNull.Value )
{
//--> calling a separate method to validate args, that returns
//--> an IDictionary<string,object> of parameter names
//--> and possibly modified arguments.
//--> Let this validation method throw exceptions.
var validatedArgs = ValidateArgs( commandName, commandArgs );
command.Parameters.Clear( );
command.CommandText = query;
foreach( var kvp in validatedArgs )
{
command.Parameters.AddWithValue( kvp.Key, kvp.Value );
}
command.ExecuteNonQuery( );
}
else
{
throw new InvalidOperationException( "Invalid command" );
}
}
}
}
I didn't attempt to write an actual argument validating method, because that's all wrapped up in your application logic...but I wanted to give you an idea of how you might get to a safer state.
There's no reason why database code inside App_Code shouldn't work. It sounds more like your connection pooling doesn't work very well. Look at the connection string, IIS settings and the performance of your database. If for some reason connection pooling is not possible, then the running time of the query becomes the problem.
Related
I am overthinking this and can't find the right terms to search on in Google.
I have a website that connects to a SQL Server database with mostly centralized classes to call out to the database.
I was able to copy/paste those classes and tweak them to connect to a postgres db by simple changes like changing:
This for connections
SqlConnection connection = new SqlConnection(ConnectionManagerToUse);
PgSqlConnection connection = new PgSqlConnection(ConnectionManagerToUse);
and this for commands:
SqlCommand SelectDataCommand = new SqlCommand();
PgSqlCommand SelectDataCommandPostgres = new PgSqlCommand();
This worked great so far by changing all instances of the above. I want to see if there is a way to have a class or something that is middle man that I can call to change between these 2 in the code so I only have to change code in this middle function to change between Postgres and SQL Server and not everywhere I call out to these commands in my code (obviously specific SQL calls and SQL syntax would be different but I am looking for ways to update the underlying code calling out to the database, not the actual SQL syntax).
Something like the below. I would call this instead of the above (I know this does not work this is just for example) then if I want to change database technology I just change it with a single variable or something in this centralized class/config files (or even have ability to connect to 2 different databases with different technologies at once.
For connections example:
public T GetConnection<T>()
{
if (_DBTechnology == "MSSQL")
{
return new SqlConnection(_connectionStringMSSQL);
}
else if (_DBTechnology == "POSTGRES")
{
return new PgSqlConnection(_connectionStringPostgres));
}
}
I know the above is not valid and I have only used generics a few times so this may not be the way to do this at all. So any suggestions would be helpful.
I was able to come up with solution for this. Below is the basic code to do what I was looking to do. I am working on expanding for more versitiality and usability for multile scenerios but this covers the basics.
This works because the classes/methods for both SQL server and Postgres work similar/the same.
The drivers I used for the Postgres were Devart.Data ande Devart.Data.PostgresSql (found through Nuget)
// set variable for db to use. Classes use MS for sql server, and PG for Postgres
string DBToUse = "MS"; //"PG" for Postgres
// Call to get the empty connection
DbConnection connection = GetConnection(DBToUse)
//Call the class to create empty connection:
DbCommand SelectDataCommandToBuild = GetCommand(DBToUse);
// here are classes to call to get the correct DB connections
public static DbConnection GetConnection(string DBToUse)
{
DbConnection connection = null;
if (DBToUse == "MS")
{
connection = new SqlConnection(_connectionStringMSSQL);
}
else if (DBToUse == "PG")
{
connection = new PgSqlConnection(_connectionStringPostgres);
}
// default just return null
return connection;
}// generic SQL Connection
public static DbCommand GetCommand(string DBToUse)
{
DbCommand command = null;
if (DBToUse == "MS")
{
command = new SqlCommand();
}
else if (DBToUse == "PG")
{
command = new PgSqlCommand();
}
// default just return null
return command;
}// generic SQL Command
I call a function which returns a SqlDataReader object to calling statement. I am confused where should I close the SqlDataReader object and SqlConnection object? In function or after calling it?
This is the function call:
SqlDataReader dr2= RetrieveSearcher();
pid = dr2[0].ToString();
This is the function:
protected SqlDataReader RetrieveSearcher()
{
String Q = "select price from tb3 where pid='12';
cn = new SqlConnection("data source=.\\sqlexpress; integrated security=true; initial catalog=singh");
cn.Open();
cmd = new SqlCommand(Q,cn);
dr1 = cmd.ExecuteReader();
dr1.Read();
return dr1;
}
Always use parameterized queries to avoid sql injection attacks and increase performance (most db servers can reuse execution plans with proper queries)
Never leave a connection open any longer than necessary!
Do not share db connections! Create it, use it, destroy it.
Wrap everything that implements IDisposable in a using block like Connections, Commands, DataReaders, etc. This ensures no resources remain open even in the event of an exception.
Use correct types in your db schema and read those types, do not blanket-convert everything to/from string! Example price seems like it should really be a decimal or numeric value and not a string so do not store it as a string and do not read it back as a string.
Retrieve the connection strings by name from the app.config or web.config (depending on the application type), do not hard code the strings into your connections or anywhere else.
About your logic
Change your method to return a custom type like a piece of data. This ensures proper SoS (Separation of Concerns). Do not return a DataReader! This will abstract the whole database call from the caller which is what you should strive for.
protected SomeType RetrieveSearcherData(string pid)
{
const string Q = "SELECT price FROM tb3 WHERE pid = #pid";
using(var cn=new SqlConnection())
using(var cmd=new SqlCommand(Q,cn))
{
// I do not know what pid is but use tho correct type here as well and specify that type using SqlDbType
cmd.Parameters.Add(new SqlParameter("#pid", SqlDbType.VarChar, 100) { Value = pid});
cn.Open();
using(var dr1= cmd.ExecuteReader())
{
if(dr1.Read())
{
var result = dr1.GetDecimal(0);
// read something and return it either in raw format or in some object (use a custom type)
}
else
return null; // return something else that indicates nothing was found
}
}
}
Do you really want to open a connection each time you call into this function? Having one thread deal with multiple connections is a sure fire way to get deadlocks.
If you still want to do #1, I'd recommend having your RetrieveSearcher return the data it needs in a List<T> or heck, just return a DataTable and deal with that. That way the function can close the connection that it opened.
If you still REALLY want to return a SqlDataReader then you need to make sure that you can close the connection that you opened. SqlDataReader doesn't expose a SqlConnection directly, so you can't directly close the connection after you leave the RetrieveSearcher method. However, you can do this:
dr1 = cmd.ExecuteReader(CommandBehavior.CloseConnection);
That will close the connection when the reader is closed. So, then you can do:
using (SqlDataReader dr2 = RetrieveSearcher()) {
pid=dr2[0].ToString();
}
I'm assuming of course that you REALLY need more than just one string. :) If you REALLY only need one string you just be returning the string and calling cmd.ExecuteScalar();
Is this a good way to process data, i dont like the idea of copying the open close connection all over the place. In essence, is this good form/style?
Data Processing Method:
public int Process(Func<Product, OleDbConnection, int> func, Product data)
{
var oleConnect = new OleDbConnection { ConnectionString = #"stuff" };
oleConnect.Open();
oleConnect.ChangeDatabase("InventoryManager");
var ret = func(data, oleConnect);
oleConnect.Close();
return ret;
}
Typical Method used by the Func:
(Update, Delete, Select are the others to pass)
public int Insert(Product data, OleDbConnection oleConnect)
{
var oleCommand = new OleDbCommand("pInsProduct", oleConnect) { CommandType = CommandType.StoredProcedure };
oleCommand.Parameters.Add(new OleDbParameter("#ProductId", data.ProductID));
oleCommand.Parameters.Add(new OleDbParameter("#ProductName", data.ProductName));
return oleCommand.ExecuteNonQuery();
}
The usage code ends up more or less written as:
Process(Insert, data);
Process(Update, data);
EDIT:
I thought up the following alternative method, which is a better implementation? (using's aside):
(open connection more or less equals the Process method above)
int Insert(Product data)
{
Using ( OleDbConnection oleConnect = OpenConnection() )
{
//do stuff
oleConnect.Close(); // maybe redundant with Using statement?
}
}
So, you should be making sure to wrap your connections in using statements to ensure that the connections get closed and disposed of properly. You should do the same for commands. In the end, it is fine to open and close connections like that as typically you won't pay a penalty due to connection pooling, but you still want to re-use connections as much as you can, so do so whenever possible as long as you make sure you close / clean up when done.
As part of my common utilities I used in all my line of business applications, I have this code...
using System.Web.UI.WebControls;
public class Database
{
/// <summary>
/// Creates a DataView object using the provided query and an SqlDataSource object.
/// </summary>
/// <param name="query">The select command to perform.</param>
/// <returns>A DataView with data results from executing the query.</returns>
public static DataView GetDataView(string query)
{
SqlDataSource ds = GetDBConnection();
ds.SelectCommand = query;
DataView dv = (DataView)ds.Select(DataSourceSelectArguments.Empty);
return dv;
}
/// <summary>
/// Creates a SqlDataSource object with initialized connection string and provider
/// </summary>
/// <returns>An SqlDataSource that has been initialized.</returns>
public static SqlDataSource GetDBConnection()
{
SqlDataSource db = new SqlDataSource();
db.ConnectionString = GetDefaultConnectionString(); //retrieves connection string from .config file
db.ProviderName = GetDefaultProviderName(); //retrieves provider name from .config file
return db;
}
}
Then, in my projects, to retrieve data from databases I'll have some code like..
DataView dv=Database.GetDataView("select mycolumn from my table");
//loop through data and make use of it
I have taken some heat from people for using SqlDataSource in this manner. People don't seem to like that I'm using a Web control purely from code instead of putting it on an ASPX page. It doesn't look right to them, but they haven't been able to tell me a downside. So, is there a downside? This is my main question. Because if there's a lot of downsides, I might have to change how I'm doing many internal applications I've developed.
My Database class even works from non-ASP.NET situations, so long as I add the System.Web assembly. I know it's a slight increase in package size, but I feel like it's worth it for the type of application I'm writing. Is there a downside to using SqlDataSource from say a WPF/Windows Forms/Console program?
Well, there are no hard rules stopping anyone from doing such implementation.
However, following are few questions that need to be answered before doing that implementation.
Is this usage thread safe? (because there is every possibility the same call can be made by multiple consuming applications.
Will there be a layered differentiation (UI.Control being used in a Data layer)?
What if that control becomes obsolete / restricted in the next framework releases?
Given how easy it is to replace this code, whilst removing the temptation to use dynamic SQL queries to pass parameters, I think the question should be: is there any benefit to keeping the code as-is?
For example:
public static class Database
{
private static readonly Func<DbCommandBuilder, int, string> getParameterName = CreateDelegate("GetParameterName");
private static readonly Func<DbCommandBuilder, int, string> getParameterPlaceholder = CreateDelegate("GetParameterPlaceholder");
private static Func<DbCommandBuilder, int, string> CreateDelegate(string methodName)
{
MethodInfo method = typeof(DbCommandBuilder).GetMethod(methodName, BindingFlags.Instance | BindingFlags.NonPublic, Type.DefaultBinder, new Type[] { typeof(Int32) }, null);
return (Func<DbCommandBuilder, int, string>)Delegate.CreateDelegate(typeof(Func<DbCommandBuilder, int, string>), method);
}
private static string GetDefaultProviderName()
{
...
}
private static string GetDefaultConnectionString()
{
...
}
public static DbProviderFactory GetProviderFactory()
{
string providerName = GetDefaultProviderName();
return DbProviderFactories.GetFactory(providerName);
}
private static DbConnection GetDBConnection(DbProviderFactory factory)
{
DbConnection connection = factory.CreateConnection();
connection.ConnectionString = GetDefaultConnectionString();
return connection;
}
public static DbConnection GetDBConnection()
{
DbProviderFactory factory = GetProviderFactory();
return GetDBConnection(factory);
}
private static void ProcessParameters(
DbProviderFactory factory,
DbCommand command,
string query,
object[] queryParameters)
{
if (queryParameters == null && queryParameters.Length == 0)
{
command.CommandText = query;
}
else
{
IFormatProvider formatProvider = CultureInfo.InvariantCulture;
DbCommandBuilder commandBuilder = factory.CreateCommandBuilder();
StringBuilder queryText = new StringBuilder(query);
for (int index = 0; index < queryParameters.Length; index++)
{
string name = getParameterName(commandBuilder, index);
string placeholder = getParameterPlaceholder(commandBuilder, index);
string i = index.ToString("D", formatProvider);
command.Parameters.AddWithValue(name, queryParameters[index]);
queryText = queryText.Replace("{" + i + "}", placeholder);
}
command.CommandText = queryText.ToString();
}
}
public static DataView GetDataView(string query, params object[] queryParameters)
{
DbProviderFactory factory = GetProviderFactory();
using (DbConnection connection = GetDBConnection(factory))
using (DbCommand command = connection.CreateCommand())
{
command.CommandType = CommandType.Text;
ProcessParameters(factory, command, query, queryParameters);
DbDataAdapter adapter = factory.CreateDataAdapter();
adapter.SelectCommand = command;
DataTable table = new DataTable();
adapter.Fill(table);
return table.DefaultView;
}
}
}
With this version, you can now pass in parameters simply and safely, without relying on custom code to try to block SQL injection:
DataView dv = Database.GetDataView(
"select mycolumn from my table where id = {0} and name = {1}",
1234, "Robert');DROP TABLE Students;--");
EDIT
Updated to support parameters for different providers, with help from this answer.
The only issues I see are
(1) this is like reinventing the wheel. There is Enterprise library v5 for FW3.5 and v6 for FW4.5, which has data access components. Use that.
With EL you can make a call and have 2,3,4 tables loaded in Dataset. With your method this is not possible, only one at the time.
Enterprise library is a complete Data Access suite provided by Microsoft. It takes care of all the little details and all you need is to call your data. This is complete data access layer. And if you look deeper, EL allows for integration of Data and Caching, and other things. But you don't have to use what you don't need. If you need data access you can use only that.
And (2) Generally, this is not a good idea to write low level assembly with high-level assembly in reference. Anything System.Web.... is UI and client related stuff. In a layered cake design this is like the top of it and Data Access is on the bottom. All references [save for "common"] should travel from bottom to the top and you have it in opposite direction.
Look at this picture:
This is from Microsoft. You see the layers of the "cake". All references are going up. What you've done - you took UI-related component and wrote Data Access in it.
You can call it opinion-based - but this opinion is standard practice and pattern in software development. Your question is also opinion based. Because you can code everything in single file, single class, and it will work. You can set references to System.Windows.Forms in Asp.net application, if you want to. Technically, it is possible but it is really bad practice.
Your application now have limited reusability. What if you write WPF component or service that need to use same Data Access. You have to drag all System.Web into it?
I have a SqlDataReader, but it never enters into Read().
When I debug it, it pass the loop while(readerOne.Read()). It never enter into this loop even though there is data.
public static List<Pers_Synthese> Get_ListeSynthese_all(string codeClient, DateTime DateDeb, DateTime DateFin)
{
try
{
using (var connectionWrapper = new Connexion())
{
var connectedConnection = connectionWrapper.GetConnected();
string sql_Syntax = Outils.LoadFileToString(Path.Combine(appDir, #"SQL\Get_ListeSynthese_All.sql"));
SqlCommand comm_Command = new SqlCommand(sql_Syntax, connectionWrapper.conn);
comm_Command.Parameters.AddWithValue("#codeClioent", codeClient);
comm_Command.Parameters.AddWithValue("#DateDeb", DateDeb);
comm_Command.Parameters.AddWithValue("#DateFin", DateFin);
List<Pers_Synthese> oListSynthese = new List<Pers_Synthese>();
SqlDataReader readerOne = comm_Command.ExecuteReader();
while (readerOne.Read())
{
Pers_Synthese oSyntehse = new Pers_Synthese();
oSyntehse.CodeTrf = readerOne["CODE_TARIF"].ToString();
oSyntehse.NoLV = readerOne["NOID"].ToString();
oSyntehse.PrxUnitaire = readerOne["PRIX_UNITAIRE"].ToString();
oSyntehse.ZoneId = readerOne["LE_ZONE"].ToString();
oSyntehse.LeZone = readerOne["LIB_ZONE"].ToString();
oSyntehse.LeDept = readerOne["DEPT"].ToString();
oSyntehse.LeUnite = readerOne["ENLEV_UNITE"].ToString();
oSyntehse.LePoids = Convert.ToInt32(readerOne["POID"]);
//oSyntehse.LePoidsCorr = Convert.ToInt32(readerOne["POID_CORR"]);
oSyntehse.LeColis = readerOne["NBR_COLIS"].ToString();
oSyntehse.LeCr = readerOne["NBR_CREMB"].ToString();
oSyntehse.SumMontantCR = readerOne["ENLEV_CREMB"].ToString();
oSyntehse.LeVd = readerOne["NBR_DECL"].ToString();
oSyntehse.SumMontantVD = readerOne["ENLEV_DECL"].ToString();
oSyntehse.LePrixHT = readerOne["PRIX_HT"].ToString();
oSyntehse.LePrixTTC = readerOne["PRIX_TTC"].ToString();
oSyntehse.TrDeb = readerOne["TR_DEB"].ToString();
oSyntehse.TrFin = readerOne["TR_FIN"].ToString();
oListSynthese.Add(oSyntehse);
}
readerOne.Close();
readerOne.Dispose();
return oListSynthese;
}
}
catch (Exception excThrown)
{
throw new Exception(excThrown.Message);
}
}
When I debug it with SQL Server profiler it shows the data....that meant the data is not empty, but it never enter into this loop.
while (readerOne.Read())
{
by the way my connection class:
class Connexion : IDisposable
{
public SqlConnection conn;
public SqlConnection GetConnected()
{
try
{
string strConnectionString = Properties.Settings.Default.Soft8Exp_ClientConnStr;
conn = new SqlConnection(strConnectionString);
}
catch (Exception excThrown)
{
conn = null;
throw new Exception(excThrown.InnerException.Message, excThrown);
}
// Ouverture et restitution de la connexion en cours
if (conn.State == ConnectionState.Closed) conn.Open();
return conn;
}
public Boolean IsConnected
{
get { return (conn != null) && (conn.State != ConnectionState.Closed) && (conn.State != ConnectionState.Broken); }
}
public void CloseConnection()
{
// Libération de la connexion si elle existe
if (IsConnected)
{
conn.Close();
conn = null;
}
}
public void Dispose()
{
CloseConnection();
}
}
and my SQL Statement:
exec sp_executesql N'SELECT CODE_TARIF,PRIX_UNITAIRE,TR_DEB,TR_FIN,LE_ZONE,T_TARIF_ZONE.LIBELLE as LIB_ZONE,
SUBSTRING(CP_DEST,1,2) as DEPT,T_UNITE.LIBELLE as ENLEV_UNITE,
count(NOID)as NOID,
SUM(CASE WHEN POID_CORR IS NOT NULL THEN POID_CORR ELSE POID END) as POID,sum(NBR_COLIS)as NBR_COLIS,COUNT(NULLIF(ENLEV_CREMB,0))as NBR_CREMB, sum(ENLEV_CREMB)as ENLEV_CREMB,COUNT(NULLIF(ENLEV_DECL,0))as NBR_DECL,sum(ENLEV_DECL)as ENLEV_DECL,sum(PRIX_HT)as PRIX_HT,sum(PRIX_TTC)as PRIX_TTC, sum (POID_CORR)as POID_CORR
FROM LETTRE_VOIT_FINAL
LEFT JOIN T_TARIF_ZONE ON LETTRE_VOIT_FINAL.LE_ZONE = T_TARIF_ZONE.NO_ID
LEFT JOIN T_UNITE ON LETTRE_VOIT_FINAL.ENLEV_UNITE = T_UNITE.NO_ID
where code_client = #codeClioent
and DATE_CLOTUR_REEL BETWEEN #DateDeb AND #DateFin
and STATUT_LV = 2
group by CODE_TARIF,PRIX_UNITAIRE,TR_DEB,TR_FIN,LE_ZONE,T_TARIF_ZONE.LIBELLE,SUBSTRING(CP_DEST,1,2),T_UNITE.LIBELLE
order by LE_ZONE,PRIX_UNITAIRE
',N'#codeClioent nvarchar(8),#DateDeb datetime,#DateFin datetime',#codeClioent=N'17501613',#DateDeb='2013-06-05 00:00:00',#DateFin='2013-06-05 23:59:00'
it return the data on SQL profiler:
my real query :
SELECT CODE_TARIF,PRIX_UNITAIRE,TR_DEB,TR_FIN,LE_ZONE,T_TARIF_ZONE.LIBELLE as LIB_ZONE,
SUBSTRING(CP_DEST,1,2) as DEPT,T_UNITE.LIBELLE as ENLEV_UNITE,
count(NOID)as NOID,
SUM(CASE WHEN POID_CORR IS NOT NULL THEN POID_CORR ELSE POID END) as POID,sum(NBR_COLIS)as NBR_COLIS,COUNT(NULLIF(ENLEV_CREMB,0))as NBR_CREMB, sum(ENLEV_CREMB)as ENLEV_CREMB,COUNT(NULLIF(ENLEV_DECL,0))as NBR_DECL,sum(ENLEV_DECL)as ENLEV_DECL,sum(PRIX_HT)as PRIX_HT,sum(PRIX_TTC)as PRIX_TTC, sum (POID_CORR)as POID_CORR
FROM LETTRE_VOIT_FINAL
LEFT JOIN T_TARIF_ZONE ON LETTRE_VOIT_FINAL.LE_ZONE = T_TARIF_ZONE.NO_ID
LEFT JOIN T_UNITE ON LETTRE_VOIT_FINAL.ENLEV_UNITE = T_UNITE.NO_ID
where code_client = #codeClioent
and DATE_CLOTUR_REEL BETWEEN #DateDeb AND #DateFin
and STATUT_LV = 2
group by
CODE_TARIF,PRIX_UNITAIRE,TR_DEB,TR_FIN,LE_ZONE,T_TARIF_ZONE.LIBELLE,SUBSTRING(CP_DEST,1,2),T_UNITE.LIBELLE
order by LE_ZONE,PRIX_UNITAIRE
it is strange....when the data is between :
DATE_CLOTUR_REEL BETWEEN '2013-06-05 00:00:00' and '2013-06-05 23:59:00'
but
DATE_CLOTUR_REEL BETWEEN '2013-06-01 00:00:00' and '2013-06-05 23:59:00'
it works.
This is the way it should be. You are not doing the connection.Open()
Also set up the connection string.
private static void ReadOrderData(string connectionString)
{
string queryString =
"SELECT OrderID, CustomerID FROM dbo.Orders;";
using (SqlConnection connection =
new SqlConnection(connectionString))
{
SqlCommand command =
new SqlCommand(queryString, connection);
connection.Open();
SqlDataReader reader = command.ExecuteReader();
// Call Read before accessing data.
while (reader.Read())
{
ReadSingleRow((IDataRecord)reader);
}
// Call Close when done reading.
reader.Close();
}
}
The perfect example of how to do it belongs to MSDN - Microsoft Website
NOTICE:
SqlCommand command =
new SqlCommand(queryString, connection);
connection.Open();
SqlDataReader reader = command.ExecuteReader();
Create the SqlCommand
then open the connection
You are doing it the other way, you open it and then create the command.
I also don't see where you set the query string, I just see that you add the parameters; are you missing it?
This is perhaps not the answer you're looking for, but your code sample exhibits a number of bad coding practices that are easy to fall into due to ADO.NET's poor API design. Rather than manually do all this sql-to-.net conversion, you should use a library that does this for you.
It's easier to avoid bugs when you're not using a bug-prone API.
I recommend PetaPoco - it's easier to use than your current code, and has virtually no overhead (and given your example, is probably faster). There are many other alternatives, however.
Issues with your code sample:
Improperly disposed objects: you aren't disposing SqlCommand and SqlDataReader properly. You possibly aren't disposing connections either (but that depends on Connexion internals).
Using .ToString rather than type-safe casts. You should never extract data from an SqlDataReader like that because it undermines the whole point of the type system, and it's slow to boot. (PetaPoco or something similar will help a lot here)
You're discarding stack traces on error due to the (pointless) try-catch. That just makes your code less readable and harder to debug. Don't catch unless you have have.
Keeping your query away from the code - your code is tightly coupled to the query, and this separation just makes it hard to keep them in sync. Also, loading from the filesystem each and everytime you query is slow and opens up unnecessary filesystem-related failure modes such as locking, max path lengths, and permissions. This is probably the source of your bug - your query probably doesn't do what you think it does.
With PetaPoco or something similar, your entire function would look something like this:
public static List<Pers_Synthese> Get_ListeSynthese_all(
string codeClient, DateTime DateDeb, DateTime DateFin) {
var db = new PetaPoco.Database("Soft8Exp_ClientConnStr");
//you should probably not be storing a query in a file.
//To be clear: your query should not be wrapped in exec sp_executesql,
//ADO.NET will do that for you.
string sql_Syntax = Outils.LoadFileToString(
Path.Combine(appDir, #"SQL\Get_ListeSynthese_All.sql"));
//You'll need to rename Pers_Synthese's properties to match the db,
// or vice versa, or you can annotate the properties with the column names.
return db.Fetch<Pers_Synthese>(sql_Syntax, new {
codeClioent = codeClient, //I suspect this is a typo
DateDeb,
DateFin
});
}
And in that much shorter, readable, faster form, you'll hopefully find whatever bug you have much faster.
Alternatives:
PetaPoco
Dapper (fewer features, but stackoverflow uses it!)
OrmLite (of ServiceStack fame)
Massive (older, uses dynamic which is a feature that can cause bad habits - I don't recommend this unless you really know what you're doing)
You could use heavier, more invasive ORM's like the Entity framework and NHibernate, but these require quite a bit more learning, and they're much slower, and they impose a particular workflow on you which I don't think makes them the best choice in your case.
when i debug it with sql profiler it show the data....that meant the data is not empty, but it never enter into this loop.
It's the other way round: if it never enters into this loop, then it means "the data is empty", i.e. the query returns no rows.
The bug is in your code, not SqlReader: you possibly have the wrong values in your parameters, or maybe the query you read from a file isn't what you think it is. Get the debugger out and inspect the query text and parameters.