I have a method (with WebMethod attribute), I define a transaction, in my method and in my transaction I call 2 stored procedures, first one is GetAllBook :
select *
from Book_TBL
where IsActive = 'True'
and second one is updateBook :
update Book_TBL
set IsActive = 'False'
where IsActive = 'True'
and my method :
public struct BOOK
{
public string BOOK_NAME;
public string BOOK_DESC;
}
[WebMethod]
public static List<BOOK> GetMyBooks()
{
using (TransactionScope _transactionScope = new TransactionScope(TransactionScopeOption.Required))
{
string _connString = "Data Source=.;Initial Catalog=BookStore;Integrated Security=True";
SqlConnection _conn = new SqlConnection(_connString);
_conn.Open();
SqlCommand _com = new SqlCommand();
_com.CommandType = System.Data.CommandType.StoredProcedure;
_com.CommandText = "GetAllBook";
_com.Connection = _conn;
SqlDataAdapter bookdataAdapter = new SqlDataAdapter(_com);
DataSet bookDS = new DataSet();
bookdataAdapter.Fill(bookDS, "Book_TBL");
List<BOOK> bookList = new List<BOOK>();
_conn.Close();
BOOK book;
foreach (DataRow dr in bookDS.Tables["Book_TBL"].Rows)
{
book = new BOOK();
book.BOOK_NAME = dr["book_name"].ToString();
book.BOOK_DESC = dr["book_desc"].ToString();
bookList.Add(book);
}
SqlCommand updateCommand= new SqlCommand();
_conn.Open();
updateCommand.CommandText = "updateBook";
updateCommand.CommandType = System.Data.CommandType.StoredProcedure;
updateCommand.Connection = _conn;
updateCommand.ExecuteNonQuery();
_conn.Close();
return bookList;
}
}
When I run the project myMethod gives me the list of books which have IsActive = True but it did not update my table! What is the problem?
You have to call TransactionScope.Complete, the equivalent of a commit. Without it, the using blocks disposes it and that's equivalent to a rollback.
I don't knoe what are you really want to do with a such deeply nested method but you should put everything in place first:
public class Book
{
public string Name { get; set; }
public string Description { get; set; }
}
public static List<Book> GetMyBooks()
{
var bookList = new List<Book>();
using (var transactionScope = new TransactionScope(TransactionScopeOption.Required))
{
const string connString = "Data Source=.;Initial Catalog=BookStore;Integrated Security=True";
using (var conn = new SqlConnection(connString))
{
using (var com = new SqlCommand())
{
com.CommandType = CommandType.StoredProcedure;
com.CommandText = "GetAllBook";
com.Connection = conn;
using (var bookdataAdapter = new SqlDataAdapter(com))
{
using (var bookDataSet = new DataSet())
{
bookdataAdapter.Fill(bookDataSet, "Book_TBL");
foreach (DataRow dr in bookDataSet.Tables["Book_TBL"].Rows)
{
bookList.Add(new Book
{
Name = dr["book_name"].ToString(),
Description = dr["book_desc"].ToString()
});
}
using (var updateCommand = new SqlCommand())
{
updateCommand.CommandText = "updateBook";
updateCommand.CommandType = CommandType.StoredProcedure;
updateCommand.Connection = conn;
updateCommand.ExecuteNonQuery();
}
}
}
}
}
transactionScope.Complete();
return bookList;
}
}
And as mentioned the other you have to manually commit the transaction.
You should be using the class SqlTransaction like this:
using (SqlConnection cn = new SqlConnection())
{
cn.Open();
using (SqlTransaction tr = cn.BeginTransaction())
{
//some code
tr.Commit();
}
}
Related
Why am I getting this error:
Connection state has not been intialized
when I'm using one method in other?
This is my DbConnectio.cs:
public class DbContext
{
public SqlConnection sqlconn = null;
public SqlConnection DbConnection
{
get { return sqlconn; }
set { value = sqlconn; }
}
public DbContext()
{
string cs = ConfigurationManager.ConnectionStrings["CTXDB"].ConnectionString;
sqlconn = new SqlConnection(cs);
}
}
web.config:
<add name="CTXDB"
connectionString="Data Source=Md;Initial Catalog=Md;User ID=sa;Password=123;MultipleActiveResultSets=true"
providerName="System.Data.SqlClient" />
This is my Repo.cs - here I'm implementing my business logic:
DbContext db = new DbContext();
public Employee FindEmpById(int key)
{
SqlConnection conn = db.DbConnection;
try
{
var employee = new Employee();
if (conn.State != System.Data.ConnectionState.Open)
{
conn.Open();
}
SqlCommand cmd = new SqlCommand("Sp_GetEmployeeById", conn);
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.AddWithValue("#EmpId", key);
SqlDataReader rdr = cmd.ExecuteReader();
if (rdr.HasRows == true)
{
while (rdr.Read())
{
employee.Emp_Id = Convert.ToInt32(rdr["Emp_Id"]);
employee.EmpName = rdr["EmpName"].ToString();
employee.Email = rdr["Email"].ToString();
employee.Psw = rdr["Psw"].ToString();
}
}
return employee;
}
catch (Exception)
{
throw;
}
finally
{
if (conn != null)
{
if (conn.State == ConnectionState.Open)
conn.Close();
conn.Dispose();
}
}
}
This FindEmpById I call in DeleteEmpById function
public void DeleteEmpById(int Key)
{
SqlConnection Con = db.DbConnection;
var x = FindEmpById(Key);
if (x != null)
{
if (Con.State != ConnectionState.Open)
{
Con.Open();
}
SqlCommand cmd = new SqlCommand("sp_DeleteById", Con);
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.AddWithValue("#EmpId", Key);
cmd.ExecuteNonQuery();
}
}
FindEmpById disposes the connection conn.Dispose();. So, when you try to use it afterwards, it is not valid any more.
Don't try to reuse a connection. Create a new connection each time you need one. Internally the physical connections are pooled automatically, i.e., the same physical connection will be reused when possible. Creating a new connection with new SqlConnection is lightweight.
Instead of doing
SqlConnection conn = db.DbConnection; // WRONG!
// All your try catch finally and testing for conn.State --- WRONG!
do
// OK!
using (SqlConnection conn = db.CreateConnection()) {
conn.Open();
...
} // Automatically closed and disposed here.
Where CreateConnection creates a new SqlConnection at each call. This is also much easier and straight forward. Change your DbContext class to
public class DbContext
{
private static readonly string _connectionString;
static DbContext()
{
_connectionString = ConfigurationManager.ConnectionStrings["CTXDB"].ConnectionString;
}
public SqlConnection CreateConnection()
{
return new SqlConnection(_connectionString);
}
}
Your overhauled FindEmpById method becomes
public Employee FindEmpById(int key)
{
using (SqlConnection conn = db.CreateConnection())
using (SqlCommand cmd = new SqlCommand("Sp_GetEmployeeById", conn)) {
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.AddWithValue("#EmpId", key);
var employee = new Employee();
conn.Open();
using (SqlDataReader rdr = cmd.ExecuteReader()) {
if (rdr.Read()) {
employee.Emp_Id = Convert.ToInt32(rdr["Emp_Id"]);
employee.EmpName = rdr["EmpName"].ToString();
employee.Email = rdr["Email"].ToString();
employee.Psw = rdr["Psw"].ToString();
}
}
return employee;
}
}
Btw: You don't need to call FindEmpById(Key) before deleting. Just delete. It is not an error to delete 0 records in SQL.
public void DeleteEmpById(int Key)
{
using (SqlConnection conn = db.CreateConnection())
using (SqlCommand cmd = new SqlCommand("sp_DeleteById", conn)) {
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.AddWithValue("#EmpId", Key);
conn.Open();
cmd.ExecuteNonQuery();
}
}
I get that we can use using to have multiple commands in a sqlconnection.
Like this:
using (SqlConnection connection = new SqlConnection(connectionString))
{
connection.Open();
using (SqlCommand command1 = new SqlCommand(commandText1, connection))
{
}
using (SqlCommand command2 = new SqlCommand(commandText2, connection))
{
}
// etc
}
However, what if the using is in a method that returns a reader cast?
Like this:
public IEnumerable<LocationInfo> GetData()
{
using (var connection = new SqlConnection(ConfigurationManager.ConnectionStrings["Database"].ConnectionString))
{
//connection.Close();
connection.Open();
using (SqlCommand command = new SqlCommand(#"SELECT .... ", connection))
{
command.Notification = null;
SqlDependency dependency = new SqlDependency(command);
dependency.OnChange += new OnChangeEventHandler(dependency_OnChange);
if (connection.State == ConnectionState.Closed)
{ connection.Open(); }
using (var reader = command.ExecuteReader())
{
return reader.Cast<IDataRecord>().Select(x => new LocationInfo()
{
Names = x.GetString(2),
Values = Math.Round(x.GetDouble(7), 2).ToString("#,##0.00"),
ValuesDouble = x.GetDouble(7),
Values2 = Math.Round(x.GetDecimal(9), 2).ToString("#,##0.00"),
ValuesDouble2 = x.GetDecimal(9),
truckDelivery=x.GetDecimal(3),
truckIdle = x.GetDecimal(4),
truckRepair = x.GetDecimal(5),
truckReady = x.GetDecimal(6),
presentEmp=x.GetInt32(11),
absentEmp = x.GetInt32(12),
ondutyEmp = x.GetInt32(13),
}).ToList();
}
/* I tried this but it just got ignored
using (var reader2 = command.ExecuteReader())
{
reader2.NextResult();
return reader2.Cast<IDataRecord>().Select(x => new LocationInfo()
{
SumVol = x.GetString(0)
}).ToList();
}*/
}
}
}
Help me please. My second using keeps getting ignored and don't assume that I know anything because I'm new to this. Thank you in advance.
You need to Read the record pointed by the SqlDataReader obtained by the ExecuteReader. Accumulate your LocationInfo in a List and return them when you have finished to loop over the reader.
public IEnumerable<LocationInfo> GetData()
{
List<LocationInfo> locations = new List<LocationInfo>();
using (var connection = new SqlConnection(ConfigurationManager.ConnectionStrings["Database"].ConnectionString))
using (SqlCommand command = new SqlCommand(#"SELECT .... ", connection))
{
connection.Open();
using (var reader = command.ExecuteReader())
{
while(reader.Read())
{
LocationInfo x = new LocationInfo()
{
Names = x.GetString(2),
Values = Math.Round(x.GetDouble(7), 2).ToString("#,##0.00"),
ValuesDouble = x.GetDouble(7),
Values2 = Math.Round(x.GetDecimal(9), 2).ToString("#,##0.00"),
ValuesDouble2 = x.GetDecimal(9),
truckDelivery=x.GetDecimal(3),
truckIdle = x.GetDecimal(4),
truckRepair = x.GetDecimal(5),
truckReady = x.GetDecimal(6),
presentEmp=x.GetInt32(11),
absentEmp = x.GetInt32(12),
ondutyEmp = x.GetInt32(13),
};
locations.Add(x);
}
}
}
return locations;
}
OK so here is what you can do in your situation to merge results from two result sets into one object.
public IEnumerable<LocationInfo> GetData()
{
List<LocationInfo> locations = new List<LocationInfo>();
using (var connection = new SqlConnection(ConfigurationManager.ConnectionStrings["Database"].ConnectionString))
using (SqlCommand command = new SqlCommand(#"SELECT .... ", connection))
{
connection.Open();
using (var reader = command.ExecuteReader())
{
LocationInfo x = new LocationInfo();
while(reader.Read())
{
{
x.Names = reader.GetString(2),
x.Values=Math.Round(reader.GetDouble(7),2).ToString("#,##0.00"),
x.ValuesDouble = reader.GetDouble(7),
Values2 = Math.Round(reader.GetDecimal(9), 2).ToString("#,##0.00"),
x.ValuesDouble2 = reader.GetDecimal(9),
x.truckDelivery=reader.GetDecimal(3),
x.truckIdle = reader.GetDecimal(4),
x.truckRepair = reader.GetDecimal(5),
x.truckReady = reader.GetDecimal(6),
x.presentEmp=reader.GetInt32(11),
x.absentEmp = reader.GetInt32(12),
x.ondutyEmp = reader.GetInt32(13),
};
}
if(reader.NextResult())
{
while (reader.Read())
{
x.SumVol=reader.GetString(0);
}
}
locations.Add(x);
}
}
return locations;
}
I'm trying to get two column details like this way:
public string GetData()
{
using (SqlConnection con = new SqlConnection(this.Connection))
{
con.Open();
SqlCommand command = new SqlCommand("Select TITTLE,VALUE from T_PROJECTS ", con);
// int result = command.ExecuteNonQuery();
using (SqlDataReader reader = command.ExecuteReader())
{
reader.Read();
return reader["TITTLE,VALUE"]?.ToString();
}
}
}
How can I do this?
You need to have a custom class of columns that you want to retrieve, for example,
public class Project
{
public int Title { get; set; }
public string Value { get; set; }
}
and then like this,
public Project GetData()
{
using (SqlConnection con = new SqlConnection(this.Connection))
{
con.Open();
SqlCommand command = new SqlCommand("Select TITTLE,VALUE from T_PROJECTS ", con);
Project proObj = new Project();
using (SqlDataReader reader = command.ExecuteReader())
{
reader.Read();
proObj.Title = reader["TITTLE"].ToString();
proObj.Value = reader["VALUE"].ToString();
}
}
return proObj;
}
You could also return a Tuple although I feel a custom class is a much better solution. -> https://msdn.microsoft.com/en-us/library/system.tuple(v=vs.110).aspx
public IEnumerable<Tuple<string,string>> GetData()
{
List<Tuple<string,string> results = new List<Tuple<string,string>>();
using (SqlConnection con = new SqlConnection(this.Connection))
{
con.Open();
SqlCommand command = new SqlCommand("Select TITTLE,VALUE from T_PROJECTS ", con);
using (SqlDataReader reader = command.ExecuteReader())
{
while(reader.Read())
results.add(new Tuple<string,string>(reader["TITLE"].ToString(),reader["VALUE"].ToString()));
}
return results;
}
}
I have written the following code to extract all records from the table in the SQL server. There is no error in the code but when I run it in fiddler, I am getting null objects.
[HttpGet]
public List<Student> Get()
{
SqlDataReader reader = null;
SqlConnection myConnection = new SqlConnection();
myConnection.ConnectionString = #"Data Source=PALLAVI-PC\SQLEXPRESS;Initial Catalog=StudentDB;Integrated Security=True;MultipleActiveResultSets=True;";
SqlCommand sqlCmd = new SqlCommand();
sqlCmd.CommandType = CommandType.Text;
sqlCmd.CommandText = "Select * from Tbl_Students;";
sqlCmd.Connection = myConnection;
myConnection.Open();
reader = sqlCmd.ExecuteReader();
int rowCount = 0;
while (reader.Read())
{
rowCount++;
}
Student[] student = new Student[rowCount];
for(int i=0;i<rowCount;i++)
{
student[i] = new Student();
}
int j = 0;
while (reader.Read())
{
// student[j] = new Student();
student[j].Roll_Number = Convert.ToInt32(reader.GetValue(0));
student[j].FirstName = reader.GetValue(1).ToString();
student[j].LastName = reader.GetValue(2).ToString();
student[j].Class = Convert.ToInt32(reader.GetValue(3));
student[j].Gender = reader.GetValue(4).ToString();
j++;
}
return student.ToList();
myConnection.Close();
}
I have 5 records in the table. I am able to get 5 json objects but without the content.
Attaching the image from Fiddler:
Student.cs
namespace WebAPIDemo.Models
{
public class Student
{
public int Roll_Number { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public int Class { get; set; }
public string Gender { get; set; }
}
}
Try this, without taking count of records. I'm not using the array, I have created new instance of student for each record.
public static List<Student> GetData()
{
List<Student> lstStudent = new List<Student>();
using (SqlConnection con = new SqlConnection(#"Data Source=PALLAVI-PC\SQLEXPRESS;Initial Catalog=StudentDB;Integrated Security=True;MultipleActiveResultSets=True;"))
{
using (SqlCommand cmd = new SqlCommand("Select * from Tbl_Students;", con))
{
cmd.CommandType = CommandType.Text;
if (con.State == ConnectionState.Closed)
con.Open();
SqlDataReader reader = cmd.ExecuteReader();
while(reader.Read())
{
Student student =new Student();
student.Roll_Number = Convert.ToInt32(reader.GetValue(0));
student.FirstName = reader.GetValue(1).ToString();
student.LastName = reader.GetValue(2).ToString();
student.Class = Convert.ToInt32(reader.GetValue(3));
student.Gender = reader.GetValue(4).ToString();
lstStudent.Add(student);
}
}
}
return lstStudent;
}
your first while(reader.Read()) is the actual reader object with data. DataReader allows forward only read only access to a data row.The next while loop won't even execute because the Read() is already completed.So you're getting an array of 5 Student objects that are initialized with default values.
Instead of initializing an array of Student populate instances and add to a List<Student> inside the while loop.
this is how I try to print something worthwhile for my site using my class, When I try to write my class will not find it at all.
I can not find my worth to return something worthwhile for my user.
the problem is that it can not find at all class.
My class:
public class AbonnementsId
{
public int indhold { get; set; }
}
public AbonnementsId HentAbonnementsId()
{
AbonnementsId AbonnementsidReturn = new AbonnementsId();
AbonnementsidReturn.indhold = 0;
SqlConnection conn1 = new SqlConnection(ConfigurationManager.ConnectionStrings["ConnectionString"].ToString());
SqlCommand cmd1 = conn1.CreateCommand();
cmd1.Connection = conn1;
int brugerid = Convert.ToInt32(HttpContext.Current.Session["id"]);
cmd1.CommandText = #"SELECT abonnementsid from brugere WHERE id = #id";
cmd1.Parameters.AddWithValue("#id", brugerid);
conn1.Open();
SqlDataReader readerBrugerA = cmd1.ExecuteReader();
if (readerBrugerA.Read())
{
AbonnementsidReturn.indhold = Convert.ToInt32(readerBrugerA["abonnementsid"]);
}
conn1.Close();
return AbonnementsidReturn;
}
Here is how I write my class out when I need it for my content.
if (Session["AbonnementsId"] != null)
{
_subscriptionId = long.Parse(Session["AbonnementsId"].ToString());
}
else
{
//when I need to print my class do I like it here
_subscriptionId = AbonnementsidReturn.indhold();
}
I'm totally shooting in the dark here:
public static class AbonnementsId
{
public static int GetAbonnementsId()
{
int abonnementsid;
string connectionString = ConfigurationManager.ConnectionStrings["ConnectionString"].ToString();
using(var conn = new SqlConnection(connectionString))
{
int brugerid = Convert.ToInt32(HttpContext.Current.Session["id"]);
SqlCommand cmd1 = conn.CreateCommand();
cmd1.Connection = conn;
cmd1.CommandText = #"SELECT abonnementsid from brugere WHERE id = #id";
cmd1.Parameters.AddWithValue("#id", brugerid);
conn.Open();
SqlDataReader readerBrugerA = cmd1.ExecuteReader();
if (readerBrugerA.Read())
abonnementsid = Convert.ToInt32(readerBrugerA["abonnementsid"]);
conn.Close();
}
return abonnementsid;
}
}
You can then call this in your code:
if (Session["AbonnementsId"] != null)
{
_subscriptionId = long.Parse(Session["AbonnementsId"].ToString());
}
else
{
//when I need to print my class do I like it here
_subscriptionId = AbonnementsId.GetAbonnementsId();
}