Hello guys i building a web app with asp.net and using sql ( MS SQL server 2000 ) and now after getting closer to finish i noticed that the sql part of my code taking huge amounts of space... and i was wondering what ways are there to make it easier to maintain? maybe even change to some other language?
Here is a simple example of the way my sql code is built ( there are some much longer commands in my code but they built this way) :
Dim connectionString As String = ConfigurationManager.ConnectionStrings("ConnectionString").ToString()
Dim query As String = "SELECT workerName FROM [poWorker] WHERE ( companyId = #companyId ) AND (workerId=#workerId) "
Using con As New SqlConnection(connectionString)
con.Open()
Using da As New SqlDataAdapter()
Using command As New SqlCommand(query, con)
command.Parameters.Add(New SqlParameter("#workerId", Session("userId")))
command.Parameters.Add(New SqlParameter("#companyId", Session("companyId")))
Dim ds As New DataSet()
da.SelectCommand = command
da.Fill(ds, "test")
If ds.Tables(0).Rows.Count = 1 Then
managerName = ds.Tables(0).Rows(0)(0).ToString()
End If
End Using
End Using
con.Close()
End Using
This is taking a lot of space and i got a lot of sql written this way. I am sure there is some solution in making it easier to maintain, probably using a newer technology? Maybe if i could figure out a way to call all the sql commands from 1-2 functions but so far i failed to do such a thing since there big differences between many of those.
In the example you give, you could simplify the code by using ExecuteScalar - as you are just returning a single value. For example:
Dim query As String = "SELECT workerName FROM [poWorker] WHERE ( companyId = #companyId ) AND (workerId=#workerId) "
Using con As New SqlConnection(connectionString)
con.Open()
Using command As New SqlCommand(query, con)
command.Parameters.Add(New SqlParameter("#workerId", Session("userId")))
command.Parameters.Add(New SqlParameter("#companyId", Session("companyId")))
managerName = command.ExecuteScalar().ToString();
End Using
con.Close()
End Using
A modern way to access databases from code is to use an ORM. The one that Microsoft provides with the .NET Framework is Entity Framework. This allows you to write your query like this:
Dim worker as Worker =
dbContext.Workers
.Where(Function (w) (w.companyId = Session("companyId") and
w.workerId = Session("userId")))
.SingleOrDefault()
If worker IsNot Nothing Then
managerName = worker.workerName
End If
This approach also provides a far more robust approach to dynamic queries as opposed to piecing SQL strings together. For example, you can dynamically swap out Where clauses, OrderBy clauses, and so on, and still have completely typesafe code.
Entity Framework does not have builtin support for SQL Server 2000, but apparently there is a workaround.
The modern way is to no use SQL directly, but rather an OR-Mapper such as Entity Framework that allows you to query using Linq.
The query you show above would the be reduced to something shorter:
using(new context = MyAppDbContext())
{
var workerId = Session["userId"];
var companyId = Session["companyId"];
managerName = context.PoWorker
.Single(w => w.companyId == companyId && w.workerId == workerId)
.workerName;
}
Sorry for using C# syntax, but I hope you can figure out the intention.
Related
sqlCommand1 = new SqlCommand("INSERT INTO dbo.Orders(Title,Seats,Payment,DateNTime)"+
"VALUES ('"+ movieName+"',"+numTickets+",'"+creditCardType+"',"+DateTime.Now+")", sqlConnection1);
sqlCommand1.Connection.Open();
sqlCommand1.ExecuteNonQuery();
sqlCommand1.Connection.Close();
No idea what's wrong with this piece of code. Title and Payment are stored as nvarchar types, Seats as an int and DateNTime as DateTime.
Can someone help me with this?
Thanks in advance
Need quotes for dates as well:
,'" + DateTime.Now.ToString("yyyyMMdd") + "')"
But this is very dangerous code you know. You really should use parametrized query for such things!
You will be in trouble if user enters in Title textbox something like this:
some text', 1, 1, '20100101'); drop table dbo.Orders--
And you are fired the same day.
Very likely the error lies here:
... ,"+DateTime.Now+")"
You must make sure, that the string expression for DateTime.Now is parseable in SQL.
Do not put your values into the SQL comman (read about SQL injection)
Read about parameters and how to pass them
Never rely on culture dependant date-time-formats... (read about ISO8601 or ODBC)
Guessing this is called from c# code, you should use parameters instead of concatenating strings into sql statements.
This will both protect you from sql injection attacks and fix your syntax error:
This code should probably work for you, though it's written right here and I didn't test it:
using (var sqlConnection1 = new SqlConnection("ConnectionString"))
{
using (var sqlCommand1 = new SqlCommand("INSERT INTO dbo.Orders(Title,Seats,Payment,DateNTime)" +
"VALUES (#movieName, #numTickets, #creditCardType, #DateTime.Now)", sqlConnection1))
{
sqlCommand1.Parameters.Add("#movieName", SqlDbType.VarChar).Value = movieName;
sqlCommand1.Parameters.Add("#numTickets", SqlDbType.VarChar).Value = numTickets;
sqlCommand1.Parameters.Add("#creditCardType", SqlDbType.Int).Value = creditCardType;
sqlCommand1.Parameters.Add("#movieName", SqlDbType.DateTime).Value = DateTime.Now;
sqlCommand1.Connection.Open();
sqlCommand1.ExecuteNonQuery();
}
}
Try to use following
sqlCommand1 = new SqlCommand("INSERT INTO dbo.Orders(Title,Seats,Payment,DateNTime)"+
"VALUES ('"+ movieName+"',"+numTickets+",'"+creditCardType+"','"+DateTime.Now+"')", sqlConnection1);
sqlCommand1.Connection.Open();
sqlCommand1.ExecuteNonQuery();
sqlCommand1.Connection.Close();
Even after that modification, if you are getting error, please tell datatype of Title,Seats,Payment,DateNTime
You have format date part also.
sqlCommand1 = new SqlCommand("INSERT INTO dbo.Orders(Title,Seats,Payment,DateNTime)"+
"VALUES ('"+ movieName+"',"+numTickets+",'"+creditCardType+"','"+DateTime.Now.ToString("yyyyMMdd")+"')", sqlConnection1);
sqlCommand1.Connection.Open();
sqlCommand1.ExecuteNonQuery();
sqlCommand1.Connection.Close();
I currently have a site (.net 2.0) with MS Access DB for data, within the access.mdb are 2 linked tables from another DB on the same site. - all of my code works fine and all my DB connections point to the one DB. I have a need now to achieve the same results, but don't want to create the links in the live DB. How can the same results be achieved via another method?
I looked at an example from: Syntax for Import Into from Different DBs - MS Access
My tables are large and don't think this is fastest way, I would think two connections and working with tables would be better, but I'm open to a proper way to accomplish this.
Here is my web.conf file with connections to the one DB.
<connectionStrings>
<clear />
<add name="OdbcServices" connectionString="Driver={Microsoft Access Driver (*.mdb)};Dbq=e:\LeaveDB.mdb;" />
</connectionStrings>
My end result I need to join 2 tables from different DB's something like:
SELECT username.table1, password.table1, fullname.table2 FROM Table1 INNER JOIN [;DATABASE=Z:\Docs\Test.mdb].Table2 ON username.table1 = username.table2;
Rather than create an "official" linked table (TableDef object) you might get away with simply creating a View that points to the table in the other database. I just tried this in C# and it seemed to work fine:
string myConnectionString;
myConnectionString =
#"Provider=Microsoft.ACE.OLEDB.12.0;" +
#"Data Source=C:\Users\Public\Database1.accdb;";
using (var con = new OleDbConnection())
{
con.ConnectionString = myConnectionString;
con.Open();
using (var cmd = new OleDbCommand())
{
cmd.Connection = con;
cmd.CommandType = System.Data.CommandType.Text;
cmd.CommandText =
#"CREATE VIEW otherTable AS " +
#"SELECT * FROM [;Database=C:\__tmp\main.accdb].otherTable";
cmd.ExecuteNonQuery();
}
con.Close();
}
That creates a "View" (saved query in Access) that seems to work just like a linked table.
I never asked any questions on stackoverflow but it already brought me tons of answers !
But this time, after some extended research, I have to ask it myself.
I'm using VB.Net with Sqlite and my query doesn't return any value when I execute it with parameters. I guess this has something to do with the fact SituationString contains commas but I really can't figure it out.
Here's is my code :
dim ChildCtx as Integer
dim SituationString as String
SituationString="968,970,978,979,980,981,995,1022,1099,1119"
With DataBase
.SQL_CMD.Parameters.Clear()
.SQL_CMD.CommandText = "SELECT SERVCTX_NO FROM SERVCTX WHERE SERVCTX_NO IN (#situationstring) AND MASTER = '1'"
.SQL_CMD.Parameters.Add("#situationstring", SqlDbType.VarChar, 255).Value = SituationString
ChildCtx = .SQL_CMD.ExecuteScalar
.SQL_CMD.Parameters.Clear()
End with
Connection is open and query works out fine if I write the whole query into one string.
Thanks,
You are mixing two techincs: merging constant data into SQL command and using parameters to pass data.
I suppose SituationString is a list of integer you expect for SERVCTX_NO.
So, you must merge it into statement:
.SQL_CMD.CommandText = "SELECT SERVCTX_NO FROM SERVCTX WHERE SERVCTX_NO IN ("+SituationString+") AND MASTER = '1'"
ChildCtx = .SQL_CMD.ExecuteScalar
Using parameters, as you did, SERVCTX_NO would be expected to be a string with 968,970,978,979,980,981,995,1022,1099,1119 exact content!
The following query concatenates the columns results for each row. I need to seperate columns with either some kind of delimiter or unique row[i] results.
Query
"exec rfc_read_table #query_table='VBAK', #rowcount=50, #FIELDS= '<FIELDS><RFC_DB_FLD xmlns="http://Microsoft.LobServices.Sap/2007/03/Types/Rfc/"><FIELDNAME>MANDT</FIELDNAME></RFC_DB_FLD><RFC_DB_FLD xmlns="http://Microsoft.LobServices.Sap/2007/03/Types/Rfc/"><FIELDNAME>VBELN</FIELDNAME></RFC_DB_FLD></FIELDS>,#fields=#flds output'"
.NET
using (SAPCommand cmd = conn.CreateCommand())
{
cmd.CommandText = //See query above
SAPDataReader rdr = cmd.ExecuteReader();
while (rdr.Read())
{
Console.Write(" {0} ", rdr[0]);
//Console.Write(" {0} ", rdr[1]);//null...
Console.WriteLine();
}
UPDATE
I am able to separate the columns by following the indexes returned by
DataTable dtFields = (DataTable)cmd.Parameters["#flds"].Value; (updated query)
The process is so klunky and tosses up exceptions because the indexes become unreliable when the last columns in the query return empty results (also found a length indicator that was wrong). I worked around most of it, but this is so bad. Is there a better supported method to query SAP with .NET?
it works for me.
ADD this --> in your script:
#DELIMITER ='|'
"exec rfc_read_table #query_table='VBAK', #rowcount=50, #DELIMITER ='|', #FIELDS= '<FIELDS><RFC_DB_FLD xmlns="http://Microsoft.LobServices.Sap/2007/03/Types/Rfc/"><FIELDNAME>MANDT</FIELDNAME></RFC_DB_FLD><RFC_DB_FLD xmlns="http://Microsoft.LobServices.Sap/2007/03/Types/Rfc/"><FIELDNAME>VBELN</FIELDNAME></RFC_DB_FLD></FIELDS>,#fields=#flds output'"
Im not sure but in Version 3 of SAP .NET connector such problem should be resolved.
We're working on making our application 100% localizable, and we're mostly there. However, we occasionally find an English string still hard-coded in stored procedures. (We're on SQL Server 2005 by the way.) We have thousands of stored procedures, so going through them by hand is impractical. I'm trying to think of the most accurate means of automating a search.
Now, I know there's no means to search for "English" strings - but searching for strings bounded by single quotes and perhaps 20+ characters long should flush MOST of them out. Good enough for our purposes now. But I'm anticipating a lot of false-positives in the comments of the stored procedures, too.
So how would you approach this? Would SMO let me tease apart the SQL in a stored procedure from the comments in it? Do I have to use OBJECT_DEFINITION() and start hacking out some terrifying regular expressions?
Much appreciated in advance, folks.
Another thought: Microsoft provides, with Visual Studio, an assembly that can parse SQL. I've used it, and it's fairly simple to use. You might be able to use it to parse the text of your stored procedures; it can return a list of the various tokens in your statements, including the type of the token. So, it should be able to help you differentiate between what is a string of text you might be interested in vs. what is part of a comment and can be ignored. There are more details here: http://blogs.msdn.com/b/gertd/archive/2008/08/21/getting-to-the-crown-jewels.aspx.
Basically, from .NET, you'd open a connection to your database and query syscomments for your stored procedures' text. You'd loop through each procedure and parse it using these parser. Then you'd use the Sql100ScriptGenerator to get the tokens out of the parsed text, loop through the tokens and look for tokens whose types are either ASCII or Unicode string literals. For those strings, check their length to see if it's 20+, and if it is, flag the strings and the procs as needing further review.
I played around with it a bit, and here is a very raw example to illustrate the basic principle:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data;
using System.Data.SqlClient;
using Microsoft.Data.Schema;
using Microsoft.Data.Schema.ScriptDom;
using Microsoft.Data.Schema.ScriptDom.Sql;
namespace FindHardCodedStrings
{
class Program
{
static void Main(string[] args)
{
using (SqlConnection conn = new SqlConnection())
{
SqlConnectionStringBuilder bldr = new SqlConnectionStringBuilder();
bldr.DataSource = "localhost\\sqlexpress";
bldr.InitialCatalog = "msdb";
bldr.IntegratedSecurity = true;
conn.ConnectionString = bldr.ConnectionString;
SqlCommand cmd = conn.CreateCommand();
cmd.CommandType = System.Data.CommandType.Text;
cmd.CommandText = "select [text] from syscomments";
SqlDataAdapter da = new SqlDataAdapter(cmd);
DataSet ds = new DataSet();
da.Fill(ds);
TSql100Parser parser = new TSql100Parser(false);
Sql100ScriptGenerator gen = new Sql100ScriptGenerator();
gen.Options.SqlVersion = SqlVersion.Sql100;
foreach (DataRow proc in ds.Tables[0].Rows)
{
string txt = proc[0].ToString();
using (System.IO.TextReader sr = new System.IO.StringReader(txt))
{
IList<ParseError> errs;
IScriptFragment frag = parser.Parse(sr, out errs);
if (null == frag)
continue;
IList<TSqlParserToken> tokens = gen.GenerateTokens((TSqlFragment)frag);
foreach (TSqlParserToken token in tokens)
{
if (token.TokenType == TSqlTokenType.UnicodeStringLiteral || token.TokenType == TSqlTokenType.AsciiStringLiteral)
{
if (token.Text.Length >= 20)
Console.WriteLine("String found: " + token.Text);
}
}
}
}
}
}
}
}
We resolved a problem stemming from this by creating SQL Servers in different language/region settings and running our sp's, noting which ones broke.
This may not be the most elegant solution but we were able to do it quickly because of the limited different locality settings we support.