How would I take info stored in a Select method and transfer it to a string? I'm trying to get the max value from the match_id column and get its value from command.CommandText into the matchCode string. Where would I go from here?
string connectString = "Server=myServer;Database=myDB;Uid=myUser;Pwd=myPass;";
string matchCode = "";
MySqlConnection connect = new MySqlConnection(connectString);
MySqlCommand command = connect.CreateCommand();
command.CommandText = "SELECT MAX(VAL(match_id)) FROM `data`";
try
{
connect.Open();
command.ExecuteNonQuery();
matchCode = "??";
connect.Close();
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
I'm new to C#, as it's like my fourth day trying it out. Thanks for the help!
The ExecuteNonQuery() method is for INSERT/UPDATE/DELETE queries. If you're just getting a single value back, use ExecuteScalar(). If you're getting a whole result set back, use ExecuteReader() or Fill() a DataSet object.
Also, there are some things that are idiomatic to C# that you should be doing:
public int GetMatchCode()
{
//this could be loaded from config file or other source
string connectString = "Server=myServer;Database=myDB;Uid=myUser;Pwd=myPass;";
string sql = "SELECT MAX(VAL(match_id)) FROM `data`";
using (var connect = new MySqlConnection(connectString))
using (var command = new MySqlCommand(sql, connect))
{
connect.Open();
var result = command.ExecuteScalar();
if (result == DBNull.Value)
{
//what you do here depends on your application
// if it's impossible for the query to return NULL, you can even skip this
}
return (int)result;
}
}
Some of the changes need explanation:
I don't ever call .Close(). The using block takes care of that for me, even if an exception was thrown. The old code would have left the connection hanging if an exception occured.
.Net developers tend to believe in very small methods. More than that, this method ought to be part of a class that has nothing but other simple public data access methods and maybe a few private helper methods or properties for abstracting common code in the class.
There is no exception handling code here. If you have small methods that are part of a generic database access class, exception handling should be at higher level, where you are better positioned to make decisions about how to proceed.
Related
I am connecting to an SQL Server 2012 database to query for a single value based on an ID. (It may be worth mentioning that this database is on a server on another continent from my development machine, and so latencies are quite high. Somewhere around 100ms).
The query appears to execute successfully. The HasRows property of the SqlDataReader object is set to true, so I try to use the value to assign a variable. When I run the program normally, I encounter an exception with message 'Given key was not present in the dictionary'. If I stop the execution and inspect the SqlDataReader object, and enumerate the results. Firstly I am told 'enumeration yielded no results' and then when I continue execution I get a different exception with the message 'invalid attempt to read when no data is present'
Here is the code in question:
SqlConnection sql_conn = new SqlConnection(ConnectionString);
SqlCommand sql_cmd = new SqlCommand(String.Format("select ItemType from ItemTable where ItemID='{0}'", item_id), sql_conn);
Console.WriteLine(sql_cmd.CommandText);
sql_conn.Open();
SqlDataReader rdr = sql_cmd.ExecuteReader();
rdr.Read();
if (rdr.HasRows) //True
{
item_type= TypesMap[rdr["ItemType"].ToString()]; //Either 'given key not found in dictionary' or 'invalid attempt to read when no data is present'
}
I have executed the SQL statement in SQL Server Management Studio and it is successful. I have tried hardcoding an ItemID into the statement in the C# code, and the same errors exist.
What more can I do to debug this? Everything appears to be okay, until I try to access the results of the query.
You have to debug: it seems that the TypesMap doesn't have the key read from the database:
// Wrap IDisposable into using
using (SqlConnection sql_conn = new SqlConnection(ConnectionString)) {
// Make SQL readable
// Make SQL parametrized (and not formatted) when it's possible
String sql =
#"select ItemType
from ItemTable
where ItemID = #prm_ItemId";
// Wrap IDisposable into using
using (SqlCommand sql_cmd = new SqlCommand(sql, sql_conn)) {
// I don't know ItemID's type that's why I've put AddWithValue
sql_cmd.Parameters.AddWithValue("#prm_ItemId", item_id);
// Wrap IDisposable into using
using (SqlDataReader rdr = sql_cmd.ExecuteReader()) {
// rdr.HasRows is redundant - rdr.Read() returns true if record has been read
if (rdr.Read()) {
String key = Convert.ToString(rdr.GetValue(0));
// Put break point here: what is the "key" value?
item_type = TypesMap[key];
}
}
}
}
Edit: as Luke has mentioned in the comment, the cause of the error was that key comparison is expected to be case insensitive, so the amendment is to explain .Net how to compare keys:
var TypesMap = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
...
TypesMap.Add("aBc", "xyz");
String test = TypesMap["Abc"]; // return "xyz"; notice "aBc" and "Abc"
As Dmitry pointed out the 'given key not found...' is not a DB thing but a dictionary thing.
Below I've added a simple check to ensure the key is in the dictionary - if it is then we can assigned to item_type.
Also, if HasRows() isn't doing what you expect, try the following. It's the standard way I read from DB:
using (SqlDataReader results = sql_cmd.ExecuteReader(CommandBehavior.CloseConnection))
{
while (results.Read())
{
string Key = rdr["ItemType"].ToString();
if (TypesMap.ContainsKey(Key))
item_type = TypesMap[Key];
}
}
I Converted:
dto.Id = (int)record["Id"];
To:
dto.Id = (int)record[0];
This worked for me.
I want to execute a stored procedure inside a Web Method. It is a select statement in the stored procedure. I tried with the following code. However, the result not successful. The result should return 1 but it is always returning -1. Does anyone have any idea? Please help.
Here is the web service .asmx code:
public class retrieveLoan : System.Web.Services.WebService
{
string constring = "Data Source=DIT-NB1260382;Initial Catalog=Experiment;Integrated Security=True";
SqlConnection myConn;
[WebMethod(Description="Simple Example")]
public int GetResult(int id, int age)
{
Int32 numberofRecords = 0;
System.Data.DataSet workDS = new System.Data.DataSet();
SqlCommand objCommand = default(SqlCommand);
//Create a command object
objCommand = new SqlCommand();
//prepare the command for retreiving
objCommand.CommandType = System.Data.CommandType.StoredProcedure;
objCommand.CommandText = "myprocedure2";
//open the connection
myConn = new SqlConnection(constring);
myConn.Open();
objCommand.Connection = myConn;
try
{
numberofRecords = (Int32)objCommand.ExecuteScalar();
return numberofRecords;
}
catch (Exception)
{
return -1;
}
finally
{
myConn.Close();
}
}
}
and my store procedure:
ALTER PROCEDURE [dbo].[myprocedure2]
(
#puserid int,
#page int
)
AS
BEGIN
select * from userdet where userid = #puserid and age = #page
END
I believe that executing this stored procedure without parameters would return an exception.
First of all, for you to see the Exception, in the catch declaration, you should try and declare the Exception explicitly, like this:
try
{
numberofRecords = (Int32)objCommand.ExecuteScalar();
return numberofRecords;
}
catch (Exception ex)
{
//here you can enter into debug mode and see the exception "ex"
return -1;
}
finally
{
myConn.Close();
}
When you see the exception, you can quickly solve the problem.
Next, you should add the parameters as NULL into your stored procedure (so they can accept null values), OR, if you do not, you must add these parameter in C# code, and send them some values.
Also, i would like to point the fact that if you want to retrieve a COUNT, you should modify your stored procedure as following:
ALTER PROCEDURE [dbo].[myprocedure2] ( #puserid int, #page int )
AS
BEGIN
select COUNT(userid) from userdet where userid = #puserid and age = #page
END
Hope this solves your issues here.
You're not providing a lot of info, so hard to answer, but here's a way forward:
Change catch (Exception) into catch (Exception ex), then see what that exception contains, either by returning it, or by analyzing it in debug mode.
If you publish your project in debug mode, you can connect to it and debug it using Tools > Attach to Process and connect to the process called w3wp.exe (if there are more than one of them, look for the one with the correct version of .Net under the Type-column).
Your query is "select * from userdet". What ExecuteScalar() does is pick the first cell value. Now you are type casting this to int. if your first cell value is a string type or some other type. you will definitely receive a error. And that will return -1. Please define the column name in your select query or count like this "select count(*) from userdet". Check ur query.
I am trying to retrieve list of records from one table , and write to another table. I've used a simple query to retrieve the values to SqlDataReader,then load them to a DataTable. Using the DataTableReader , I am going through the entire data set which is Saved in DataTable. The problem is, while reading each and every record I am trying to insert those values to another table using a Stored Procedure.But it only insert the first row of values,and for the second row onward giving some Exception saying."procedure or function has too many arguments specified".
string ConStr = ConfigurationManager.ConnectionStrings["ConString"].ConnectionString;
SqlConnection NewCon = new SqlConnection(ConStr);
NewCon.Open();
SqlCommand NewCmd3 = NewCon.CreateCommand();
NewCmd3.CommandType = CommandType.Text;
NewCmd3.CommandText ="select * from dbo.Request_List where group_no ='" +group_no+ "'";
NewCon.Close();
NewCon.Open();
SqlDataReader dr = (SqlDataReader)NewCmd3.ExecuteReader();
DataTable dt = new DataTable();
dt.Load(dr);
DataTableReader reader = new DataTableReader(dt);
NewCmd.Dispose();
NewCon.Close();
NewCon.Open();
SqlCommand NewCmdGrpReqSer = NewCon.CreateCommand();
NewCmdGrpReqSer.CommandType = CommandType.StoredProcedure;
NewCmdGrpReqSer.CommandText = "Voucher_Request_Connection";
if (reader.HasRows)
{
int request_no = 0;
while (reader.Read())
{
request_no = (int)reader["request_no"];
NewCmdGrpReqSer.Parameters.Add("#serial_no", serial_no);
NewCmdGrpReqSer.Parameters.Add("#request_no", request_no);
try
{
NewCmdGrpReqSer.ExecuteNonQuery();
MessageBox.Show("Connection Updated");//just to check the status.tempory
}
catch (Exception xcep)
{
MessageBox.Show(xcep.Message);
}
MessageBox.Show(request_no.ToString());//
}
NewCmdGrpReqSer.Dispose();
NewCon.Close();
}
Any Solutions ?
As #Sparky suggests, the problem is that you continue to add parameters to the insertion command. There are several other ways in which the code could be improved, however. These improvements would remove the need to clear the parameters and would help to make sure you don't leave disposable resources undisposed.
First - use the using statement for your disposable objects. This removes the need for the explicit Close (btw, only one of Close/Dispose is needed for the connection as I believe Dispose calls Close). Second, simply create a new command for each insertion. This will prevent complex logic around resetting the parameters and, possibly, handling error states for the command. Third, check the results of the insertion to make sure it succeeds. Fourth, explicitly catch a SqlException - you don't want to accidentally hide unexpected errors in your code. If it's necessary to make sure all exceptions don't bubble up, consider using multiple exception handlers and "doing the right thing" for each case - say logging with different error levels or categories, aborting the entire operation rather than just this insert, etc. Lastly, I would use better variable names. In particular, avoid appending numeric identifiers to generic variable names. This makes the code harder to understand, both for others and for yourself after you've let the code sit for awhile.
Here's my version. Note there are several other things that I might do such as make the string literals into appropriately named constants. Introduce a strongly-typed wrapper around the ConfigurationManager object to make testing easier. Remove the underscores from the variable names and use camelCase instead. Though those are more stylistic in nature, you might want to consider them as well.
var connectionString = ConfigurationManager.ConnectionStrings["ConString"].ConnectionString;
using (var newConnection = new SqlConnection(connectionString))
{
newConnection.Open();
using (var selectCommand = newConnection.CreateCommand())
{
selectCommand.CommandType = CommandType.Text;
select.CommandText ="select request_no from dbo.Request_List where group_no = #groupNumber";
selectCommand.Parameters.AddWithValue("groupNumber", group_no);
using (dataReader = (SqlDataReader)newCommand.ExecuteReader())
{
while (reader.HasRows && reader.Read())
{
using (var insertCommand = newConnection.CreateCommand())
{
insertCommand.CommandType = CommandType.StoredProcedure;
insertCommand.CommandText = "Voucher_Request_Connection";
var request_no = (int)reader["request_no"];
insertCommand.Parameters.Add("#serial_no", serial_no);
insertCommand.Parameters.Add("#request_no", request_no);
try
{
if (insertCommand.ExecuteNonQuery() == 1)
{
MessageBox.Show("Connection Updated");//just to check the status.tempory
}
else
{
MessageBox.Show("Connection was not updated " + request_no);
}
}
catch (SqlException xcep)
{
MessageBox.Show(xcep.Message);
}
MessageBox.Show(request_no.ToString());//
}
}
}
}
}
Try clearing your parameters each time...
while (reader.Read())
{
request_no = (int)reader["request_no"];
// Add this line
NewCmdGrpReqSer.Parameters.Clear();
NewCmdGrpReqSer.Parameters.Add("#serial_no", serial_no);
NewCmdGrpReqSer.Parameters.Add("#request_no", request_no);
try
{
I have a datatable which gives me the columnname,datatype of the column and the maximum character length. returntype should be a list and how can i link the column name and the datatype in just one list. Please Help.
try
{
List<string> tablelist = new List<string>();
DataTable dt;
conn.Open();
SqlCommand cmd = new SqlCommand("SELECT column_name,data_type,character_maximum_length FROM "+ dbPropertiesobj.DBName +".information_schema.columns WHERE table_name = '"+ TableName+"' ", conn);
SqlDataAdapter sqlda = new SqlDataAdapter(cmd);
dt = new DataTable();
sqlda.Fill(dt);
for (int i = 0; i < dt.Rows.Count; i++)
{
string dtrow = dt.Rows[i].ItemArray[0].ToString(); //gets only the columnname
tablelist.Add(dtrow);
}
return tablelist; // need the datatype and maximum character length along with the name.
}
catch (Exception ex)
{
return null;
}
You've got a lot of stuff going on with this code. I rewrote it completely, this is not the shortest code or even the best way to do it, but it is written to be illustrative of areas to learn more or work on.
Study up on exception handling especially, when to use it and what to use it for. Never write try blocks that swallow errors. What were you planning on doing if this method returned null? Was the table name bad? Did the connection fail? A user may or may not could fix the error, but you swallowed it. Now no one knows what it was.
Even worse possibly no one knows an error even occurred here depending on what you did with the null. NEVER swallow errors like this. Applications should fail at the points where errors occur. Be careful about returning nulls like this as well. When you return nulls and then do something else the application may fail somewhere later instead and the original error is now much harder to find and fix. You may occasionally return nulls in this style when writing frameworks. For ordinary applications it is usually not needed and almost never a good idea.
Good quality production code typically contains very little exception handling because you should use conditionals to handle anything you can anticipate. Anything you cannot anticipate you usually also cannot handle. You may have a lot of try...finally blocks that exist to clean up resources, but an application should contain very few actual try..catch blacks. Normally you let errors propagate back up the call stack to a final handler that notifies a user before the app shuts down.
The following is still not the best code you could write. I kept it as close to your original as was reasonable and eliminated some shortcuts to make it clearer. Study the differences and go from there
public class SomeClass
{
//Use parameters rather than accessing module level properties
private IList<ColumnInformation> GetColumnInformationForTable(string dbName, string tableName)
{
// Favor object oriented styles and meaningful names. Your method does not return a list of tables
// it returns a list of column meta data
List<ColumnInformation> columnInformations = new List<ColumnInformation>();
// Avoid SQL conncatenation if at all possible. NEVER concatenate where parameters into SQL commands and NEVER EVER with single quotes.
// Here table name requires concatenation but the select parameter TableName does not.
string selectCmdString = "SELECT column_name,data_type,character_maximum_length FROM " + dbName + ".information_schema.columns WHERE table_name = #TableName";
// Use parameters. Get everything ready first, don't open connections prematurely and only wrap error prone code in try blocks.
SqlCommand cmd = new SqlCommand(selectCmdString, conn);
SqlParameter tableNameParameter = new SqlParameter("#TableName", tableName);
cmd.Parameters.Add(tableNameParameter);
// Use a DataReader since you cannot modify this data anyway.
// This also shows an appropriate use of a try block to ensure a connection gets closed,
// but better yet, open your reader with the CommandBehavior set to close
// and get rid of this try block altogether
try
{
//Reconsider use of a module or global level connection. May be better to create a new here.
conn.Open();
SqlDataReader reader = cmd.ExecuteReader();
//Favor OOP styles rather than indexes and arrays and repeated calls to determine things like Rows.Count in a loop
while(reader.Read())
{
// Favor explicit member access rather than index acess.
//YOUR HOMEWORK! Study DataReader access and rewrite the code below to handle possible nulls in length field. Use a method based on evaluating conditionals, DO NOT use a method based on a try block.
ColumnInformation columnInformation = new ColumnInformation(reader["column_name"].ToString(), reader["data_type"].ToString(), (int)reader["character_maximum_length"].ToString());
columnInformations.Add(columnInformation);
}
reader.Close();
}
finally
{
// The only reason to use the try is to make sure the connection gets closed here. A better approach
// is to use the CommandBehavior.CloseConnection option and get rid of the try finally block completely.
// But NEVER just wrap a bunch of code in try blocks arbitrarily, swallow any errors and return a null.
conn.Close();
}
return columnInformations;
}
}
public class ColumnInformation
{
private string _columnName;
private string _dataType;
private int _columnLength;
public string ColumnName
{
get { return _columnName; }
}
public string DataType
{
get { return _dataType; }
}
public int ColumnLength
{
get { return _columnLength; }
}
public ColumnInformation(string columnName, string dataType, int columnLength)
{
_columnName = columnName;
_dataType = dataType;
_columnLength = columnLength;
}
}
Why not a List of 3-Tuple objects (ie Tuple with 3 values)? Not sure the exact syntax for declaring the List but each entry would be something like:
Tuple.Create(column_name, datatype, max_length)
I am exploring Silverlight (C#) and SQLServer as a next evolution for our current (slow) Access database. So far everything has been great, using DomainServices to retrieve the data I need. In our database we have a table (Supervisors) with Supervisor_ID, Supervisor_FirstName, Supervisor_LastName and many other fields.
What I want to do is recreate a function I use in my current database called EntityNameFirstLast(EntityID) which would take an integer. I could then retrieve the value of [Supervisor_FirstName] from [Supervisors] table where [Supervisor_ID] == EntityID using the following:
FirstName = DLookup("[Supervisor_FirstName]", "Supervisors", "[Supervisor_ID] = EntityID
I would do the same for lastname and combine the strings returning one string with First and last name.
How can I get just a single value from my database through my DomainService (or any way for that matter)? I understand that IQueryable GetSupervisorByID(Int SupID) will return the entire row that I need, but how can I get a specific field from that row?
I am also aware that I can set the DomainDataSource in my XAML and then bind to the data I want, but I am curious if what I asked above is doable or not.
There are number of ways you can accomplish your requirement if what you need is a single value from MS-SQL server:
1.Use a Query to do the concatenation and then use its output in your code
Select Supervisor_FirstName + ' ' + Supervisor_LastName as Supervisor_FullName From Supervisors Where Supervisor_ID = EntityID
Now you can get the above query to execute through a SqlCommand and get the part thats interesting to you
private string GetSupervisorFullName(string entityID, string connectionString) {
string query = "Select Supervisor_FirstName + ' ' + Supervisor_LastName as Supervisor_FullName From Supervisors Where Supervisor_ID = #EntityID";
string supervisorFullname = "";
using(SqlConnection con = new SqlConnection(connectionString)) {
SqlCommand cmdSupervisorFullname = new SqlCommand();
cmdSupervisorFullname.Connection = con;
cmdSupervisorFullname.CommandText = query;
cmdSupervisorFullname.CommandType = CommandType.Text;
SqlParameter paraEntityID = new SqlParameter();
paraEntityID.ParameterName = "#EntityID";
paraEntityID.SqlDbType = SqlDbType.NVarChar;
paraEntityID.Direction = ParameterDirection.Input;
paraEntityID.Value = entityID;
cmdSupervisorFullname.Parameters.Add(paraEntityID);
try {
con.Open();
supervisorFullname = (String) cmdSupervisorFullname.ExecuteScalar();
} catch(Exception ex) {
Console.WriteLine(ex.Message);
}
return supervisorFullname;
}
}
2.Second way would be create a Scalar function in the SQL for your requirement and then access that function using the same kind of method as mentioned above.
Then finally you would take the return value from your method GetSupervisorFullName and populate any control value of your choice.
Please do note that there are again other methods of doing the same with LINQtoSQL or with any other ORM tools. The above 2 methods are the basic way of accomplishing them.
Hope that helps.