SqlConnection.Open fails randomly - sql-server

I have been facing a strange issue. I have a code snippet like below:
SqlTransaction transaction = null;
SqlDataReader reader = null;
using (SqlConnection sqlConnection = new SqlConnection(_sqlEndpoint.ConnectionString))
{
sqlConnection.Open(); **// Randomly fails here**
try
{
switch (_sqlEndpoint.ResultType)
{
case ResultSetType.None:
transaction = sqlConnection.BeginTransaction();
using (SqlCommand command = new SqlCommand(_sqlEndpoint.Query, sqlConnection, transaction))
{
command.CommandTimeout = _sqlEndpoint.CommandTimeout;
int rowsAffected = command.ExecuteNonQuery();
exchange.Message.SetHeader("RowsAffected", rowsAffected);
}
break;
case ResultSetType.Single:
using (SqlCommand command = new SqlCommand(_sqlEndpoint.Query, sqlConnection))
{
command.CommandTimeout = _sqlEndpoint.CommandTimeout;
exchange.Message.Body = command.ExecuteScalar();
}
break;
default:
using (SqlCommand command = new SqlCommand(_sqlEndpoint.Query, sqlConnection))
{
command.CommandTimeout = _sqlEndpoint.CommandTimeout;
reader = command.ExecuteReader(CommandBehavior.CloseConnection);
exchange.Message.Body = reader;
}
break;
}
}
catch (SqlException ex)
{
Log.ErrorFormat("[{0}] Error occured while fetching data from sql server: {1}", _sqlEndpoint.RouteName, ex.Message);
}
}
The connection opens 9 out of 10 times and then fails with the following exception:
"The operation is not valid for the state of the transaction"
Again is able to connect the 11th time and then fails randomly the 19th time or so. This doesnt happen in the dev environment, only in production.
I have tried/checked the following:
Changing the connection string to use UserId and Password creds instead of SSPI to eliminate any AD related issues
Tried Clearing the specific connection from the pool by calling SqlConnection.ClearPool(conn) so that a new connection is made rather than getting from the pool
Checked that MSDTC service is running on the production server
I am running out of ideas on this one. I am not even sure how to reproduce this error on the dev machine.
Any help or pointers will be appreciated.
Thanks

Related

EF Core migrations on a new SQL Server

I'm trying to build a Migration console app, that is able to start from scratch, i.e. on a freshly installed SQL Server, including a dedicated user with credentials, which should be the db_owner.
I have it working on PostgreSQL, but need to get a similar solution for SQL Server.
Before I call context.Database.Migrate() , I'm calling CheckDatabaseCreated(context.Database, configuration) which basically does this:
Tries to connect with given connectionstring
If it fails it replaces user, and password with SA and SA password
and connects to master.
Creates the login, if it does not exist.
Creates the database, if it does not exist.
Connects to the newly created database - still as SA.
Creates the user with the login, and adds db_owner role.
Finally it tries again to connect using the original connectionstring - this is where it fails.
Using HeidiSQL, I can see that the database is created, but I can only connect using SA credentials.
private static void CheckDatabaseCreated(DatabaseFacade contextDatabase, IConfiguration configuration)
{
bool canConnect;
try
{
canConnect = contextDatabase.CanConnect();
Console.WriteLine("Database connected succesfully.");
}
catch (Exception e)
{
Console.WriteLine($"Unable to connect to database: {e.Message}");
canConnect = false;
}
if (!canConnect)
{
var builder = new SqlConnectionStringBuilder(configuration["ConnectionString"]);
var originalUser = builder.UserID;
var originalPassword = builder.Password;
var originalDatabase = builder.InitialCatalog;
builder.UserID = _masterUsername;
builder.Password = _masterPassword;
builder.InitialCatalog = "master";
var login = $"{originalUser}Login";
SqlConnection conn = new SqlConnection(builder.ConnectionString);
try
{
conn.Open();
// Check if login exists
SqlCommand command = new SqlCommand($"SELECT COUNT(*) FROM master.sys.server_principals WHERE name = '{login}'", conn);
object result = command.ExecuteScalar();
result = (result == DBNull.Value) ? null : result;
if (Convert.ToInt32(result) < 1)
{
Console.WriteLine("Login does not exist - creating.");
command = new SqlCommand($"CREATE LOGIN [{login}] WITH PASSWORD = N'{originalPassword}', CHECK_POLICY = OFF, CHECK_EXPIRATION = OFF", conn);
command.ExecuteNonQuery();
}
// Check if database exists
command = new SqlCommand($"SELECT COUNT(*) FROM master.sys.databases WHERE name = '{originalDatabase}'", conn);
result = command.ExecuteScalar();
result = (result == DBNull.Value) ? null : result;
if (Convert.ToInt32(result) < 1)
{
Console.WriteLine("Database does not exist - creating.");
command = new SqlCommand($"CREATE DATABASE \"{originalDatabase}\" ", conn);
command.ExecuteNonQuery();
}
conn.Close();
// Now connect to the (newly created) database - still as sa.
builder.InitialCatalog = originalDatabase;
conn = new SqlConnection(builder.ConnectionString);
try
{
conn.Open();
command = new SqlCommand($"CREATE USER [{originalUser}] FOR LOGIN [{login}]", conn);
command.ExecuteNonQuery();
command = new SqlCommand($"EXEC sp_addrolemember 'db_owner', '{originalUser}'", conn);
command.ExecuteNonQuery();
conn.Close();
}
catch (Exception e)
{
Console.WriteLine($"Unable to connect to {originalDatabase} database: {e.Message}");
}
// Finally try to connect as the user created above.
builder = new SqlConnectionStringBuilder(configuration["ConnectionString"]);
conn = new SqlConnection(builder.ConnectionString);
try
{
conn.Open();
}
catch (Exception e)
{
// This is where it fails.
Console.WriteLine($"Unable to connect to database: {e.Message}");
}
}
catch (Exception e)
{
Console.WriteLine($"Unable to connect to database: {e.Message}");
}
}
}
The User ID in a SQL Server connection string refers to a Login, or a Contained Database User.
So your problem is here:
var login = $"{originalUser}Login";
This login is not the one referenced in your connection string. There's no reason the Login and Database User need different names. So jut make them the same:
var login = originalUser;

