I have a class that is responsible for all my Database actions, in general it calls stored procedures.
I've created 2 delegates in my class, one responsible for positive response (server returned OK for example) and second for all error handling.
public delegate void Part1_Callback(string message);
public delegate void Part2_Callback(DataTable dt);
public delegate void Part3_Callback(DataTable dt, int x, int y);
public delegate void ErrorHandler(string message);
I call all methods asynch like shown in my previous question: C# asynch SQL inside singleton and delegates
I have a problem when I need my delegate to return a different type of data.
For example my first method returns a String, second a DataTable, and third a DataTable and 2 ints.
Right now for every method I must create class that holds parameters:
public class Part1_CommandAndCallback
{
public SqlCommand Sql;
public Part1_Callback Callback;
public ErrorHandler Error;
}
public class Part2_CommandAndCallback
{
public SqlCommand Sql;
public Part2_Callback Callback;
public ErrorHandler Error;
}
public class Part3_CommandAndCallback
{
public SqlCommand Sql;
public Part3_Callback Callback;
public ErrorHandler Error;
}
Is it possible to create one generic delegate so that I will be able to have one delegate for responses and one class for parameters?
This way I'll be able to more easily control my code.
I found article on codeproject: http://www.codeproject.com/Articles/192027/Delegates-101-Part-III-Generic-Delegates but I don't know how to use this in my case :/
Should I declare my delegate like this:
delegate void MyDelegate (params object[] params);
or:
public delegate void MyDelegate2<T>(T param1);
but this way I'll be able to pass only one parameter, I won't be able to use same delegate for 3 parameters.
Which solution is better?
I would like to have one generic delegate that will be able to take one-to-three parameters with different types.
Can this be done?
EDIT:
I'll try to show my scenario:
In my main form class I'm calling my DB class like so:
private void form1_Enter(object sender, EventArgs e)
{
showStatus("Loading statistics...");
DB.Instance.Part1(part1_ok,ErrorHandler);
DB.Instance.Part2(part2_ok, ErrorHandler);
}
private void ErrorHandler(string msg)
{
hideStatus();
//viewStack1.InvokeIfRequired(c => { c.moveToFirst(); });
MessageBox.Show(msg, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
private void part1_ok(string msg)
{
MessageBox.Show(msg);
}
private void part2_ok(DataTable dt)
{
dataGridView1.InvokeIfRequired(c =>
{
c.DataSource = dt;
});
}
My DB class looks like so:
public delegate void Part1_Callback(string message);
public delegate void Part2_Callback(DataTable dt);
public delegate void Part3_Callback(DataTable dt, int x, int y);
public delegate void ErrorHandler(string message);
public class Part1_CommandAndCallback
{
public SqlCommand Sql;
public Part1_Callback Callback;
public ErrorHandler Error;
}
public class Part2_CommandAndCallback
{
public SqlCommand Sql;
public Part2_Callback Callback;
public ErrorHandler Error;
}
public class Part3_CommandAndCallback
{
public SqlCommand Sql;
public Part3_Callback Callback;
public ErrorHandler Error;
}
class DB : SingletonBase<DB>
{
public static readonly string SqlConnectionString = #"Data Source=MyDB;Initial Catalog=Stats;Integrated Security=True;Asynchronous Processing=true;";
private DB()
{
}
public void Part1(Part1_Callback callback, ErrorHandler error)
{
SqlConnection conn = new SqlConnection(SqlConnectionString);
SqlCommand cmd = conn.CreateCommand();
cmd.CommandType = CommandType.StoredProcedure;
cmd.CommandText = "Part1";
try
{
conn.Open();
}
catch (Exception ex)
{
error(ex.ToString());
return;
}
Part1_CommandAndCallback ar = new Part1_CommandAndCallback() { Callback = callback, Error = error, Sql = cmd };
IAsyncResult result = cmd.BeginExecuteReader(new AsyncCallback(Part1_Handler), ar, CommandBehavior.CloseConnection);
}
private void Part1_Handler(IAsyncResult result)
{
string stats = string.Empty;
Part1_CommandAndCallback ar = (Part1_CommandAndCallback)result.AsyncState;
SqlDataReader dr;
if (result.IsCompleted)
{
dr = ar.Sql.EndExecuteReader(result);
}
else
dr = null;
while (dr.Read())
{
stats += dr[0].ToString() + Environment.NewLine;
}
dr.NextResult();
while (dr.Read())//problem is here
{
stats += dr[0].ToString() + " - " + dr[1].ToString() +Environment.NewLine;
}
dr.Close();
ar.Callback(stats);
}
public void Part2(Part2_Callback callback, ErrorHandler error)
{
SqlConnection conn = new SqlConnection(SqlConnectionString);
SqlCommand cmd = conn.CreateCommand();
cmd.CommandType = CommandType.StoredProcedure;
cmd.CommandText = "Part2";
try
{
conn.Open();
}
catch (Exception ex)
{
error(ex.ToString());
return;
}
Part2_CommandAndCallback ar = new Part2_CommandAndCallback() { Callback = callback, Error = error, Sql = cmd };
IAsyncResult result = cmd.BeginExecuteReader(new AsyncCallback(Part2_Handler), ar, CommandBehavior.CloseConnection);
}
private void Part2_Handler(IAsyncResult result)
{
DataTable dt = new DataTable();
Part2_CommandAndCallback ar = (Part2_CommandAndCallback)result.AsyncState;
SqlDataReader dr;
if (result.IsCompleted)
{
dr = ar.Sql.EndExecuteReader(result);
}
else
dr = null;
dt.Load(dr);
dr.Close();
ar.Callback(dt);
}
}
My idea was to use my singleton DB controller in many forms at the same time. So in first form I'll see some stats that will auto refresh, and if I'll like then is second form I'll be able to see some different stats that I'll be able to refresh on button click.
I would use the built in delegates I think,
Action<T>
Action<T, T>
etc
See here:
http://msdn.microsoft.com/en-us/library/018hxwa8.aspx
It looks like what you need is to create a generic CommandAndCallback class:
public class CommandAndCallback<TCallback>
{
public SqlCommand Sql;
public TCallback Callback;
public ErrorHandler Error;
}
And, for example, where you previously used Part3_CommandAndCallback, you would now use CommandAndCallback<Part3_Callback> or even CommandAndCallback<Action<DataTable, int, int>>, if you didn't want to create a delegate type for each part either.
Also, public fields are generally discouraged, so you might consider changing them to autoproperties:
public class CommandAndCallback<TCallback>
{
public SqlCommand Sql { get; set; }
public TCallback Callback { get; set; }
public ErrorHandler Error { get; set; }
}
Related
I'm trying to do a three layer program in C# with mysql database.
I have my main form, with an datagridview inside it, and I'm loading the data of the dgv in the form load:
private void Principal_Load(object sender, EventArgs e)
{
// load data
C_Cliente.GetClientes();
dgv_Clientes.DataSource = C_Cliente.dtClientes;
}
in my controller clientes (C_Cliente) class I have like this:
public static DataTable dtClientes;
public static void GetClientes()
{
DAO_Cliente db = new DAO_Cliente();
dtClientes = db.GetClientes();
}
public bool cadastrarCliente(M_Cliente cliente)
{
// validate cliente attributes and then:
DAO_Cliente db = new DAO_Cliente();
if (db.CadastrarCliente(cliente))
{
GetClientes();
return true;
}
else
{
return false;
}
}
and in my DAO object (DAO_Cliente) I have like this:
public bool CadastrarCliente(M_Cliente cliente)
{
// my method to insert a client inside the mysql database, irrelevant here.
}
public DataTable GetClientes()
{
DAO dao = DAO.getInstance(); // it just has the openconnection and closeconnection methods.
try
{
dao.OpenConnection();
MySqlCommand cmd = dao.GetConnection().CreateCommand();
cmd.CommandText = "select * from clientes";
MySqlDataAdapter da = new MySqlDataAdapter(cmd);
DataTable dt = new DataTable();
da.Fill(dt);
return dt;
}
catch (MySqlException ex)
{
MessageBox.Show(ex.Message.ToString());
return null;
}
finally
{
dao.CloseConnection();
}
}
Basically, when I insert a new client into my database, it calls the method GetClientes() which changes the dtClientes to a new pointer, but then, my datagridview is still using the old pointer (so it doesn't get updated). How can I fix that? Thank you so much!
I'm getting some troubles in the last days to use the SQL Dependency.
With the sample in this link I have ALMOST reach my goal. "Almost" because the event get fired correctly, but also at the startup, when I call the Start method of the MyService class. Here there is my code, but as you can see it's the same (or almost) of the link.
I think I can simply insert a bool value, only to avoid the initialitation event, but I think also that another solution would be better.
SQLWatcher:
using System;
using System.Collections.Generic;
using System.Data;
using System.Data.SqlClient;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
public enum SqlWatcherNotificationType
{
Blocking,
Threaded // Launch in another thread so SqlWatcher can immediately start monitoring again.
}
public class SqlWatcher : IDisposable
{
private string ConnectionString;
private SqlConnection Connection;
private SqlCommand Command;
private SqlDataAdapter Adapter;
private DataSet Result;
private SqlWatcherNotificationType NotificationType;
public SqlWatcher(string ConnectionString, SqlCommand Command, SqlWatcherNotificationType NotificationType)
{
this.NotificationType = NotificationType;
this.ConnectionString = ConnectionString;
SqlDependency.Start(this.ConnectionString);
this.Connection = new SqlConnection(this.ConnectionString);
this.Connection.Open();
this.Command = Command;
this.Command.Connection = this.Connection;
Adapter = new SqlDataAdapter(this.Command);
}
public void Start()
{
RegisterForChanges();
}
public void Stop()
{
SqlDependency.Stop(this.ConnectionString);
}
public delegate void SqlWatcherEventHandler(DataSet Result);
public event SqlWatcherEventHandler OnChange;
public DataSet DataSet
{
get { return Result; }
}
private void RegisterForChanges()
{
//Remove old dependency object
this.Command.Notification = null;
//Create new dependency object
SqlDependency dep = new SqlDependency(this.Command);
dep.OnChange += new OnChangeEventHandler(Handle_OnChange);
//Save data
Result = new DataSet();
Adapter.Fill(Result);
//Notify client of change to DataSet
switch (NotificationType)
{
case SqlWatcherNotificationType.Blocking:
OnChange(Result);
break;
case SqlWatcherNotificationType.Threaded:
ThreadPool.QueueUserWorkItem(ChangeEventWrapper, Result);
break;
}
}
public void ChangeEventWrapper(object state)
{
DataSet Result = (DataSet)state;
OnChange(Result);
}
private void Handle_OnChange(object sender, SqlNotificationEventArgs e)
{
if (e.Type != SqlNotificationType.Change)
throw new ApplicationException("Failed to create queue notification subscription!");
//Clean up the old notification
SqlDependency dep = (SqlDependency)sender;
dep.OnChange -= Handle_OnChange;
//Register for the new notification
RegisterForChanges();
}
public void Dispose()
{
Stop();
}
}
and this is MyService class:
public class MyService
{
private static SqlWatcher SqlQueueWatcher;
public static void Start()
{
string connS = MainWindow.dbContext.Database.Connection.ConnectionString + "Password=111;";
//Build the command object we want to monitor (don't include a SqlConnection)
SqlCommand cmd = new SqlCommand();
cmd = new SqlCommand("SELECT CODVEI FROM dbo.ArchivioErogazioni");
cmd.CommandType = CommandType.Text;
//Setup the SQLWatcher
SqlQueueWatcher = new SqlWatcher(connS, cmd, SqlWatcherNotificationType.Blocking);
SqlQueueWatcher.OnChange += new SqlWatcher.SqlWatcherEventHandler(QueueSQLWatcher_OnChange);
SqlQueueWatcher.Start();
}
private static void QueueSQLWatcher_OnChange(DataSet Result)
{
//Do something with the updated DataSet object
Debug.WriteLine("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"); ---> ENTERS HERE IN THE INIT PHASE
}
public static void Stop()
{
SqlQueueWatcher.Dispose();
}
}
and this is how I symply call it:
MyService.Start();
Is the bool flag a bad idea? Why does it enter in QueueSQLWatcher_OnChange at startup time?
To fix this issue, you need only to set the notification for that SqlCommand to null.
cmd.Notification = null;
Just try this and i think your problem will be resolved. Enter this line before starting the dependency using the SqlWatcher class
Of course there are many answers for this question,though,I got no clear Idea for my problem.
I'm working on Visual Studio.I have 2 forms as one is weeklyGVadminview and dailyGVadminview..
I wrote a function(method) in dailyGVadminview which is needed now in another form.....
I'm adding my code here..Any ansers will be appreciated in advance thanks.
namespace weeklyattendance
{
public partial class dailyGvAdminview : Form
{
public dailyGvAdminview()
{
InitializeComponent();
}
private void datechanges()
{
string date = dateTimePicker1.Value.ToString("dd/MM/yyyy");
var connectionstring = ConfigurationManager.ConnectionStrings["attendancemanagement"].ConnectionString;
SqlConnection cn = new SqlConnection(connectionstring);
string query = "select count(Employee_id) from employee_details";
cn.Open();
SqlCommand cmd = new SqlCommand(query, cn);
object result =cmd.ExecuteScalar();
txttotalstaff.Text = (result.ToString());
cn.Close();
}
}
}
Form2 code as follows
namespace weeklyattendance
{
public partial class weeklyGvAdminView : Form
{
public weeklyGvAdminView()
{
InitializeComponent();
}
}
private void dateTimePicker1_ValueChanged_1(object sender, EventArgs e)
{
//here I want to call that function or method
}
}
So I would just create a new class and put the method on that class:
public class DbHandler
{
public string DateChanges()
{
string date = dateTimePicker1.Value.ToString("dd/MM/yyyy");
var connectionstring = ConfigurationManager.ConnectionStrings["attendancemanagement"].ConnectionString;
SqlConnection conn = new SqlConnection(connectionstring);
string query = "select count(Employee_id) from employee_details";
conn.Open();
SqlCommand cmd = new SqlCommand(query, conn);
object result = cmd.ExecuteScalar();
var result = result.ToString();
conn.Close();
return result;
}
}
Then in your classes do this:
var dbHandler = new DbHandler();
var result = dbHandler.DateChanges();
I would try and follow some conventions as well. Makes it easier for other programmers to follow what is going on.
You can create object of dailyGvAdminview and call for that try below code :-
namespace weeklyattendance
{
public partial class weeklyGvAdminView : Form
{
public weeklyGvAdminView()
{
InitializeComponent();
}
}
private void dateTimePicker1_ValueChanged_1(object sender, EventArgs e)
{
var form1 = new dailyGvAdminview();
//// call function using this instance
}
}
If you want to access method from outside then make it to Public as shown below :-
public string DateChanges()
{
}
If you want a really GLOBAL and easy solution do this, first you make a static class.
namespace weeklyattendance
{
public static class Obj
{
public static dailyGvAdminview globalDaily;
}
}
Then in your initialize or onLoad code where you create this class just write
Obj.globalDaily = this;
Now you can access this class anywhere in your program with Obj.globalDaily, you can also add whatever else you want in there and access it like that
I'm trying to make a thread safe Data Access Layer (kind of like a SQL Data Client wrapper). What are some steps I should be making to make this thread safe, while maximizing performance.
For example, if i add a lock on the sqlConn before it closes the connection (since it implements IDisposable); what if the connection is in the middle of a transaction or query?
In summary, I'm trying to accomplish a thread-safe solution; but at the same time, I do not want to risk any critical exceptions, or any delays. Is there any way I can prioritize the closing thread?
public class SQLWrapper : IDisposable
{
private SqlConnection _sqlConn;
public SQLWrapper(string serverName_, string dbName_)
{
SqlConnectionStringBuilder sqlConnSB = new SqlConnectionStringBuilder()
{
DataSource = serverName_,
InitialCatalog = dbName_,
ConnectTimeout = 30,
IntegratedSecurity = true,
};
sqlConnSB["trusted_connection"] = "yes";
this.start(sqlConnSB.ConnectionString);
}
public SQLWrapper(string connString_)
{
this.start(connString_);
}
private void start(string connString_)
{
if (string.IsNullOrEmpty(connString_) == true)
throw new ArgumentException("Invalid connection string");
**lock (this._sqlConn)**
{
this._sqlConn = new SqlConnection(connString_);
this._sqlConn.Open();
}
}
private void CloseConnection()
{
**lock (this._sqlConn)**
{
this._sqlConn.Close();
this._sqlConn.Dispose();
this._sqlConn = null;
}
}
}
The step you should do is:
NOT making it thread safe.
Simple.
Every thread should have it's own copy, with locking / synchronization happening on the database.
THen it will also scale across computers.
This is the standard approach for the last 20 years or so.
So, every thread creates a new SqlWrapper and everything is fine.
The database performs connection pooling for you; lean on it as much as you can. You really shouldn't require locking.
Option 1.
SqlConnection is not encapsulated by the DAO class; appropriate using structures and storage of the connection string is required at the method level.
public class SomeDAO
{
private readonly string _connectionString;
public SomeDAO(string dsn)
{
_connectionString = dsn;
}
public IEnumerable<AssetVO> DoWork()
{
const string cmdText = "SELECT [AssetId] FROM [dbo].[Asset]";
using (var conn = new SqlConnection(_connectionString))
{
conn.Open();
using (var cmd = new SqlCommand(cmdText, conn))
using (var dr = cmd.ExecuteReader())
{
while (dr.Read())
{
yield return new AssetVO
{
AssetId = Guid.Parse(dr["AssetId"].ToString()),
};
}
}
}
}
}
Option 2.
The SqlConnection is a class member; it's state is carefully maintained by helper methods. Using syntax is used for SqlDataReader and SqlCommand.
public class SomeDAO : IDisposable
{
#region backing store
private readonly SqlConnection _connection;
#endregion
public SomeDAO(string dsn)
{
_connection = new SqlConnection(dsn);
}
public SqlConnection OpenConnection()
{
if (_connection.State != ConnectionState.Closed)
_connection.Open();
return _connection;
}
public void CloseConnection()
{
if (_connection.State != ConnectionState.Closed)
_connection.Close();
}
public IEnumerable<AssetVO> DoWork()
{
const string cmdText = "SELECT [AssetId] FROM [dbo].[Asset]";
try
{
using (var cmd = new SqlCommand(cmdText, OpenConnection()))
using (var dr = cmd.ExecuteReader())
{
while (dr.Read())
{
yield return new AssetVO
{
AssetId = Guid.Parse(dr["AssetId"].ToString()),
};
}
}
}
finally
{
CloseConnection();
}
}
#region Implementation of IDisposable
/// <summary>
/// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
/// </summary>
/// <filterpriority>2</filterpriority>
public void Dispose()
{
_connection.Dispose();
}
#endregion
}
Both solutions survive a threaded test without the need for explicit locking.
private static volatile bool _done;
private static void Main()
{
#region keyboard interrupt
ThreadPool.QueueUserWorkItem(delegate
{
while (!_done)
{
if (!Console.KeyAvailable) continue;
switch (Console.ReadKey(true).Key)
{
case ConsoleKey.Escape:
_done = true;
break;
}
}
});
#endregion
#region start 3 threads in the pool
ThreadPool.QueueUserWorkItem(DatabaseWorkerCallback);
ThreadPool.QueueUserWorkItem(DatabaseWorkerCallback);
ThreadPool.QueueUserWorkItem(DatabaseWorkerCallback);
#endregion
Thread.Sleep(Timeout.Infinite);
}
private static void DatabaseWorkerCallback(object state)
{
Console.WriteLine("[{0}] Starting", Thread.CurrentThread.ManagedThreadId);
Thread.Sleep(1000);
while (!_done)
{
using (var dao = new SomeDAO(Properties.Settings.Default.DSN))
{
foreach (var assetVo in dao.DoWork())
Console.WriteLine(assetVo);
}
}
Console.WriteLine("[{0}] Stopping", Thread.CurrentThread.ManagedThreadId);
}
I'm trying to build a class that will be responsible for all operations on database.
Using singleton pattern from here: http://codebender.denniland.com/a-singleton-base-class-to-implement-the-singleton-pattern-in-c/
I've build a class like so:
class DB : SingletonBase<DB>
{
public static readonly string SqlConnectionString = #"Data Source=MYDB;Initial Catalog=PRODUCTS;Integrated Security=True;Asynchronous Processing=true;";
private DB()
{
}
public void loadData()
{
SqlConnection conn = new SqlConnection(SqlConnectionString);
SqlCommand cmd = conn.CreateCommand();
cmd.CommandType = CommandType.StoredProcedure;
cmd.CommandText = "STATISTICS_1";
cmd.Connection = conn;
conn.Open();
IAsyncResult result = cmd.BeginExecuteReader(new AsyncCallback(HandleCallback), cmd, CommandBehavior.CloseConnection);
}
private void HandleCallback(IAsyncResult result)
{
SqlDataReader dr;
SqlCommand _this = (SqlCommand)result.AsyncState;
if (result.IsCompleted)
{
dr = _this.EndExecuteReader(result);
}
else
dr = null;
DataTable dt = new DataTable();
dt.Load(dr);
dr.Close();
MessageBox.Show("loaded");
}
}
In my main class I'm using this like so:
private void loadStatistics(object sender, EventArgs e)
{
showStatus("loading data");
DB.Instance.loadData();
}
But this will give me only my message box.
What I would like to do is to declare function in my main class that will be called after SQL query return something.
I think that the best way would by using events, but I don't know how to do that proper way.
I would like to do something like this in my main class:
private void loadCompleted(string msg)
{
MessageBox.Show(msg);
}
private void loadStatistics(object sender, EventArgs e)
{
showStatus("loading data");
DB.Instance.loadData(loadCompleted);
}
So that I can specify function that will be called after SQL call is finished.
I don't know if this is the best way of doing this, so any comments, suggestions and solutions are welcome.
What I would like to achieve is to have one class responsible for calling SQL asynchronously and passing data to other functions that will process it.
public delegate void NotifyCallback(string message);
public class ClassWithCommandAndCallback
{
public SqlCommand Sql;
public NotifyCallback Callback;
}
public void loadData(NotifyCallback callback)
{
ClassWithCommandAndCallback ar = new ClassWithCommandAndCallback();
ar.Sql = cmd;
ar.Callback = callback;
IAsyncResult result = cmd.BeginExecuteReader(new AsyncCallback(HandleCallback), ar, CommandBehavior.CloseConnection);
}
private void HandleCallback(IAsyncResult result)
{
ClassWithCommandAndCallback ar = (ClassWithCommandAndCallback)result.AsyncState;
ar.Callback("loaded (SQL was: "+ar.Sql+")");
}