I have a Visual Basic application where I have a connection to a MS SQL database. I have code which defines a SqlDataReader, opens the connection, and executes the ExecuteReader() command. I use the following code to retrieve the data from the reader
While myDataReader.Read()
Session("menu_PEO") = myDataReader("menu_PEO")
Session("menu_Transfer") = myDataReader("menu_Transfer")
Session("menu_Loan") = myDataReader("menu_loan")
End While
menu_PEO, menu_Transfer, and menu_loan are 3 of the column headings in the data that the SQL returns.
I am now tasked with converting the code to c#. I have the following code in c# which works:
while (dataReader.Read())
{
dbMenuPEO = dataReader.GetString(1);
dbMenuTransfer = dataReader.GetString(2);
dbMenuLoan = dataReader.GetString(3);
}
Since my SQL is a SELECT *, I can not guarantee the order of the data being returned so I dont want to rely on specifying the instance of the GetString.
Is there a way in c# to specify the column name that I would like to retrieve similar to the way it works in Visual Basic?
Thanks!
There are two ways to do this. The easy way is to use the named indexer:
dbMenuPEO = (string)dataReader["menu_PEO"];
Which is essentially the same as:
dbMenuPEO = (string)dataReader.GetValue(dataReader.GetOrdinal("menu_PEO"));
You can move the ordinal lookup outside of the loop, which is a bit more efficient. You can also use the the strongly-typed accessor which avoids the cast. This can be important when reading value types (int, dateTime, etc) as it avoid needing to box the value, which would happen with GetValue.
var menuPeoIdx = dataReader.GetOrdinal("menu_PEO");
while(dataReader.Read())
{
dbMenuPEO = dataReader.GetString(menuPeoIdx);
}
Generally, you should never use select * in a production system. Always explicitly list the columns you want. The reason is that it allows the underlying tables to change without having to fix the consuming code, or over-selecting columns that you don't need. Select * is a great tool for "exploring" a data set, but should only be used as a dev tool.
I have code which defines a SqlDataReader, opens the connection, and executes the ExecuteReader()
And isn't it the most incredibly tedious code to have to write? Many people have thought this over the time and many things have been invented to relieve you of the tedium of it. MarkPflug's answer directly addresses your question, but just in case you aren't aware that there are significant productivity boosts available I'd like to introduce you to one of these technologies
Is there a way in c# to specify the column name that I would like to retrieve similar to the way it works in Visual Basic?
Here's a way to do it, in that when you do this you don't have to type it. It avoids typing the same thing again that you've already typed (twice - once for the variable name, once in the SQL)
Use the nuget package manager built into visual studio, to install Dapper
Then lets say you have a class that holds your data:
//C#
record DbMenu(string DbMenuPEO, string DbMenuTransfer, string DbMenuLoan);
'or VB, if you like that sort of thing
Class DbMenu
Public Property DbMenuPEO as String
Public Property DbMenuTransfer As String
Public Property DbMenuLoan As String
End Class
You can get Dapper to make your query, add any parameters, open your connection, download your data, fill up a list full of your classes, close the connection and return it.. all in one line of code:
//C#
using var conn = ... //code here that gets a connection; doesn't need to be open
var myListOfDbMenus = conn.Query<DbMenu>("SELECT * FROM ... ");
'VB
Using conn = ...
Dim myListOfDbMenus = conn.Query(Of DbMenu)("SELECT * FROM ... ");
End Using
The short short version is: your c# class properties should be named the same as your columns. If they aren't, it's easiest to use AS xyz in the SQL to equalize the names. If you want to write a parameterized query, you provide #parameterNames that are the same as the property names of an anonymous object you pass at the same time as your query:
var q = conn.Query<Type>("SELECT ... WHERE col = #val1", new {val1 = "hello" } );
If you like writing SQL and having that low level control/don't want to use an ORM like EF, then Dapper lets you carry on doing the SQL directly as you're doing, but takes away all the repetitive surrounding boilerplate
The SQLDataReader class exposes a set of instance methods that allow to retrieve the value of a field of a specific type, such as GetInt32() and GetString(), which can be used in combination with the GetOrdinal() method
Example
dataReader.GetString(dataReader.GetOrdinal("YOUR FIELD NAME HERE"))
Related
I need to filter a sql request by passing a list of id to , this is the command:
var command = "select Software.Name as SoftwareName,SoftwareType.Name as SoftwareType from AssetManagement.Asset as Software inner join AssetManagement.AssetType as SoftwareType on (SoftwareType.Id = Software.TypeId) where Software.Id in (#P)";
cmd.Parameters.AddWithValue("#P", authorizedSoftwaresId);
authorizedSoftwaresId is a list of string , containing data like :
"7D23968B-9005-47A9-9C37-0573629EECF9,F1982165-3F6D-4F35-A6AB-05FA116BA279"
with that it returns to me just one row, I tried adding quotes foreach value but i got "converstion from string caractere to uniqueidentifier failed " exception
This is a pretty common problem and the answer might depend on your database engine and whether you're using ADO.Net, Dapper, etc.
I'll give you some hints from my own experience with this problem on both MS Sql Server and PostgreSQL.
A lot of people think AddWithValue is a devil. You might consider Add instead.
The IN (#P) in your SQL statement might be the source of your problem. Try the Any option instead. See Difference between IN and ANY operators in SQL ; I've had success with this change in similar situations.
If all of your inputs are GUIDs, you might consider changing the type to a collection of GUIDs, although I don't think this is the fix, you have to try everything when you're stuck.
If you have to, you can parse the string version of your collection and add the ticks (') around each value. This choice has consequences, like it may prevent you from using a parameter (#P), and instead construct the final SQL statement you desire (i.e., manually construct the entire WHERE clause through string manipulations and lose the parameter.)
Good luck.
I am using Dapper to process data.
Here is a code sample:
using(var connection = new SqlConnection(ConfigurationManager.AppSettings["MyConnectString"])
{
var sql = string.Format(
#"SELECT [Column1]
FROM [MyTable]
WHERE [Column3] > {0}
AND [Column4] < {1}"
, myValue1
, myValue2
);
var result = connection.Query<long>(sql).ToList();
}
My project writes many sql scripts like the one above.
I want to write scripts to files (maybe QueryAccount.config, QueryOrder.config, (xml format), or others)
Then I can load the script from the files.
What I want is write my scripts in files, then I write the same class
in file. (eg. I write all query Product scripts in Product.config, and all query Order scripts in Order.config)
Then I use like:
var cmd = MyCommandManager.GetScript("QueryProduct");
cmd.SetParam("#ProductId", 123);
cmd.SetParam("#InvoicingDate", DateTime.Now(-7))
...
Script in file like:
SELECT [ProductName]
FROM [Product]
WHERE [ProductId] = #ProductId
AND [InvoicingDate] = #InvoicingDate
Ignore the downvoters. SQL should always be in its own file. These files should have the extension .sql, not .config. As such, they will be edited in the VS SQL editor, a real comfort. You want one file per query I think. Nothing is gained by grouping different queries in the same file. I would advocate putting these files next to the .cs files that consume them, grouping together files you open together, and files you're likely to want to delete together one day.
Once created, right click the .sql in solution explorer, Properties --> Build Action --> Embedded resource. Then, in your MyCommandManager.GetScript() method, use GetManifestResourceStream() to access the query text. Compared to stored procedures, this has the huge advantage that your queries are compiled with the calling code, so you never have to worry about synching the version of your stored procedures and your application.
If all this seems like a lot of work, it kinda is. That's why no one does it, but they should :-) Grab QueryFirst, and it will be done for you, and a lot else besides. Disclaimer : I wrote QueryFirst.
If you have full access to the database you can try to implement a Stored Procedure to store the SQL text. All you would need to do is reference the Stored Proc name in the Dapper query and set the command type to StoredProcedure and you will be good to go, like so:
using(var cn = new SqlConnection("MyConnectionString"))
{
cn.Open();
return cn.Query<MyModel>("MyProcName", new { Parameter1 = myValue1, Parameter2 = myValue2 }, commandType: CommandType.StoredProcedure);
}
Using SQL Paramters instead of injecting the values into your query is a very smart thing to do, as it prevents SQL Injection attacks. reformatting your query like so would help:
#"SELECT [Column1]
FROM [MyTable]
WHERE [Column3] > #Parameter1
AND [Column4] < #Parameter2"
Notice my parameter names match the dapper call above. When I do not use Stored Procedures, however, I usually create a private const string at the top of the class that references the query for my "storage".
public class QueryClass
{
private const string query = "SELECT * FROM Table1";
public IEnumerable<MyModel> CallQuery()
{
// Dapper Query Details
}
}
I subscribe to a Command/Query Pattern like this one so I never have an issue with query storage since each class usually has one query.
EDIT
If you like the Command/Query pattern, I recommend you check out MediatR as it is a nice implementation of such pattern.
SECOND EDIT
I see what you are trying to do by adding the SQL Queries to some sort of config file, however if I can I would like to advise you against that. At my last job, all SQL Queries were stored in XML files that were compiled into the application. It seemed like an effective way to manage queries, but once the application grew to even a few SQL XML files we had a hard time managing what queries could be found where, and eventually we had queries duplicated in several XML files. We also had many problems with typo's and other XML structure errors that were not caught until runtime, but I guess you can have typo's in any string so that won't necessarily go away. It ends up being a mess and causing more problems than it solves.
I believe that having the SQL query text as close to the code that requires it as possible is a better alternative, and if you are clever about namespacing and organizing query objects, you can make it easy for devs to find a query via intellisense.
I have a number of properties in Properties.Settings.Default whose name all start with "store" and an integer number, these numbers follow in sequence and what I would like to do after the method is fired off is to increase the number in the property name, i.e. from "store1" to "store2".
I keep getting an "identifier expected" error. I'm rather new at programming, so any help would be appreciated.
public void store()
{
storename1.ForeColor = Color.Orange;
if (File.Exists(Filedestination))
{
File.Delete(Filedestination);
}
NumberOfScales = Properties.Settings.Default.("store"+ Convert.ToString(storeNumber) + "NrOfScales");
StartRange = EndRange - Properties.Settings.Default.DegrendelNrOfScales;
IPRange = Properties.Settings.Default.DegrendelIPRange;
CurrentRange = StartRange;
PingScales();
}
I don't even know how I can read a property with the name ("store" + Convert.ToString(storeNumber) + "NrOfScales"). If I knew how to do that, it would shorten the code by at least 9/10ths as I would not have to redo this for every single instance of all the stores that I have. Is there any way I can get this to work?
At first glance, it seems like you possibly chose the wrong place to store your data. Is there any particular reason why you are using Windows Forms' application settings (Settings) to store data?
If you really want to do it that way, IIRC you can access a setting by its name using Properties.Settings.Default["PropertyName"] (where you can substitute "PropertyName" by any expression that yields a string, e.g. "store" + Convert.ToString(storeNumber) + "NrOfScales" (or more succinctly in Visual Studio 2015 or later, $"store{storeNumber}NrOfScales"). You will get back an object that you'll have to cast to whatever type of values you stored in there, e.g.:
var numberOfScales = (int)Properties.Settings.Default[$"store{storeNumber:D}NrOfScales"];
Some hints about syntax used here:
The [] syntax is called an "indexer".
$"…" is for string interpolation. It often allows for neater concatenation of strings than by using +.
The D (decimal) format specifier used in $"…{…:D}…" makes sure that storeNumber will be formatted as a decimal without any thousands/decimal separators.
Now, back to my initial question, if you're open to other means of storing data, let me point out a few alternatives:
If you only need the data during one single execution of your program, i.e. the data does not need to be persisted from one run of the program to the next, then a Dictionary<string, int> might be sufficient. Dictionaries allow you to associate int values with string values and look them up by these strings.
If your data is actually user content / business data, then don't store it as "application settings". At the least, store the data to a simple file (possibly to Isolated Storage) using the facilities under System.IO (File.Create, File.Open, StreamWriter, etc.). If you want to store structured data, you could make use of relational databases (see e.g. SQLite, SQL Server Compact, or SQL Server) or document databases.
If the data you're storing is in fact data that influences the setup / configuration of your application, then your current use of application settings might be fine.
In my program, I want to select some bookIDs into a tempDB for later queries like this (using Dapper extension):
using (var conn = new SqlConnection(connStr)) {
conn.Execute("SELECT bookID INTO #tempdb WHERE ... FROM Books");
int count = conn.ExecuteScalar<int>("SELECT COUNT(*) FROM #tempdb");
var authors = conn.Query("SELECT * FROM #tempdb LEFT JOIN BookAuthors ON ...");
}
However when I execute the page, I get following exception:
Invalid object name '#tempdb'.
It seems that life-cycle of #tempdb is only valid in first query ?
It looks like you're using the implicit connection opening/closing. This will indeed cause problems with transient objects. If you need temp tables between queries, you will need to manually open the connection before you execute any such queries. This should then work fine, and many examples in the test suite make use of temp tables in the way.
However, from a practical standpoint, making use of temporary tables to transfer state between queries is ... awkward. In addition to being brittle, it isn't good for the plan cache, as #foo has a different meaning between all uses on different connection (including reset but reused connections).
I found a previous poster who met the same problem and his solution.
Using dapper, why is a temp table created in one use of a connection not available in a second use of the same connection
The post indicates that you have to "CREATE TABLE #tempdb" explicitly in your SQL first and everything goes fine. Even the poster himself don't know why such style of coding works.
I am debugging code someone else wrote that calls a lot of stored procedures (sql server 2008 r2) from C# code. The C# code looks like this
SqlCommand sqlCommand = new SqlCommand(strSP, ConnectionOpen());
sqlCommand.CommandType = CommandType.StoredProcedure;
sqlCommand.CommandTimeout = intTimeOut;
//System.Data.SqlClient.SqlParameter[] prmSQL
if (prmSQL != null)
{
while (i < prmSQL.Length)
{
sqlCommand.Parameters.Add(prmSQL[i]);
i = i + 1;
}
}
SqlDataReader sqlReader = sqlCommand.ExecuteReader();
For debugging my stored procedures I really need the string that sql management studio needs which is like
exec sp_name param one, param two (with quotes if needed for strings and dates..)
The sql command object does not provide this string via some property. The only way I know is to run the sql profiler on sql server and grab the string. Unfortunately the DBA's do not like this since they say running the profiler impacts performance. Is there any addin or code snippet you guys use to get the sp exec string from c# code ? Whats the best way to get this string ? Thanks
You could use a tool like mvc-mini-profiler available on NuGet (note: the name is misleading; it isn't limited to MVC). Minor clarification - since it wraps the connection, you would need to use the abstract DbConnection rather than SqlConnection, and then you just tweak the one line of code (probably in a utility class somewhere) that creates your connection, i.e. instead of:
var conn = new SqlConnection(someString);
return conn;
you might use:
var conn = new SqlConnection(someString);
return new StackExchange.Profiling.Data.ProfiledDbConnection(
conn, MiniProfiler.Current);
There's a couple of other steps to enable it (all shown on the site page), but it literally takes 2 minutes to add to an MVC application. The output is that it monitors, in real time, for enabled users (developers etc), all the activity. We use it 24x7 on stackoverflow/stackexchange (meaning: we made very sure it didn't impact performance). A live demo is available on https://data.stackexchange.com/ - just log in, and the profiling data is visible top-left. It automatically presents the data in a form runnable from SSMS, because that is how we often use it - so: it presents parameters as though they were variable declarations / initializations.
It also plays nicely with ORMs such as LINQ-to-SQL and dapper-dot-net (and many others).
Rep is too low (still a noob to StackOverflow)to comment so I'm posting this as an answer. My apologies. However, you might consider looking at SMO. SMO is a .NET object model that can be used to interact with SQL Server. Using SMO you can get a reference to a specific Stored Procedure
and then enumerate it's parameters.
That might help you get started.
In order to construct the EXEC command, you will need to know the parameter names used by the procedure. I believe you can find them by using the GetDbSchemaTable method, whcih will retrieve stored procedure SQL (I have done this using MS-Access/OLEDB and am assuming it works the same for MS-SQL/SqlClient):
using (conn == new OleDb.OleDbConnection(DBConnection)) {
conn.Open();
DataTable DBObject = conn.GetOleDbSchemaTable(OleDb.OleDbSchemaGuid.Procedures, null);
}
The column named "PROCEDURE_DEFINITION" contains the procedure's SQL and hopefully the parameter list.
You may also want to have a look at Obtaining Schema Information from a Database.
HTH