SqlBulkCopy Login Failed

I have an issue with SqlBulkCopy command when using SQL Server authentication. The issue does not arise with Windows authentication.
SqlBulkCopy sbc = new SqlBulkCopy(sqConn.ConnectionString, SqlBulkCopyOptions.KeepIdentity);
this throws an error:
Login failed for user 'xx'
Code:
SqlBulkCopy sbc = new SqlBulkCopy(sqConn);
This works fine but does not preserve identity column original values.
"persist security info=true" is required in the connection string. Otherwise password is stripped from sqlConn.ConnectionString if the connection is already open.
The solution is quite straightforward but I am still interested to know why SQL server authentication should be different from Windows authentication.
using (SqlTransaction transaction =
sqConn.BeginTransaction())
{
SqlBulkCopy sbc = new SqlBulkCopy(sqConn,SqlBulkCopyOptions.KeepIdentity,transaction);
sbc.DestinationTableName = file;
sbc.BatchSize = 1000;
sbc.NotifyAfter = 1000;
sbc.SqlRowsCopied += new SqlRowsCopiedEventHandler(OnSqlRowsCopied);
sbc.WriteToServer(SourceTable);
transaction.Commit();
}
Try this it worked for me
private static void BulkInsert(DataTable dtExcel, SqlConnection con)
{
try
{
{
if (con.State == ConnectionState.Closed)
con.Open();
var sqlTransactionScope = con.BeginTransaction();
//Open bulkcopy connection.
using (SqlBulkCopy bulkcopy = new SqlBulkCopy(con, SqlBulkCopyOptions.Default, sqlTransactionScope))
{
//Set destination table name
bulkcopy.BulkCopyTimeout = 0;
bulkcopy.BatchSize = 1000;
bulkcopy.DestinationTableName = "[dbo].[cc_alertowner]";
try
{
foreach (DataColumn col in dtExcel.Columns)
{
bulkcopy.ColumnMappings.Add(new SqlBulkCopyColumnMapping(col.ColumnName, col.ColumnName));
}
// bulkcopy.ColumnMappings.Add(new SqlBulkCopyColumnMapping("", ""));
// bulkcopy.ColumnMappings.Add(new SqlBulkCopyColumnMapping("DateCreated", "DateCreated"));
if (con.State == ConnectionState.Closed)
con.Open();
bulkcopy.WriteToServer(dtExcel);
sqlTransactionScope.Commit();
}
catch (Exception ex)
{
sqlTransactionScope.Rollback();
throw;
}
}
}
}
catch (Exception ex)
{
throw ex;
}
}
Looks Like your SQL Server Mixed Mode authentication is turned off.
Right Click your DB instance and select Properties.
Click on Security and in Server Authentication select second radio button SQL Server and Windows Authentication Mode.
After this Please restart SQL service from services.msc

Nunit database rollback

