I'm working on creating web application, which will host in IIS, which I plan to accept a lot of short time connections. Right now I'm working on error logging, and it turns out that it's not that clear how to do it.
For example my back-end sql server went down and 1000 clients which try to get responses from server each 10 minutes, will flood in the event log of the server 60000 events in 1 hour. Can anyone share his experience on how not to flood the event log on the server?
The code is close to this:
RequestHandler :
try {
SqlConnection con = new SqlConnection(...);
cmd.CommandText = "SELECT TOP FROM Foo WHERE Bar = `"Some`"";
con.Open();
cmd.Connection = con;
// convert data to instance
}
catch (SqlException ex)
{
Logger.WriteErrorEvent(ex)
}
// convert instance to response and send it back.
Thanks,
Jenia.
As in another post - the best solution is to use your own files for logging. And I recommend to use log4net for this. It will take a while to get started, but once you learn it you'll see how useful it is. It will allow you to set different logging levels so you can log only information you need at that time and change them on the fly.
Instead of logging to Event log, You need to create a file and write all your errors to that file. Something like the following method, and you can call this method whenever you get any exception.
public static void SaveLog(string exc)
{
FileStream objFS = null;
string strFilePath = "Exception.log";
if (!File.Exists(strFilePath))
{
objFS = new FileStream(strFilePath, FileMode.Create);
}
else
objFS = new FileStream(strFilePath, FileMode.Append);
using (StreamWriter Sr = new StreamWriter(objFS))
{
Sr.WriteLine(System.DateTime.Now.ToShortTimeString() + "---" + exc);
}
}
Related
I'm trying to restore a database from a bak file. I found some code on how to do it grammatically but I'm not sure what I'm doing wrong. I'm getting an error:
Error:
Restore failed for Server 'www.freegamedata.com'.
I assume because i'm remotely connected? I'm not sure. The bak file is not on the server machine. I'm trying to build a desktop application that will install my database on the users server using my file. Here is my code:
private void Restore_Database()
{
try
{
Server server = new Server(Properties.Settings.Default.SQL_Server);
string filename = "Test.bak";
string filepath = System.IO.Directory.GetCurrentDirectory() + "\\file\\" + filename;
Restore res = new Restore();
res.Database = Properties.Settings.Default.SQL_Database;
res.Action = RestoreActionType.Database;
res.Devices.AddDevice(filepath, DeviceType.File);
res.PercentCompleteNotification = 10;
res.ReplaceDatabase = true;
res.PercentComplete += new PercentCompleteEventHandler(res_PercentComplete);
res.SqlRestore(server);
}
catch(Exception ex)
{
}
}
I'm not sure if I'm going about this the correct way. I'd like to add my database with my data to the users server as a base database. Am I doing something wrong? My connection string is good so I know its not a connection issue.
I have found a workaround for those whom do not have local access. This is a bit involved so I hope I explain this correctly and it makes sense.
Also note you will need to export your data to an excel spreadsheet before you do the steps listed below.
Exporting Data
Part 1:
Backup Your DATA!
This is a pretty simple process. Open SQL Management Studio and right click on your database. Choose export data and export it as an excel spreadsheet 2007. I'm not going to give detailed steps on this part because its pretty basic and you can google it. Sorry for the inconvenience.
Part 2:
Delete your database for testing purposes but make sure you have a working backup before you delete your database.
Importing Data
Part 1:
You need to create a script that will build your database for you automatically. You can do this by logging into SQL management Studio and right click on the database and choose:
Task -> Generate scripts
you should only need the default information. However, if your like me, I excluded the users in the list. This will generate a large SQL script.
Part 2:
Next you will want to store this file in your solution/project. Make sure you right click it and choose always copy or or copy if newer. I think that's the options. Basically it just copies your file when you debug or build it. This is critical because you will need to access this file to execute the script. Next you need to make a SQL function similar to mine to execute the script:
public bool SQLScript_ExecuteSQLScript(string ScriptLocation)
{
try
{
//5 min timeout
SqlConnection SQLConn = new SqlConnection(cn + "; Connection Timeout = 300;");
string script = File.ReadAllText(ScriptLocation);
Server server = new Server(new ServerConnection(SQLConn));
server.ConnectionContext.ExecuteNonQuery(script);
return true;
}
catch (Exception ex)
{
return false;
}
}
In my code sample please note I changed my timeout to 5 minutes. In the event you have a large script you may need to adjust the timeout to make sure your script fully executes.
Congrats you have rebuilt your database.
Part 3:
Load SQL Management Studio and make sure your database has been rebuilt successfully. You should see all your tables and Stored Procs but no data. If this is true, great you can continue. If not please go back and review your script. If you have SQL comments in your script, you may need to remove them. I had to in order for my script to execute without errors.
Part 4:
Now you need to import your data from your excel spreadsheet you created earlier. If your like me, you had multipal sheets. If you have multipal sheets then you will want to make a list to loop through each item in your list to import the sheets. If not then you can ignore my code on the list. I also put mine in a background worker but you don't need to depending on the size of your data. Also note I created a separate class containing my list but you dont have to do that if you don't want too. My sheet names are Table_1, Table_2 and Table_3 your will be differently most likely.
Sample Sheet List:
public List<string> GetTestTableList()
{
try
{
List<string> testlist = new List<string>();
testlist.Add("Table_1");
testlist.Add("Table_2");
testlist.Add("Table_3");
return testlist;
}
catch (Exception ex)
{
return null;
}
}
Part 5:
Next we will import the data from excel into SQL. This is a function I made but you can modify this to meet your needs.
Function:
private bool Import_Data_Into_SQL(string filepath, string SheetName, string Database, string Schema)
{
try
{
// sql table should match your sheet name in excel
string sqltable = SheetName;
// select all data from sheet by name
string exceldataquery = "select * from [" + SheetName + "$]";
//create our connection strings - Excel 2007 - This may differ based on Excel spreadsheet used
string excelconnectionstring = #"Provider=Microsoft.ACE.OLEDB.12.0; Data Source='" + filepath + " '; Extended Properties=Excel 8.0;";
string sqlconnectionstring = Properties.Settings.Default.SQL_Connection;
//series of commands to bulk copy data from the excel file into our sql table
OleDbConnection oledbconn = new OleDbConnection(excelconnectionstring);
OleDbCommand oledbcmd = new OleDbCommand(exceldataquery, oledbconn);
oledbconn.Open();
OleDbDataReader dr = oledbcmd.ExecuteReader();
SqlBulkCopy bulkcopy = new SqlBulkCopy(sqlconnectionstring);
bulkcopy.DestinationTableName = Database + "." + Schema +"." + sqltable;
while (dr.Read())
{
bulkcopy.WriteToServer(dr);
}
dr.Close();
oledbconn.Close();
return true;
}
catch (Exception ex)
{
return false;
}
}
I hope this helps. This was my workaround solution. Originally I wanted/tried to import my data using the .bak file but as pointed out above you can only do that if the sql server is local. So I hope this work around helps those who where faced with a similar issue as me. I'm not marking this as the answer because the above post answers the question but I'm posting this in case someone else needs this workaround. Thanks
Restore file must be on server. For installation use SQL script. This can be generated by SQL Server Management Studio (including data).
Right click on database. Choose "Tasks" - "Generate scripts". On second page of wizard choose "Advanced" and find "Types of data to script". Select "Schema and data" and save script to file.
Then use this code to run script on database
string scriptText = File.ReadAllText(scriptFile, Encoding.Default);
ExecuteBatch executeBatch = new ExecuteBatch();
StringCollection commandTexts = executeBatch.GetStatements(scriptText);
using (SqlConnection sqlConnection = new SqlConnection(conn))
{
sqlConnection.InfoMessage += SqlConnection_InfoMessage;
sqlConnection.Open();
for (int i = 0; i < commandTexts.Count; i++)
{
try
{
log.InfoFormat("Executing statement {0}", i + 1);
string commandText = commandTexts[i];
using (SqlCommand sqlCommand = sqlConnection.CreateCommand())
{
log.Debug(commandText);
sqlCommand.CommandText = commandText;
sqlCommand.CommandTimeout = 300;
int r = sqlCommand.ExecuteNonQuery();
log.DebugFormat("{0} rows affected", r);
}
}
catch (Exception ex)
{
log.Warn("Executing command failed", ex);
try
{
sqlConnection.Open();
}
catch (Exception ex2)
{
log.Error("Cannot reopen connection", ex2);
}
}
}
sqlConnection.Close();
}
We have two servers. An application server and a SQL server.
When running this simple program from the application server:
static void Main(string[] args)
{
OleDbCommand cmd;
OleDbConnection cnn;
string connectionString = "Provider=SQLNCLI10;Integrated Security=SSPI;User ID=***;Password=***;Persist Security Info=False;Initial Catalog=MCS_BATCH;Data Source=CD6S01;Initial File Name=;";
string sql = "EXEC [msp].[MasterMSP] #BTYPE = N'B_PVM_KNN', #AC_KEY = NULL, #RUN_TS = '2014-05-02 17:29:31.1400555', #CHUNK_ID = 8794";
System.IO.StreamWriter file = new System.IO.StreamWriter("MasterMSP_output.txt");
cnn = new OleDbConnection(connectionString);
try
{
cnn.Open();
cmd = new OleDbCommand(sql, cnn);
try
{
OleDbDataReader reader = cmd.ExecuteReader();
int numberOfFields = reader.VisibleFieldCount;
StringBuilder sb = new StringBuilder();
while (reader.Read())
{
for (int i = 0; i < (numberOfFields - 1); i++)
{
file.Write(reader[i].ToString());
}
file.WriteLine("");
}
file.Close();
}
catch (Exception ex)
{
file.Write("Execption ex at : {0}", System.DateTime.Now);
file.Write(ex.Message.ToString());
Console.WriteLine("Exception ex time is : {0}", System.DateTime.Now);
throw;
}
cmd.Dispose();
cnn.Close();
}
catch (Exception exx)
{
file.Write("Execption exx at : {0}", System.DateTime.Now);
file.Write(exx.Message.ToString());
Console.WriteLine("Exception exx time is : {0}", System.DateTime.Now);
throw;
}
}
We get - after some time - a "Protocol error in TDS stream" error:
We ran a network trace, and we can see that the "TCP Window size" is decreasing after 10 mins and then it's sending a TCP Window size = 0 (close window).
This means that the SQL server can't send more data until it has gotten a update window size, bigger than 0, from the application server.(right?):
The SQL server is trying to send 1 byte keepalive, but the application server is never responding. (The problem is that the application server never raise TCP Windows Size again. At the end the application server is terminating the session.)
We except that it's the application server fault and it could be that the networks buffer is not being empty(flushed) anymore. The only thing the TCP stack can do is to close the TCP Windows Size until the application again empties the buffer - but it never does that.
Any hints, ideas on what the issue can be?
The problem actually came up in 3rd party program. This program is calling a stored procedure on the SQL server. So I just tried to reproduce the logic in a C# program and was able to reproduce the error.
Any help, ideas or comments are highly appreciated.
Thanks
Adding this to the connectionstring fixed the error: MARS Connection=True
Link
I've written the below function, which errors out correctly with non-int input and with int input returns that the audit was started properly. Unfortunately when I check the table I see that the data was never actually inserted.
Any suggestions for what I'm doing wrong?
public string SqlLocation = "Data Source="+ new FileInfo(Path.GetDirectoryName(Application.ExecutablePath) + "\\DRAssistant.sdf");
public string StartAudit(string sqlLocation, string dps)
{
int dpsInteger;
if (!int.TryParse(dps, out dpsInteger))
return "DPS is not a number!";
try
{
var myConnection = new SqlCeConnection(sqlLocation);
myConnection.Open();
var myCommand = myConnection.CreateCommand();
myCommand.CommandText = string.Format("SELECT dps FROM DispatchReviews
WHERE dps = {0}", dpsInteger);
SqlCeDataReader reader = myCommand.ExecuteReader();
if (reader.Read())
{ return "DPS review has already started!"; }
myCommand.CommandText = "INSERT INTO DispatchReviews (dps, starttime,
reviewer) VALUES (#dps, #starttime, #reviewer)";
myCommand.Parameters.AddWithValue("#dps", dpsInteger);
myCommand.Parameters.AddWithValue("#starttime", DateTime.Now);
myCommand.Parameters.AddWithValue("#reviewer", Environment.UserName);
myCommand.Prepare();
myCommand.ExecuteNonQuery();
myCommand.Dispose();
myConnection.Close();
return "Dispatch Review Started!";
}
catch(Exception ex)
{ return "Unable to save DPS!" + ex.Message; }
}
Edit: Turns out this was just an idiot problem--which anybody looking at the SqlLocation could probably figure out--in that every time I built the application a new copy of the .sdf was copied into the application directory, overwriting the previous one. Also, the database I was checking for updates was not the one in the execution directory, but the one that was being copied into it, which is why it was always empty. I noticed this because when I tried to add the same DPS multiple times the first time I would get the DPS review started message, but subsequent attempts would give the error that it had previously been created.
Can you please show us your connection string??
Most likely, if you test this inside Visual Studio, the database file is being copied around (from your initial directory to the output directory where the app runs) and your INSERT will probably work just fine - but you're just looking at the wrong file when you check that fact.
I'm trying to teach myself c#, and have found various examples on connecting to a MSSQL database. What I've done seems to be the simplest way to do it, but still seems overly complicated.
Is there a better way?
here's my code:
static void dbcon()
{
List<int> familyID = new List<int>();
String connString = "Server=[myServer]\\[myInstance];Database=[dbName];User Id=[userID};Password=[password];";
using (var sqlconn = new SqlConnection(connString))
{
using (var cmd = sqlconn.CreateCommand())
{
try
{
sqlconn.Open();
cmd.CommandText = "SELECT id FROM family";
using (var reader = cmd.ExecuteReader())
{
while (reader.Read())
{
familyID.Add(Convert.ToInt32(reader["id"].ToString()));
}
}
foreach (int tempy in familyID)
{
Console.WriteLine("id: " + tempy);
}
}
catch (Exception ex)
{
Console.WriteLine(ex);
}
}
}
}
This is fine for an app that only runs one sql statement, but you wouldn't want to use all that code every time you wanted new data.
What you want to do is separate the code that creates the connection, from the code that gets and runs the sql, from the code that deals with the results.
This way, the connection code (and possibly the data display code) can be written once and called each time you want to execute different sql, and you only have to concentrate on how to write the code that gets the data you want.
hth
Details: First of all welcome to Stackoverflow. Just a few tips below
Having your connection string hard coded like that is bad practice. You should ALWAYS have it in your App.config (or Web.config if it is a web application). The reason is because if you have it hard coded and your boss ask you to change the Applications Database connection string you will need to recompile the entire application. If you have it in a App.config file you just need to change it (open it up with notepad) and save it.
Example on how to add it to the app.config
<configuration>
<connectionStrings>
<add name="myConnectionString"
connectionString="Data Source=localhost;Initial Catalog=MySQLServerDB;
Integrated Security=true" providerName="System.Data.SqlClient" />
</connectionStrings>
</configuration>
Then to access it in your code (You will need to add a reference to System.Configuration as well as add using System.Configuration;)
string connString = ConfigurationManager.ConnectionStrings["myConnectionString"].ConnectionString;
In regards to your other code I would change your exception catching to include the Sql Exception first and then fall back to any other exceptions.
catch (SqlException ex)
{
// Handle the Sql Exception code
}
catch (Exception ex)
{
// Handle the Normal Exception code
}
The example contained in the docs for SqlConnectionStringBuilder is quite easy to follow and understand as an alternative way.
System.Data.SqlClient.SqlConnectionStringBuilder builder =
new System.Data.SqlClient.SqlConnectionStringBuilder();
builder["Data Source"] = "(local)";
builder["integrated Security"] = true;
builder["Initial Catalog"] = "AdventureWorks;NewValue=Bad";
Console.WriteLine(builder.ConnectionString);
Edit:
Actually, the example I copied above shows you how the SqlConnectionStringBuilder class handles an "...invalid value in a safe manner". Whoops. At least it gives you an idea of how it works.
For more info on the various methods of obtaining, storing and constructing your connection string within ADO.NET, look at the MSDN documentation on Connection Strings
when upload the same file for the multiple times i am getting this error......
"The process cannot access the file 'd:\MarketingSystem\ExcelImport\Sample.xls' because it is being used by another process."
getting error in this line
RevenueDumpFileUpload.PostedFile.SaveAs(Server.MapPath(strFilePathOnServer) + RevenueDumpFileUpload.FileName);
This is my full code.....
protected void btnImport_Click(object sender, EventArgs e)
{
if (RevenueDumpFileUpload.HasFile)
{
string strFilePathOnServer = ConfigurationManager.AppSettings["RevenueDumpFileLocation"];
String sConnectionString = #"Provider=Microsoft.Jet.OLEDB.4.0;Data Source=" + Server.MapPath(strFilePathOnServer) + RevenueDumpFileUpload.FileName + ";Extended Properties=Excel 8.0;";
string strPostedFileName = RevenueDumpFileUpload.PostedFile.FileName;
if (strPostedFileName != string.Empty && RevenueDumpFileUpload.PostedFile.ContentLength != 0)
{
//Delete Old file before uploading new file.
if (System.IO.File.Exists(strFilePathOnServer))
{
System.IO.File.Delete(strFilePathOnServer);
}
//Save-Upload File to server.
RevenueDumpFileUpload.PostedFile.SaveAs(Server.MapPath(strFilePathOnServer) + RevenueDumpFileUpload.FileName);
RevenueDumpFileUpload.FileContent.Dispose();
}
OleDbConnection Exlcon = new OleDbConnection(sConnectionString);
try
{
Exlcon.Open();
}
catch
{
return;
}
finally
{
RevenueDumpFileUpload.PostedFile.InputStream.Flush();
RevenueDumpFileUpload.PostedFile.InputStream.Close();
}
OleDbCommand objCmdSelect = new OleDbCommand("SELECT * FROM [Sheet1$]", Exlcon);
OleDbDataAdapter objAdapter1 = new OleDbDataAdapter();
objAdapter1.SelectCommand = objCmdSelect;
objAdapter1.Fill(objDataset1, "XLData");
methodtosave();
}
}
In my web config file:
<appSettings>
<add key="RevenueDumpFileLocation" value="~/ExcelImport/"/>
How to resolve this?
Help me..
Thanks in advance
Well, if the OleDbConnection acts anything like the SqlConnection object, you've got this line:
Exlcon.Open();
which is opening the connection, but you don't have a matching line to close the connection. Which means the Jet database provider is going to continue to keep this file open until the connection object is garbage collected. It would be far better to wrap this line:
OleDbConnection Exlcon = new OleDbConnection(sConnectionString);
In a using statement, whose body extends over the remainder of the function, so that you're guaranteed that it's closed/disposed.
Next, have you considered what happens if multiple users upload files with the same name simultaneously - this method will be broken. It may be better to use a new file name on the server, related to the user ID or session ID, and wrap a try/finally around the whole method to ensure the file is deleted after use.
The above may be the cause of your current issues, if this is an error coming out of production - if two people attempt an upload at the same time, then both of their requests may go past the "delete if it exists" part of the code, then one request manages to save the file and open a connection, then the other request will fall over when trying to save the same file name.
You forget to pass the File Name and File Extension when you are trying to delete the file.
if (System.IO.File.Exists(Server.MapPath(strFilePathOnServer) + strPostedFileName+
System.IO.Path.GetExtension(RevenueDumpFileUpload.FileName)))
{
System.IO.File.Delete(Server.MapPath(strFilePathOnServer) + strPostedFileName +
System.IO.Path.GetExtension(RevenueDumpFileUpload.FileName) );
}