I have got a couple of questions. I am making gym management system to submit in my university. I implemented a service based database by reading the tutorials at www.homeandlearn.co.uk. The problem is I can not update it
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Gym_Management_System
{
class GMSDConnectionClass
{
public string sql_string;
public string strCon;
public string Sql
{
set { sql_string = value; }
}
public string connection_string
{
set { strCon = value; }
}
public System.Data.DataSet GetConnection
{
get { return MyDataSet(); }
}
public System.Data.DataSet MyDataSet()
{
System.Data.SqlClient.SqlConnection con = new
System.Data.SqlClient.SqlConnection(strCon);
con.Open();
System.Data.SqlClient.SqlDataAdapter da_1 = new
System.Data.SqlClient.SqlDataAdapter(sql_string, con);
System.Data.DataSet dat_set = new System.Data.DataSet();
da_1.Fill(dat_set, "Table_data_1");
con.Close();
return dat_set;
}
public void UpdateDatabase(System.Data.DataSet ds)
{
System.Data.SqlClient.SqlCommandBuilder cb = new
System.Data.SqlClient.SqlCommandBuilder(da_1);
cb.DataAdapter.Update(ds.Tables[0]);
}
}
}
The problem is that I can not access da_1 in UpdateDatabase() method.
What's wrong there? I have tried couple of things but I couldn't get it. It may be a simple problem but I am totally new to database and this stuff.
Note:
I have to submit this project on 23rd January 2014.
Variables cannot be accessed outside of their declaring scope. There are various scoping rules.. the one you're most concerned with here is method-scope. This might help explain:
public System.Data.DataSet MyDataSet()
{
// da_1 is delcared in this method.. it is only available here
...
System.Data.SqlClient.SqlDataAdapter da_1 = new
System.Data.SqlClient.SqlDataAdapter(sql_string, con);
...
}
public void UpdateDatabase(System.Data.DataSet ds)
{
// not available here
}
In order to access it across methods within a class.. you must declare it at class level:
class GMSDConnectionClass
{
System.Data.SqlClient.SqlDataAdapter da_1;
Then you can assign it like this in your other method:
da_1 = new System.Data.SqlClient.SqlDataAdapter(sql_string, con);
Related
I'm trying to create a Data Framework in the form of a .dll so that I can reference it when creating new projects, as opposed to reinventing the wheel with each project I create.
I have an app.config in which I store my SQL connections, a class that uses the app.config to build my SQL ConnectionString (ConnectionStrings.cs) and a Logic class (Logic.cs) that'll build whatever objects I require from the SQL Server.
Here's the classes in the .dll:
ConnectionStrings.cs:
using System.Configuration;
using System.Data.SqlClient;
namespace DataFramework
{
public static class ConnectionStrings
{
static string _liveConnectionString = ConfigurationManager.ConnectionStrings["LiveDataSource"].ConnectionString;
static string _liveMISConnectionString = ConfigurationManager.ConnectionStrings["LiveDataSource_MIS"].ConnectionString;
static string _devConnectionString = ConfigurationManager.ConnectionStrings["DevDataSource"].ConnectionString;
static string _devMISConnectionString = ConfigurationManager.ConnectionStrings["DevDataSource_MIS"].ConnectionString;
public static SqlConnection CreateLiveConnection
{
get { return new SqlConnection(_liveConnectionString); }
}
public static SqlConnection CreateLiveMISConnection
{
get { return new SqlConnection(_liveMISConnectionString); }
}
public static SqlConnection CreateDevConnection
{
get { return new SqlConnection(_devConnectionString); }
}
public static SqlConnection CreateDevMISConnection
{
get { return new SqlConnection(_devMISConnectionString); }
}
}
}
Logic.cs:
using System;
using System.Threading.Tasks;
using System.Data;
using System.Data.SqlClient;
namespace DataFramework
{
public class Logic
{
SqlConnection liveConnection = ConnectionStrings.CreateLiveMISConnection;
SqlConnection devMISConnection = ConnectionStrings.CreateDevMISConnection;
public bool IsConnecting { get; set; }
public string ConnectionMessage { get; set; }
public async Task<DataTable> ResultDataTable(bool connectToLive, string commandText, CommandType commandType)
{
DataTable dt = new DataTable();
using (SqlCommand command = new SqlCommand())
{
try
{
command.CommandType = commandType;
command.CommandTimeout = 360000000;
switch (connectToLive)
{
case true:
command.CommandText = commandText;
command.Connection = liveConnection;
if (liveConnection.State == ConnectionState.Connecting)
{
IsConnecting = true;
ConnectionMessage = "Connecting to Data Source...";
}
if (liveConnection.State != ConnectionState.Closed)
liveConnection.Close();
if (liveConnection.State != ConnectionState.Open)
{
liveConnection.Open();
IsConnecting = false;
ConnectionMessage = "";
}
break;
case false:
command.CommandType = commandType;
command.CommandText = "";
command.Connection = devMISConnection;
if (devMISConnection.State == ConnectionState.Connecting)
{
IsConnecting = true;
ConnectionMessage = commandText;
}
if (devMISConnection.State != ConnectionState.Closed)
devMISConnection.Close();
if (devMISConnection.State != ConnectionState.Open)
{
devMISConnection.Open();
IsConnecting = false;
ConnectionMessage = "";
}
break;
}
using (SqlDataReader reader = await command.ExecuteReaderAsync())
{
dt.Load(reader);
}
}
catch (Exception ex)
{
System.Windows.Forms.MessageBox.Show(ex.Message, "An Error Has Occured", System.Windows.Forms.MessageBoxButtons.OK, System.Windows.Forms.MessageBoxIcon.Error);
}
finally
{
if (devMISConnection.State != ConnectionState.Closed)
devMISConnection.Close();
if (liveConnection.State != ConnectionState.Closed)
liveConnection.Close();
}
}
return dt;
}
}
}
I include this dll as a reference in the app that I'm writing:
using System.Data;
using System.Threading.Tasks;
using System.Windows.Forms;
using DataFramework;
namespace WindowsFormsApplication1
{
public partial class Form1 : Form
{
DataTable dt = new DataTable();
DataFramework.Logic logic = new Logic();
public Form1()
{
InitializeComponent();
}
private async void Form1_Load(object sender, EventArgs e)
{
dt = await Task.Run(() => logic.ResultDataTable(true, "SELECT * FROM MIS.dbo.ETL_Table", CommandType.StoredProcedure));
}
}
}
The code throws the exception here:
SqlConnection liveConnection = ConnectionStrings.CreateLiveMISConnection;
So why, when I'm initializing the class, do I get this issue?
When you reference a DLL (or project) from another project, the app.config from the top most project is used. So, if you have your DataFramework being called from your WinformsApp, then your WinformsApp needs to have the right config settings in it. By default, it will ignore any app.config in the DataFramework. A bit frustrating at times! Copy your settings from your DataFramework app.config in to the WinformsApp app.config and it will work.
Another unrelated observation is that you have the following:
"SELECT * FROM MIS.dbo.ETL_Table", CommandType.StoredProcedure
The command type should be text and not a stored procedure.
This is my current pattern
private void ReadData(string connString, string cmdString)
{
using (OracleConnection conn = new OracleConnection(connString))
{
conn.Open();
OracleCommand cmd = new OracleCommand(cmdString, conn);
OracleDataReader reader = cmd.ExecuteReader();
//some long operation using reader
}
}
In the above case, the connection remains open while the long operation is going on. Is there a way I could close the connection but still preserve the reader. Is that going to be advantageous?
If by long operations you mean to say that you need to do extra operations on the database (like update/insert/delete), then you cannot close the connection.
If you want to read the data and do some calculation based on it, then you should modify you pattern to: 1. read all the data, 2. close the connection, 3. do long operation on the data.
using System;
using System.Collections.Generic;
using Oracle.DataAccess.Client;
namespace Utils
{
class Test
{
private class Class
{
public string FirstProperty { get; set; }
public string SecondProperty { get; set; }
}
private void ReadData(string connString, string cmdString)
{
List<Class> data = new List<Class>();
using (OracleConnection conn = new OracleConnection() { ConnectionString = connString })
using (OracleCommand objCmd = new OracleCommand()
{
Connection = conn,
CommandText = cmdString
})
{
try
{
conn.Open();
}
catch (OracleException)
{
OracleConnection.ClearPool(conn);
conn.Open();
}
using (OracleDataReader dataReader = objCmd.ExecuteReader())
{
while (dataReader.Read())
data.Add(new Class()
{
FirstProperty = dataReader.GetString(0),
SecondProperty = dataReader.GetString(1)
});
}
conn.Close();
}
//some long operation using data
}
}
}
Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 9 years ago.
Improve this question
I keep getting this error while trying to call a DataAccess layer in my C# windows form application.
In my design, I have used an interface and a class.
The inteface called iDataAccess, and the code inside it looks like this:
IDataAccess.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Data;
public interface IDataAccess
{
object GetScalar(string sql);
DataTable GetDataTable(string sql);
int InsOrUpdOrDel(string sql);
}
The class called DataAccess and the code inside it looks something like this:
DataAccess.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Data.SqlClient;
using System.Data;
using System.Configuration;
public class DataAccess : IDataAccess
{
public static string CONNSTR = ConfigurationManager.ConnectionStrings["SqlDbConnect"].ConnectionString;
public DataAccess()
{
}
public System.Data.DataTable GetDataTable(string sql)
{
DataTable dt = new DataTable();
SqlConnection conn = new SqlConnection(CONNSTR);
try
{
conn.Open();
SqlDataAdapter da = new SqlDataAdapter(sql, conn);
da.Fill(dt);
}
catch (Exception)
{
throw;
}
finally
{
conn.Close();
}
return dt;
}
}
Now, in form.cs code I want to create an object of the mentioned class. For this reason, I have tried two different ways, but none of them worked for me, as shown below:
IDataAccess _idataAccess = new DataAccess(); // I get instance failure error
IDataAccess _idataAccess = null; // Error: Object reference not set to instance of an object
can anyone give me an idea about this problem, please?
You dont have SqlDbConnect
ConfigurationManager.ConnectionStrings["SqlDbConnect"].ConnectionString;
in your configurations.
make sure that this line returns what you expect it to.
And your code can't compile unless you implement these methods in your dataaccess class
public object GetScalar(string sql)
{
throw new NotImplementedException();
}
public int InsOrUpdOrDel(string sql)
{
throw new NotImplementedException();
}
IDataAccess _idataAccess = new DataAccess();
will work but you need to implement
object GetScalar(string sql);
and
int InsOrUpdOrDel(string sql);
in
DataAccess
as follows
public class DataAccess : IDataAccess
{
public static string CONNSTR = ConfigurationManager.ConnectionStrings["SqlDbConnect"].ConnectionString;
public DataAccess()
{
}
object GetScalar(string sql)
{
return new object();
}
int InsOrUpdOrDel(string sql)
{
return 1;
}
public System.Data.DataTable GetDataTable(string sql)
{
DataTable dt = new DataTable();
SqlConnection conn = new SqlConnection(CONNSTR);
try
{
conn.Open();
SqlDataAdapter da = new SqlDataAdapter(sql, conn);
da.Fill(dt);
}
catch (Exception)
{
throw;
}
finally
{
conn.Close();
}
return dt;
}
}
I'm having class Report and class Program, what I want to accomplish (no luck so far) is to send data from class Program and method SaveRep() to class Report method Save() and save it in this method.
I apologize if the question is badly formulated, I am really stuck at this, please help. Thanks
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Configuration;
using System.Data.SqlClient;
namespace Application
{
class Program
{
static void Main(string[] args)
{
//call method SaveRep
}
public void SaveRep(...)
{
int RepID = 1;
string Data1 = "SomeData1"
string Data2 = "SomeData2"
//This method should send the data above to method Save() in Report class
//data to this method will be provided from another method.
}
}
public class Report
{
private static int _repID;
public int RepID
{
get { return _repID; }
set { _repID = value; }
}
private static string _data1;
public string Data1
{
get { return _data1; }
set { _data1 = value; }
}
private static string __data2;
public string Data1
{
get { return _data2; }
set { _data2 = value; }
}
public void Save()
{
string strConnectionString = (#"server=(local)\sqlexpress;Integrated Security=True;database=DataBase");
SqlConnection connection = new SqlConnection(strConnectionString);
connection.Open();
// This method should save all data (RepID,Data1,Data2)
// to the DB, provided to her by SaveRep method from Program class.
// Properties are columns from a database
}
}
}
public void Save()
{
string yourConnString="Replace with Your Database ConnectionString";
using(SqlConnection connection = new SqlConnection(yourConnString))
{
string sqlStatement = "INSERT Table1(Data1) VALUES(#Data1)";
using(SqlCommand cmd = new SqlCommand(sqlStatement, connection))
{
cmd.CommandType = CommandType.Text;
cmd.Parameters.AddWithValue("#Data1",Data1);
//add more parameters as needed and update insert query
try
{
connection.Open();
cmd.ExecuteNonQuery();
}
catch(Exception ex)
{
//log error
}
}
}
}
Assuming your Data1 is the name of your Column name. Update the sqlStatement to have your relevant columns.
Now in SaveRep, you can call it like
public void SaveRep()
{
Report objReport=new Report();
objReport.Data1="Something somethinng";
objReport.Save();
}
string connectionString = ....; //adjust your connection string
string queryString = "INSERT INTO ....;"; //Adjust your query
using (SqlConnection connection = new SqlConnection(
connectionString))
{
using( SqlCommand command = new SqlCommand(
queryString, connection))
{
connection.Open();
command .ExecuteNonQuery();
}
}
One tiny thing that the previous speakers have not noticed, is that your Report class is severely broken. Why did you use static keywords on the variables? Do not do that! Well, unless you know what it does, and here I have the feeling you don't. Update your Report class to look like this:
public class Report
{
public int RepID {get;set;}
public string Data1 {get;set;}
public string Data2 {get;set;}
public void Save()
{
// what others said
}
}
'static' in your original code makes all te variables global/shared. That means that if you'd create 500 Report objects with new, they all would have the same one ID, Data1 and Data2. Any change to any single of those 500 objects would cause all the objects to change. But it's be misleading, the objects would not change. They would simply have the same data. Looking at the "ID" field, I think you'd rather want a separate objects with separate data for separate records..
I'd like to implement N-tier architecture in my WinForms applications to separate (just logically - in one project) business logic from data access, however I have some doubts about using transacion in BLL. All tutorials I've found in the Internet either are very simple implementations of that architecture (without transactions), or are too complex for my needs. Trying to find my own way, I've come to the point, where I don't know the best way to handle transactions of in BLL layer.
I'll try to use some simple example to illustrate the problem (all classes are in separate files):
//DTO - Data Transfer Objects
public class Item
{
public int Id { get; set; }
public string Name { get; set; }
}
public class SomeOtherItem
{
public int Id { get; set; }
public string Name { get; set; }
}
//DAL - Data Access layer
public class ItemDAL
{
public ItemDAL()
{
}
public void Add(Item item)
{
using (NpgsqlConnection conn = new NpgsqlConnection(connString))
{
conn.Open();
using (NpgsqlCommand cmd = new NpgsqlCommand())
{
cmd.Connection = conn;
cmd.CommandText = #"INSERT INTO tbl_items (name)
VALUES (#name)";
cmd.Parameters.AddWithValue("#name", item.Name);
cmd.ExecuteNonQuery();
}
}
}
}
public class SomeOtherItemDAL
{
public SomeOtherItemDAL()
{
}
public void Add(SomeOtherItem someOtherItem)
{
using (NpgsqlConnection conn = new NpgsqlConnection(connString))
{
conn.Open();
using (NpgsqlCommand cmd = new NpgsqlCommand())
{
cmd.Connection = conn;
cmd.CommandText = #"INSERT INTO tbl_some_other_items (name)
VALUES (#name)";
cmd.Parameters.AddWithValue("#name", someOtherItem.Name);
cmd.ExecuteNonQuery();
}
}
}
}
//BLL - Business Logic Layer
public class SomeBLL
{
public SomeBLL()
{
}
public void Add(Item item, SomeOtherItem someOtherItem)
{
ItemDAL itemDAL = new ItemDAL();
SomeOtherItemDAL someOtherItemDAL = new SomeOtherItemDAL();
// *** this must be done in one transaction ***
itemDAL.Add(item);
someOtherItemDAL.Add(someOtherItem);
}
}
Now, the problem is that if I want to use Transacion, I cannot use:
using (NpgsqlConnection conn = new NpgsqlConnection(connString))
in DAL. To use NpgsqlTransacion object I must somehow keep connection opened and visible in both DAL classes.
I've tried use TransacionScope object for that, but from from some reasons it's not working with PostgreSQL and the driver I'm using (INSERTS are done just after executed and there is no transaction rollback when exception within TransacionScope occures).
What I've come into is to make additional Singleton class to keep connection alive and manage transactions:
public class DB
{
private static DB instance;
private const string connString = #"Server=localhost;Port=5432;Database=db_test;User Id=usr_test;Password=pass";
private NpgsqlConnection conn;
private DB()
{
conn = new NpgsqlConnection(connString);
}
public static DB Instance
{
get
{
if (instance == null)
{
instance = new DB();
}
return instance;
}
}
#region --- connection ---
public NpgsqlConnection GetOpenConnection()
{
OpenConnection();
return conn;
}
private void OpenConnection()
{
if (conn.State == ConnectionState.Closed || conn.State == ConnectionState.Broken)
conn.Open();
}
public void CloseConnection()
{
if (conn != null && !inTransaction)
{
conn.Close();
}
}
#endregion
#region --- transaction ---
private NpgsqlTransaction trans;
private bool inTransaction;
public bool InTransaction { get { return inTransaction; } }
public void TransactionStart()
{
OpenConnection();
trans = conn.BeginTransaction();
inTransaction = true;
}
public void TransactionCommit()
{
if (inTransaction)
{
try
{
trans.Commit();
trans.Dispose();
}
finally
{
inTransaction = false;
CloseConnection();
}
}
}
public void TransactionRollback()
{
if (inTransaction)
{
try
{
trans.Rollback();
trans.Dispose();
}
finally
{
inTransaction = false;
CloseConnection();
}
}
}
#endregion
}
and rebuild both DAL Add methods to access connection like that:
//DAL - Data Access layer
public class ItemDAL
{
public ItemDAL()
{
}
public void Add(Item item)
{
using (NpgsqlCommand cmd = new NpgsqlCommand())
{
cmd.Connection = DB.Instance.GetOpenConnection();
cmd.CommandText = #"INSERT INTO tbl_items (name)
VALUES (#name)";
cmd.Parameters.AddWithValue("#name", item.Name);
cmd.ExecuteNonQuery();
}
if (!DB.Instance.InTransaction)
DB.Instance.CloseConnection();
}
}
public class SomeOtherItemDAL
{
public SomeOtherItemDAL()
{
}
public void Add(SomeOtherItem someOtherItem)
{
using (NpgsqlCommand cmd = new NpgsqlCommand())
{
cmd.Connection = DB.Instance.GetOpenConnection();
cmd.CommandText = #"INSERT INTO tbl_some_other_items (name)
VALUES (#name)";
cmd.Parameters.AddWithValue("#name", someOtherItem.Name);
cmd.ExecuteNonQuery();
}
if (!DB.Instance.InTransaction)
DB.Instance.CloseConnection();
}
}
Note that I'd like to follow the rule to "close database connection as soon as possible", so when Add method is called without transaction scope, I'd like it to close the connection.
So, the final questions are:
1. What do you think about it, is there a better way to handle that issue, any suggestions?
2. Should I dispose a connection in DB.CloseConnecion()? I surely do when using using (NpgsqlConnection conn = ...) { ... } pattern, but as Singleton is alive as long as application, does it make sense? Connection is returned to ConnectionPool after Close(), isn't it? Or maybe I should also dispose a Singleton object (together with connection), after each using?
3. It's not directly connected question, but if I use DTO objects (just properties, no methods) and have also some BusinessObjects (BO) with the same properties, but also with additional methods (validations, calculations, operations etc.), can it be inherited from DTO? Or maybe I can use full BusinessObject to transfer it between layers, and get rid off DTO?
EDIT: TransacionScope
As requested, I add some code from my tries with TransactionScope. Simply WinForm application, no Exceptions handling. As a result, there is an Exception window when I throw it, but in database I see records with values test1 ans test2. Both when debbuging in VS and executing application from .exe
using Npgsql;
using System.Transactions;
//...
private void button1_Click(object sender, EventArgs e)
{
using (System.Transactions.TransactionScope scope = new TransactionScope())
{
AddValue("test1");
AddValue("test2");
throw new Exception("bam!");
AddValue("test3");
scope.Complete();
}
}
private void AddValue(string value)
{
string connString = "Server=localhost;Port=5432;Database=db_test;User Id=usr_test;Password=pass";
using (NpgsqlConnection conn = new NpgsqlConnection(connString))
{
conn.Open();
using (NpgsqlCommand cmd = new NpgsqlCommand())
{
cmd.Connection = conn;
cmd.CommandText = #"INSERT INTO tbl_test (name)
VALUES (#name)";
cmd.Parameters.AddWithValue("#name", value);
cmd.ExecuteNonQuery();
}
}
}
I've never used NpgSql, but reading the documentation of NpgSql it appears they have some support of TransactionScope() if you add "enlist=true" in your connection string.
I'm looking at the "System.Transactions Support" section of the below NpgSql documentation:
http://npgsql.projects.postgresql.org/docs/manual/UserManual.html
Assuming TransactionScope() did work, then you can do simething like this...
using (var scope = new System.Transactions.TransactionScope())
{
ItemDAL itemDAL = new ItemDAL();
SomeOtherItemDAL someOtherItemDAL = new SomeOtherItemDAL();
// *** this must be done in one transaction ***
itemDAL.Add(item);
someOtherItemDAL.Add(someOtherItem);
scope.Complete()
}
What you did is brave, but not scalable. I'm not familiar with PGSQL, but this problem is the exact reason why the TransactionScope API was designed.
Can you show your code using the TransactionScope api? Make sure that you are not calling scope.Complete(); if an error occurs in one of the methods. Be careful to not "eat" the exceptions inside the methods because in that case the flow will continue just like if nothing happened.
More reading about TransactionScope here:
http://msdn.microsoft.com/en-us/library/ms172152.aspx
Update 1
Thanks for sharing your code that uses the TransactionScope class. The code looks perfectly correct to me. According to this (http://npgsql.projects.postgresql.org/docs/manual/UserManual.html) document (the same quoted by ChrisNeil52), Enlist=true should be included in the connection string for transactions to work.
You might be dealing with a buggy API. Good luck with this.
I know this sounds peculiar, but something I would try would be to use a different NpgsqlCommand constructor. new NpgsqlCommand("sql query", connection), instead of creating the command and assigning it the connection. They should be equivalent. but who know...