I am currently working on a problem that involves abstract classes and stopwatches. I have two classes, SQL and Oracle. They both take a string for a connection code (this stuff doesn't actually do anything, but I am trying to make this a bit realistic). I want to start a stopwatch, then stop it in a different method - but the Timespan always says 00:00...
Am I accessing my parent class' properties correctly?
I have tried initializing my stopwatches and timespan in different places.
public class Program
{
public static void Main(string[] args)
{
// ConnectionManagement management = new ConnectionManagement();
// management.SetUpOptions();
}
}
public class ConnectionManagement
{
public void SetUpOptions()
{
while (true)
{
SqlConnection sqlGatherer = new SqlConnection("placeholder");
OracleConnection oracleGatherer = new OracleConnection("placeholder");
Console.WriteLine("1. Open an SQL connection.");
Console.WriteLine("2. Close an SQL connection.");
Console.WriteLine("3. Open an Oracle connection.");
Console.WriteLine("4. Close an SQL connection.");
string choice = Console.ReadLine();
if (choice == "1")
{
Console.WriteLine("Enter your connection string.");
string enteredConnectionString = Console.ReadLine();
sqlGatherer.ConnectionString = enteredConnectionString;
sqlGatherer.OpenConnection();
}
else if (choice == "2")
{
sqlGatherer.CloseConnection();
}
else if (choice == "3")
{
Console.WriteLine("Enter your connection string.");
string enteredConnectionString = Console.ReadLine();
oracleGatherer.ConnectionString = enteredConnectionString;
oracleGatherer.OpenConnection();
}
else if (choice == "4")
{
oracleGatherer.CloseConnection();
}
else
{
Console.WriteLine("That was not a valid option.");
}
}
}
}
public abstract class DataBaseConnection
{
public string ConnectionString { get; set; }
public TimeSpan Timeout { get; set; }
public Stopwatch OracleStoppy { get; set; }
public Stopwatch SqlStoppy { get; set; }
public abstract void OpenConnection();
public abstract void CloseConnection();
}
public class SqlConnection : DataBaseConnection
{
private bool CurrentConnection = false;
public SqlConnection()
{
Timeout = new TimeSpan();
SqlStoppy = new Stopwatch();
}
public SqlConnection(string connectionString)
{
Timeout = new TimeSpan();
SqlStoppy = new Stopwatch();
if (connectionString == null || String.IsNullOrWhiteSpace(connectionString))
{
throw new ArgumentException("Program has an invalid SQL connection string.");
}
else
{
this.ConnectionString = connectionString;
}
}
public override void OpenConnection()
{
if (CurrentConnection == true)
{
throw new Exception("A connection has already been established.");
}
else
{
Console.WriteLine("SQL connection established.");
SqlStoppy.Start();
CurrentConnection = true;
}
}
public override void CloseConnection()
{
if (CurrentConnection == false)
{
SqlStoppy.Stop();
TimeSpan reportedTimeout = Timeout;
Console.WriteLine("Connection closed. \nThe connection was active for {0}", reportedTimeout);
SqlStoppy.Reset();
CurrentConnection = false;
}
else
{
throw new Exception("There is no SQL connection to close.");
}
}
}
public class OracleConnection : DataBaseConnection
{
private bool CurrentConnection = false;
public OracleConnection()
{
Timeout = new TimeSpan();
OracleStoppy = new Stopwatch();
}
public OracleConnection(string connectionString)
{
Timeout = new TimeSpan();
OracleStoppy = new Stopwatch();
if (connectionString == null || String.IsNullOrWhiteSpace(connectionString))
{
throw new Exception("Program has an invalid Oracle connection string.");
}
else
{
this.ConnectionString = connectionString;
}
}
public override void OpenConnection()
{
if (CurrentConnection == true)
{
throw new Exception("A connection has already been established.");
}
else
{
Console.WriteLine("Oracle connection established.");
OracleStoppy.Start();
CurrentConnection = true;
}
}
public override void CloseConnection()
{
if (CurrentConnection == false)
{
throw new Exception("There is no Oracle connection to close.");
}
else
{
OracleStoppy.Stop();
this.Timeout = OracleStoppy.Elapsed;
Console.WriteLine("Connection closed. \nThe connection was active for {0}", Timeout);
OracleStoppy.Reset();
CurrentConnection = false;
}
}
}
After I close an opened connection, the method should print how long it was opened for, or the stopwatch duration. The same problem happened with my CurrentConnection variable, so I put it as a private variable in each class. But isn't the point of having a parent class like this to have common properties that its children classes can interact with?
Problem
I see that in oracle you have:
this.Timeout = OracleStoppy.Elapsed;
Console.WriteLine("Connection closed. \nThe connection was active for {0}", Timeout);
But in sql:
TimeSpan reportedTimeout = Timeout;
Console.WriteLine("Connection closed. \nThe connection was active for {0}", reportedTimeout);
This should be
this.Timeout = SqlStoppy.Elapsed;
Console.WriteLine("Connection closed. \nThe connection was active for {0}", Timeout);
Isn't it?
Advices
You use a parent class, so you don't need two stopwatches, only one is needed else inheritence is useless and you repeat code.
You should consider remove some setters too.
And move CurrentConnection that is repeated, as protected.
This should be:
public abstract class DataBaseConnection
{
protected bool CurrentConnection;
public string ConnectionString { get; }
public TimeSpan Timeout { get; }
public Stopwatch Stoppy { get; }
public abstract void OpenConnection();
public abstract void CloseConnection();
public DataBaseConnection()
{
Timeout = new TimeSpan();
Stoppy = new StopWatch();
}
}
Also use call pattern between constructors:
public OracleConnection()
{
...
}
public OracleConnection(string connectionString)
: this()
{
...
}
You repeat too many same code and you really should refactor and better abstract things.
When code is repeated you can:
create one method and call it instead of repeated code.
sometimes you can move that in a parent class.
When variables is repeated in child classes you can remove them to have only one in a parent class.
You do that playing with modifiers like public, private, protected, internal, abstract, virtual, override...
You should also rename some vars to be more consistent and coherent.
The rule is that a name must be simple and designate exactly what it is.
For example Chrono may be better that Stoppy as well as index or indexRowis better that i.
So here Duration or Elapsed may be better than Timeout that indicates the delay to stop a connection attempt.
Also because classes are named Connection you don't need to specify OpenConnection and CloseConnection: Open and Close is sufficient.
Perhaps you will find this usefull:
How to choose between private and protected access modifier
What is polymorphism
Related
I'm using the Quartz.NET library to create a job in my C# application.
I have some registers in my database, so I have a table wich contains a column called "start_date". The job runs every 50 seconds, so I compare the dates from the column "start_date" with the date of my computer, and if the dates are equal, I want to instantiate a new Windows Form with a message and a button.
At the moment, the new Windows Form is opening at the right moment, but the message is not showed and the window stops to respond.
Basically, in my code I have something like this:
FormMessage.cs
public partial class FormMessage : Form
{
public FormMessage()
{
InitializeComponent();
}
public FormMessage(double minutes)
{
InitializeComponent();
string message = string.Format("You have {0} minutes!", minutes);
lblMessage.Text = message ;
}
private void btnOK_Click(object sender, EventArgs e)
{
this.Close();
}
}
JobMessage.cs
public class JobMessage: IJob
{
List<Information> informations;
public void Execute(IJobExecutionContext context)
{
//Class with methods to get registers from database.
InformationAPI infoAPI = new InformationAPI();
informations = infoAPI.GetInformations();
foreach (Information info in informations)
{
DateTime computerDateTime = DateTime.Now;
DateTime infoDateTime = info.StartDate;
double difference;
if (DateTime.Compare(computerDateTime, infoDateTime) < 0)
{
difference = Math.Round(infoDateTime.Subtract(computerDateTime).TotalMinutes);
if (difference == 5)
{
FormMessage formMessage = new FormMessage(difference);
formMessage.Show();
}
}
}
}
}
Someone have some idea of the reason why the FormMessage window stops to respond?
Thank you for your attention!
You can try Quartz Listeners to let them open the form to show the data and keep the execution out of the job scope:
Action<IJobExecutionContext, JobExecutionException> listenerAction = (c, e) => {
var dataMap = context.GetJobDetail().GetJobDataMap();
var difference = dataMap.GetIntValue("difference");
FormMessage formMessage = new FormMessage(difference);
formMessage.Show();
}
var listener = new SyncJobListener(listenerAction);
And add the listener in to the scheduler:
scheduler.ListenerManager.AddJobListener(listener,
GroupMatcher<JobKey>.GroupEquals("GroupName"));
Using this SyncJobListener:
public class SyncJobListener : IJobListener
{
private readonly Action<IJobExecutionContext, JobExecutionException> _syncExecuted;
public string Name { get; private set; }
public SyncJobListener(
Action<IJobExecutionContext, JobExecutionException> syncExecuted
)
{
Name = Guid.NewGuid().ToString();
_syncExecuted = syncExecuted;
}
public void JobToBeExecuted(IJobExecutionContext context)
{
}
public void JobExecutionVetoed(IJobExecutionContext context)
{
}
public void JobWasExecuted(IJobExecutionContext context, JobExecutionException jobException)
{
_syncExecuted(context, jobException);
}
}
I have not tested this so if the dataMap does not have any data, you are going to need to allow the persistance:
[PersistJobDataAfterExecution]
[DisallowConcurrentExecution]
public class JobMessage: IJob {}
Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 7 years ago.
Improve this question
Just 1st student rotating from java to C# here. When we were studying Java we were given this kind of SQL connection manager class thing, which is just basically just bunch of code to make it easier accessing it in different classes (It's written by my danish teacher and has some misspells/inside jokes, not sure):
public class DbConnection
{ //Constants used to get access to the database
//SQL Server
private static final String driver = "nope";
// private static final String driver = "nope";
private static final String databaseName = ";databaseName=nope";
//SQL Server
// private static String userName = ";user=sa";
private static String userName = "; user=nope";
private static String password = ";password=nope";
private DatabaseMetaData dma;
private static Connection con;
// an instance of the class is generetated
private static DbConnection instance = null;
// the constructor is private to ensure that only one object of this class is created
DbConnection()
{
String url = driver + databaseName + userName + password;
try{
//load af driver
//SQL Server
Class.forName("com.microsoft.sqlserver.jdbc.SQLServerDriver");
System.out.println("Load af class ok");
}
catch(Exception e){
System.out.println("Can not find the driver");
System.out.println(e.getMessage());
}//end catch
try{
//connection to the database
con = DriverManager.getConnection(url);
//set autocommit
con.setAutoCommit(true);
dma = con.getMetaData(); // get meta data
System.out.println("Connection to " + dma.getURL());
System.out.println("Driver " + dma.getDriverName());
System.out.println("Database product name " + dma.getDatabaseProductName());
}//end try
catch(Exception e){
System.out.println("Problems with the connection to the database");
System.out.println(e.getMessage());
System.out.println(url);
}//end catch
}//end constructor
//closeDb: closes the connection to the database
public static void closeConnection()
{
try{
con.close();
System.out.println("The connection is closed");
}
catch (Exception e){
System.out.println("Error trying to close the database " + e.getMessage());
}
}//end closeDB
//getDBcon: Get-method, returns the connection to the database
public Connection getDBcon()
{
return con;
}
//this method is used to get the instance of the connection
public static DbConnection getInstance()
{
if (instance == null)
{
instance = new DbConnection();
}
return instance;
}
public static void startTransaction()
{ try{
con.setAutoCommit(false);
}
catch(Exception e){
System.out.println("fejl start transaction");
System.out.println(e.getMessage());
}
}
public static void commitTransaction()
{ try{
con.setAutoCommit(true);
}
catch(Exception e){
System.out.println("fejl commit transaction");
System.out.println(e.getMessage());
}
}
public static void rollbackTransaction()
{ try{
con.rollback();
con.setAutoCommit(true);
}
catch(Exception e){
System.out.println("fejl rollback transaction");
System.out.println(e.getMessage());
}
}
}//end DbConnection
So to get used to C# for second year i thought of first of recreating this in C# and first of all: Is it good idea to have it in C#? I see many people just using
using(SqlConnection....){}
thing and I'm not sure how to implement autoCommits/Transaction rollbacks since for example transactions in C# are different classes
So far i made this little class:
class DbConnection
{
private const string DB_USER_ID = "user id=sa;";
private const string DB_USER_PASSWORD = "password=nope;";
private const string DB_SERVER_URL = #"server=localhost\SQLExpress1;";
private const string DB_NAME = "database=test; ";
private const string DB_TIME_OUT = "connection timeout=30";
private const string DB_TRUSTED_CONN = "Trusted_Connection=yes;";
private static SqlConnection myConnection = null;
private static DbConnection instance = null;
// private constructor to ensure that only object of this class is created
private DbConnection()
{
createConnection();
}
// Instantiates SqlConnection object
private void createConnection()
{
Console.WriteLine("Attempting to create connectiong...");
try
{
myConnection = new SqlConnection(DB_USER_ID +
DB_USER_PASSWORD +
DB_SERVER_URL +
DB_TRUSTED_CONN +
DB_NAME +
DB_TIME_OUT);
}
catch (Exception e)
{
Console.WriteLine("Problems with the connection to the database");
Console.WriteLine(e.Message);
}
}
private void openConnection()
{
try{
myConnection.Open();
Console.WriteLine("Connection succesfful!");
} catch(Exception e) {
Console.WriteLine(e.StackTrace);
}
}
public static void closeConnection()
{
try
{
myConnection.Close();
Console.WriteLine("Connection closed");
}
catch (Exception e)
{
Console.WriteLine("Problem closing connection");
Console.WriteLine(e.Message);
}
}
public SqlConnection getDBcon()
{
return myConnection;
}
public static DbConnection getInstance()
{
if (instance == null)
{
instance = new DbConnection();
}
return instance;
}
}
Man, that DbConnection class is bad stuff. Throw it away. In VB the author would have used ON ERROR RESUME NEXT.
The main problem is that errors are just thrown away. The program continues in a bad state.
Next problem is a static (globally shared) connection object. That's not thread-safe and if the connection ever breaks (network issue) it permanently breaks.
.NET has connection pooling. This class is something you don't need. Maybe you can write yourself a little helper to open a connection:
static SqlConnection CreateConnection() {
...
}
using (var conn = CreateConnection()) {
}
How much more simple can it be?
I've been trying to implement a windows service that would keep vpn connection alive. I've found that it is possible to achieve using DotRas library by subscribing to RasConnectionWatcher.Disconnected event:
public class SampleService {
public SampleService() {
this.shutdownEvent = new ManualResetEvent(false);
this.connectionWatcher = new RasConnectionWatcher();
this.connectionWatcher.Disconnected += onVpnDisconnected;
}
// redial
void onVpnDisconnected(Object sender, RasConnectionEventArgs e) {
this.DialUp();
}
void DialUp() {
// connection setup is omitted
// keep the handle of the connection
this.connectionWatcher.Handle = dialer.Dial();
}
public void Start() {
this.thread = new Thread(WorkerThreadFunc);
this.thread.IsBackground = true;
this.thread.Start();
}
public void Stop() {
this.shutdownEvent.Set();
if(!this.thread.Join(3000)) this.thread.Abort();
}
private void WorkerThreadFunc() {
this.DialUp();
while(!this.shutdownEvent.WaitOne(0)) Thread.Sleep(1000);
}
}
When I start the service vpn connection opens without any problem, but when I manually interrupt the connection it seems that Disconnected event doesn't fire up.
solution 1
Found similar question/answer here:
http://social.msdn.microsoft.com/Forums/en-US/56ab2d0d-2425-4d76-81fc-04a1e1136141/ras-connection-application-and-service?forum=netfxnetcom.
solution 2
Got an answer from Jeff Winn yesterday:
https://dotras.codeplex.com/discussions/547038
public class VpnKeeperService : IService {
private ManualResetEvent shutdownEvent;
private RasConnectionWatcher connWatcher;
private Thread thread;
public VpnKeeperService() {
this.shutdownEvent = new ManualResetEvent(false);
this.connWatcher = new RasConnectionWatcher();
this.connWatcher.EnableRaisingEvents = true;
this.connWatcher.Disconnected += (s, args) => { this.DialUp(); };
}
Boolean DialUp() {
try {
using(var phoneBook = new RasPhoneBook()) {
var name = VpnConfig.GetConfig().ConnectionName;
var user = VpnConfig.GetConfig().Username;
var pass = VpnConfig.GetConfig().Password;
var pbPath = VpnConfig.GetConfig().PhoneBookPath;
phoneBook.Open(pbPath);
var entry = phoneBook.Entries.FirstOrDefault(e => e.Name.Equals(name));
if(entry != null) {
using(var dialer = new RasDialer()) {
dialer.EntryName = name;
dialer.Credentials = new NetworkCredential(user, pass);
dialer.PhoneBookPath = pbPath;
dialer.Dial();
}
}
else throw new ArgumentException(
message: "entry wasn't found: " + name,
paramName: "entry"
);
}
return true;
}
catch {
// log the exception
return false;
}
}
public void Start() {
this.thread = new Thread(WorkerThreadFunc);
this.thread.Name = "vpn keeper";
this.thread.IsBackground = true;
this.thread.Start();
}
public void Stop() {
this.shutdownEvent.Set();
if(!this.thread.Join(3000)) {
this.thread.Abort();
}
}
private void WorkerThreadFunc() {
if(this.DialUp()) {
while(!this.shutdownEvent.WaitOne(0)) {
Thread.Sleep(1000);
}
}
}
}
Hope it helps someone.
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);
}
okay, so here is what im doing:
class Connection
{
public int SERVERID;
private Thread connection;
public Connection()
{
connection = new Thread(new ThreadStart(this.Run));
}
public void Start(int serverid)
{
SERVERID = serverid;
connection.Start();
}
void Run()
{
while(true)
{
//do stuff here
}
}
}
now, there is the class i need to manage, here is how im calling it:
static void Main(string[] args)
{
StartConnection(1);
StartConnection(2);
StartConnection(3);
//etc
}
static void StartCOnnection(int serverid)
{
Connection connect = new Connection();
connect.Start(serverid);
}
i was origanally trying to do somthing like this:
foreach(Connection connect in Connection)
{
if(connect.SERVERID == 2)
{
//destroy the thread, and class.
}
}
but that gets the error " 'Connection' is a 'type' but is used like a 'variable'
", and i dont know how to do that destroy the thread and class part...
Summary:
So what i basically need to do, is get a list of all the open Connetion class's, and be able to, based on the settings of the class be able to destroy it. how do i do this?
~code examples please
You didn't say what kind of errors you're getting. That might help. Also; you might want to add a stop method on your connection that looks like:
public void Stop()
{
if (this.connection.IsAlive)
{
this.stopCondition = true;
this.connection.Join();
}
}
where stopCondition is a class member that is checked in your while loop (instead of just 'true').
The code in you Main() won't compile.
You need something like:
List<Connection> connections = new List<Connection> ();
Connection connect;
connect = new Connection();
connect.Start(1);
connections.Add(connect);
connect = new Connection();
connect.Start(2);
connections.Add(connect);
// etc
And then you can later do:
foreach(Connection connect in connections)
{
if(connect.SERVERID == 2)
{
//destroy the thread, and class.
}
}
For the actual stopping, I agree with SnOrfus' answer. You need to build in some logic to break the while-loop.