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;
Related
How to get data from sql DB in selenium webdriver?
I would like to connect the selenium webdriver and sql DB, and need to get value from DB and to use in the selenium testNg framework.
Can any one provide me the right solution.
First you need to make connection with database by using following commands,
DriverManager.getConnection(URL of database, "username", "password" )
To get value, use following commands
ResultSet result = stmt.executeQuery(select * from tablename;);
You need to implement DB connector helper for connect, execute query and close data base connection. After than you can use result of query in your test.
Data Base connector depends on DB type(you need use specify DB driver).
Folowing java methodis illustrated connection with SQL Server Driver:
public java.sql.Connection getConnection() {
try {
Class.forName(SQLServerDriver.class.getName());
con = java.sql.DriverManager.getConnection(getConnectionUrl(), userName, password);
if (con != null) System.out.println("Connection Successful!");
} catch (Exception e) {}
return con;
}
and execute query:
public void executeQuery(String query) {
con = this.getConnection();
if (con != null) {
Statement st = null;
try {
st = con.createStatement();
st.executeQuery(query);
} catch (SQLException e) {}
}
this.closeConnection();
}
than close connection:
public void closeConnection() {
try {
if (con != null)
con.close();
con = null;
} catch (Exception e) {}
}
I am trying to find newly created tables in the target database on publishing. Using DAC Fx I am able to find the differences and delete the tables after moving the newly created table to another db.
I developed and tested the code with IntegratedSecurity. Started failing on machines with SQLServer logins.
The moment I toggle the IntegratedSecurity to true it works. Is it a bug?
private void Analyse()
{
try
{
var sourceDacpac = new SchemaCompareDacpacEndpoint(DacPacSrc);
var csb = new SqlConnectionStringBuilder(ConnectionString);
csb.IntegratedSecurity = false;
var targetDatabase =new SchemaCompareDatabaseEndpoint(csb.ToString());
var comparison = new SchemaComparison(sourceDacpac, targetDatabase);
comparison.Options.DropObjectsNotInSource = true;
var result = comparison.Compare();
if (result.GetErrors().Any())
{
throw new Exception("Compare failed " + result.GetErrors().FirstOrDefault().Message);
}
var delta = new List<string>();
if (result.Differences != null && result.Differences.Any())
{
var deltaTables = result.Differences.Where(x => x.Name == "Table" && x.UpdateAction == SchemaUpdateAction.Delete);
delta = deltaTables.Select(x => x.TargetObject.Name.ToString()).ToList();
}
FindingDeltaCompleted?.Invoke(this, new DeltaEventArgs(delta));
}
catch (Exception ex)
{
Logging.HandleException(ex);
}
}
Try setting Persist Security Info=True in the SQL Authentication connection string.
SSDT/DAC Fx saves connection strings in registry under HKEY_CURRENT_USER\SOFTWARE\Microsoft\SSDT\ConnectionStrings. When Persist Security Info=True is not set, it won't restore the password when loading the connection strings from registry.
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
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
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.