I'm fairly new to using Nunit as a test framework and have come across something I don't really understand.
I am writing an integration test which inserts a row into a database table. As I want to repeatedly run this test I wanted to delete the row out once the test has completed. The test runs fine, however when I look in the database table the row in question is still there even though the delete command ran. I can even see it run when profiling the database whilst running the test.
Does Nunit somehow rollback database transaction? If anyone has any ideas why I am seeing this happen please let me know. I have been unable to find any information about Nunit rolling back transactions which makes me think I'm doing something wrong, but the test runs the row appears in the database it is just not being deleted afterwards even though profiler shows the command being run!
Here is the test code I am running
[Test]
[Category("Consultant split integration test")]
public void Should_Save_Splits()
{
var splitList = new List<Split>();
splitList.Add(new Split()
{
UnitGroupId = 69,
ConsultantUserId = 1,
CreatedByUserId = 1,
CreatedOn = DateTime.Now,
UpdatedByUserId = 1,
UpdatedOn = DateTime.Now,
Name = "Consultant1",
Unit = "Unit1",
Percentage = 100,
PlacementId = 47
});
var connection = Helpers.GetCpeDevDatabaseConnection();
var repository = new Placements.ConsultantSplit.DAL.PronetRepository();
var notebookManager = Helpers.GetNotebookManager(connection);
var userManager = Helpers.GetUserManager(connection);
var placementManager = Helpers.GetPlacementManager(connection);
var sut = new Placements.ConsultantSplit.ConsultantSplitManager(repository, connection, notebookManager, userManager, placementManager);
IEnumerable<string> errors;
sut.SaveSplits(splitList, out errors);
try
{
using (connection.Connection)
{
using (
var cmd = new SqlCommand("Delete from ConsultantSplit where placementid=47",
connection.Connection))
{
connection.Open();
cmd.CommandType = CommandType.Text;
connection.UseTransaction = false;
cmd.Transaction = connection.Transaction;
cmd.ExecuteNonQuery();
connection.Close();
}
}
}
catch (Exception exp)
{
throw new Exception(exp.Message);
}

Elapsed time and CPU time are incorrect when run from external application

Is there any improvements or magical way when a SqlCommand is executed from a .net console application ?
I see huge improvements and consistent times when running the command from .net based application but its completely different when executed in Sql server management studio.
.NET Code
static void Main(string[] args)
{
try
{
string strConn = "Data Source=.;Initial Catalog=school;Integrated Security=True";
using (SqlConnection conn = new SqlConnection(strConn))
{
conn.Open();
SqlCommand command = new SqlCommand();
command.CommandType = System.Data.CommandType.Text;
command.CommandText = "SET NOCOUNT OFF;SET STATISTICS IO,TIME ON; Exec spStudents #type='SELECTALL';SET STATISTICS IO,TIME OFF;";
conn.InfoMessage += conn_InfoMessage;
command.Connection = conn;
using (var reader = command.ExecuteReader())
{
do
{
while (reader.Read())
{
//grab the first column to force the row down the pipe
var x = reader[0];
}
} while (reader.NextResult());
}
Console.Read();
}
}
catch (Exception ex)
{
throw;
}
}
static void conn_InfoMessage(object sender, SqlInfoMessageEventArgs e)
{
foreach (SqlError item in e.Errors)
{
Console.WriteLine(item.Message);
}
}
I see sql reports consistent times of CPU time = 130 ~ 150 ms and elapsed time = 250ms ~ 300 ms. But when running same in SSMS it reports me CPU time = 900ms, elapsed time = 1s. So which one is reliable result ?
another related question that i asked yesterday SQL query performance statistics messages returned multiple times

How to retrieve data using JDBC

I have been trying with the following code.
The connection is being made. But the resultSet is coming as empty (not null), whereas there are a couple of entries (2 fields each) in the database for the same.
It does not enter the while condition. I'm new to JDBC, please help!
My code is:
import java.sql.*;
public class JDBCTest123
{
public static void main(String[] args)
{
System.out.println("oracle Connect Example.");
Connection conn = null;
String url = "jdbc:oracle:thin:#127.0.0.1:1521:XE";
String driver = "oracle.jdbc.driver.OracleDriver";
String userName = "system";
String password = "mumpymamai";
Statement stmt = null;
String query = "select * from table1";
try
{
Class.forName(driver);
conn = DriverManager.getConnection(url, userName, password);
stmt = conn.createStatement();
System.out.println("Connected to the database");
ResultSet rs = stmt.executeQuery(query);
while (rs.next())
{
System.out.println(rs.getString(1));
System.out.println(rs.getString(2));
}
conn.close();
System.out.println("Disconnected from database");
} catch (Exception e)
{
e.printStackTrace();
}
}
}
And the output is:
oracle Connect Example.
Connected to the database
Disconnected from database
So few suggestions. I recommend to you use PreparedStatements which are more faster and safer.
PreparedStatement ps = null;
conn = DriverManager.getConnection(url, userName, password);
ps = conn.prepareStatement(query);
ResultSet rs = ps.executeQuery();
while (rs.next())
{
// do some work
}
Second suggestion, call close() method in finally block, because application may crash and then your connection won't be closed. Finally block guarantees that will be always called.
Third suggestion if it doesn't work without Exception, probably you have empty table.

Resources