I'm making an application as follows: I have a webservice running on a local server, this webservice returns json like this:
[{"Id":1,"Titulo":"Live to win","Link":"https://www.youtube.com/embed/DPHlGVe8wxI","BandaId":1,"BandaNome":"Paul Stanley","GeneroId":1,"GeneroNome":"Rock","DtCriacao":"2017-03-23T16:42:54","CriadorId":1,"CriadorNome":"Márcio Eric","Ativo":false},{"Id":2,"Titulo":"Welcome to the jungle","Link":null,"BandaId":2,"BandaNome":"Guns n´ roses","GeneroId":1,"GeneroNome":"Rock","DtCriacao":"2017-03-23T16:42:54","CriadorId":2,"CriadorNome":"Usuário Teste","Ativo":true},{"Id":3,"Titulo":"Something just like this","Link":null,"BandaId":3,"BandaNome":"The Chainsmokers","GeneroId":3,"GeneroNome":"Indie","DtCriacao":"2017-03-23T16:42:54","CriadorId":1,"CriadorNome":"Márcio Eric","Ativo":true},{"Id":4,"Titulo":"Beliver","Link":null,"BandaId":4,"BandaNome":"Imagine Dragons","GeneroId":3,"GeneroNome":"Indie","DtCriacao":"2017-03-23T16:42:54","CriadorId":1,"CriadorNome":"Márcio Eric","Ativo":true},{"Id":5,"Titulo":"Radioactive","Link":null,"BandaId":4,"BandaNome":"Imagine Dragons","GeneroId":3,"GeneroNome":"Indie","DtCriacao":"2017-03-23T16:42:54","CriadorId":1,"CriadorNome":"Márcio Eric","Ativo":true},{"Id":6,"Titulo":"Friends - Original Mix","Link":null,"BandaId":5,"BandaNome":"Steener","GeneroId":2,"GeneroNome":"EDM","DtCriacao":"2017-03-23T16:42:54","CriadorId":2,"CriadorNome":"Usuário Teste","Ativo":true},{"Id":7,"Titulo":"Amanheceu","Link":null,"BandaId":6,"BandaNome":"Scalene","GeneroId":3,"GeneroNome":"Indie","DtCriacao":"2017-03-23T16:42:54","CriadorId":2,"CriadorNome":"Usuário Teste","Ativo":true},{"Id":8,"Titulo":"Sonhador II","Link":null,"BandaId":6,"BandaNome":"Scalene","GeneroId":3,"GeneroNome":"Indie","DtCriacao":"2017-03-23T16:42:54","CriadorId":2,"CriadorNome":"Usuário Teste","Ativo":true},{"Id":9,"Titulo":"Amianto","Link":null,"BandaId":7,"BandaNome":"Supercombo","GeneroId":3,"GeneroNome":"Indie","DtCriacao":"2017-03-23T16:42:54","CriadorId":1,"CriadorNome":"Márcio Eric","Ativo":true},{"Id":10,"Titulo":"Monstros","Link":null,"BandaId":7,"BandaNome":"Supercombo","GeneroId":3,"GeneroNome":"Indie","DtCriacao":"2017-03-23T16:42:54","CriadorId":1,"CriadorNome":"Márcio Eric","Ativo":true},{"Id":11,"Titulo":"Piloto Automático","Link":null,"BandaId":7,"BandaNome":"Supercombo","GeneroId":3,"GeneroNome":"Indie","DtCriacao":"2017-03-23T16:42:54","CriadorId":2,"CriadorNome":"Usuário Teste","Ativo":true},{"Id":12,"Titulo":"Eutanásia","Link":null,"BandaId":7,"BandaNome":"Supercombo","GeneroId":3,"GeneroNome":"Indie","DtCriacao":"2017-03-23T16:42:54","CriadorId":1,"CriadorNome":"Márcio Eric","Ativo":true},{"Id":13,"Titulo":"Shots - Broiler Remix","Link":null,"BandaId":4,"BandaNome":"Imagine Dragons","GeneroId":3,"GeneroNome":"Indie","DtCriacao":"2017-03-24T16:55:46","CriadorId":1,"CriadorNome":"Márcio Eric","Ativo":true}]
But now I need to create a Windows Services that communicates with this WebService and what it receives in a database, I spent all day doing this webservice in several ways, but I did not find anything about good practices with webservices or etc. I'll show you what I did, but I'd like some tips on windows services, thank you
Here is my code
Service:
private Timer _worker;
private readonly int _interval = Convert.ToInt32(ConfigurationManager.AppSettings["timer"]);
private readonly string _connection = ConfigurationManager.AppSettings["connection"];
//private readonly Connector _usuarioConnector;
private readonly Connector _bandaConnector;
//private readonly Connector _generoConnector;
private readonly Connector _musicaConnector;
private SqlConnection conn;
public Service1()
{
//_usuarioConnector = new Connector("UsuarioBase");
_bandaConnector = new Connector("BandaBase");
//_generoConnector = new Connector("GeneroBase");
_musicaConnector = new Connector("MusicaBase");
InitializeComponent();
}
protected override void OnStart(string[] args)
{
_worker = new Timer((Update), null, 0, _interval);
}
protected override void OnStop()
{
}
private void Update(Object state)
{
using (SqlConnection connection = new SqlConnection(
_connection))
{
SqlCommand command = new SqlCommand("insert into Genero(nome, descricao, dtcriacao, criadorid, ativo) values('teste', 'teste', getdate(), 1, 1)", connection);
command.Connection.Open();
command.ExecuteNonQuery();
EventLog.WriteEntry("Query executada", EventLogEntryType.Warning);
}
}
Loader
public class Loader
{
public static List<Usuario> LoadUsuarios(Connector usuarioConnector)
{
return (List<Usuario>)Newtonsoft.Json.JsonConvert.DeserializeObject(usuarioConnector.GetData(), typeof(List<Usuario>));
}
public static List<Banda> LoadBandas(Connector bandaConnector)
{
var bandasDtos = (List<BandaDto>)Newtonsoft.Json.JsonConvert.DeserializeObject(bandaConnector.GetData(), typeof(List<BandaDto>));
return bandasDtos.Select(dto => dto.ConvertToBanda()).ToList();
}
//public static List<Genero> LoadGeneros(Connector generoConnector)
//{
//var musicasDtos = (List<MusicaDto>)Newtonsoft.Json.JsonConvert.DeserializeObject(musicaConnector.GetData(), typeof(List<MusicaDto>));
//return musicasDtos.Select(dto => dto.ConvertToMusica()).ToList();
//}
public static List<Musica> LoadMusicas(Connector musicaConnector)
{
var musicasDtos = (List<MusicaDto>)Newtonsoft.Json.JsonConvert.DeserializeObject(musicaConnector.GetData(), typeof(List<MusicaDto>));
return musicasDtos.Select(dto => dto.ConvertToMusica()).ToList();
}
}
Connector
private string Host { get; }
private WebRequest _request;
public WebResponse Response;
public string ConnectionStatus;
private Stream _dataStream;
private StreamReader _reader;
public Connector(string config)
{
Host = ConfigurationManager.AppSettings[config];
}
public string GetData()
{
StartConnection();
_dataStream = Response.GetResponseStream();
_reader = new StreamReader(_dataStream);
var toReturn = _reader.ReadToEnd();
EndConnection();
return toReturn;
}
private void StartConnection()
{
_request = WebRequest.Create(Host);
_request.Credentials = CredentialCache.DefaultCredentials;
Response = _request.GetResponse();
ConnectionStatus = (((HttpWebResponse)Response).StatusDescription);
}
private void EndConnection()
{
_reader.Close();
Response.Close();
}
Edit1 In the current code it is doing a select, however in what I intend to do it will do inserts
I discovered, unfortunately the timer was not working, so I just changed it to a thread.
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'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);
}