SQL Server DAC FX SchemaComparison SchemaCompareDatabaseEndpoint fails with no IntegratedSecuirty - sql-server

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.

Related

Script task in SSIS package is executing but not performing the action

I have the two SSIS packages which basically has two action like below
First it truncates the contents of the table and then it executes the script task like basically call an API and inserts the response in to the table
[Microsoft.SqlServer.Dts.Tasks.ScriptTask.SSISScriptTaskEntryPointAttribute]
public partial class ScriptMain : Microsoft.SqlServer.Dts.Tasks.ScriptTask.VSTARTScriptObjectModelBase
{
public async void Main()
{
try
{
var sqlConn = new System.Data.SqlClient.SqlConnection();
ConnectionManager cm = Dts.Connections["SurplusMouse_ADONET"];
string serviceUrl = Dts.Variables["$Project::RM_ServiceUrl"].Value.ToString();
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12 | SecurityProtocolType.Tls11 | SecurityProtocolType.Tls;
HttpClient client = new HttpClient();
client.BaseAddress = new Uri(serviceUrl);
client.DefaultRequestHeaders.Accept.Add(
new MediaTypeWithQualityHeaderValue("application/json"));
string APIUrl = string.Format(serviceUrl + "/gonogo");
var response = await client.GetAsync(APIUrl);
if (response.IsSuccessStatusCode)
{
var result = await response.Content.ReadAsStringAsync();
try
{
sqlConn = (System.Data.SqlClient.SqlConnection)cm.AcquireConnection(Dts.Transaction);
const string query = #"INSERT INTO [dbo].[RM_Approved_Room_State]
(APPROVED_ROOM_STATEID,SOURCE_ROOMID,DEST_ROOMID,ENTITY_TYPEID)
SELECT id, sourceRoomRefId, destinationRoomRefId,entityRefId
FROM OPENJSON(#json)
WITH (
id int,
sourceRoomRefId int,
destinationRoomRefId int,
entityRefId int
) j;";
using (var sqlCmd = new System.Data.SqlClient.SqlCommand(query, sqlConn))
{
sqlCmd.Parameters.Add("#json", SqlDbType.NVarChar, -1).Value = result;
await sqlCmd.ExecuteNonQueryAsync();
}
}
catch (Exception ex)
{
Dts.TaskResult = (int)ScriptResults.Failure;
}
finally
{
if (sqlConn != null)
cm.ReleaseConnection(sqlConn);
}
}
}
catch (Exception ex)
{
Dts.TaskResult = (int)ScriptResults.Failure;
}
}
#region ScriptResults declaration
enum ScriptResults
{
Success = Microsoft.SqlServer.Dts.Runtime.DTSExecResult.Success,
Failure = Microsoft.SqlServer.Dts.Runtime.DTSExecResult.Failure
};
#endregion
} }
Similar to the above package I have another one which does more likely does insert records into different table the response from a different endpoint. When I execute the packages locally/ execute them separately after deploying it in to the server it works fine. But when I add them in to the SQL Server Agent Job like below and run them on a schedule
The Jobs run successfully and dont show any errors but I can see only one table with data from one package but the other one truncates the records but I dont think the script task is getting executed / I dont see any records inserted. I dont think there are any issues with access because when I run them seperate manually the data are getting inserted, Just when it is running on a schedule it is not working as expected. Any idea what could be happening here.. Any help is greatly appreciated

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;

Spring ldap unlocking an account

I am trying to unlock user account using spring ldap and getting the error message
""Malformed 'LockoutTime' attribute value" exception.
My code looks like below
public boolean unlockAccount(Name dn) {
ModificationItem item = new ModificationItem(DirContext.REPLACE_ATTRIBUTE, new BasicAttribute("lockoutTime", 0));
ldapTemplate.modifyAttributes(dn, new ModificationItem[] {item});
return true;
}
I am using Windows server 2016 and Spring ldap 2.3.2.
Is 'lockoutTime' the correct attribute to unlock an account ?
Is there anything else I am missing ?
In LDAP if you type the wrong password for more than 5 times, the account gets locked. If you want to unlock the user you have to delete an operational attribute name as pwdAccountLockedTime.
public String unlockUser(Users pvo) {
System.out.println("this is pvo" + pvo);
Name dn = buildDn(pvo);
DirContextOperations context = ldapTemplate.lookupContext(dn);
ModificationItem[] modificationItems;
modificationItems = new ModificationItem[1];
modificationItems[0] = new ModificationItem(DirContext.REMOVE_ATTRIBUTE,
new BasicAttribute("pwdAccountLockedTime"));
ldapTemplate.modifyAttributes(dn, modificationItems);
return "Account Unlocked";
}
build Dn for your LDAP and use the above code then the user gets unlocked.
String[] attrIDs = new String[] { "lockoutTime", "sAMAccountName",
"distinguishedName","pwdLastSet", "accountExpires", "userAccountControl",
"IsAccountLocked" };
ctls.setReturningAttributes(attrIDs);
ctls.setSearchScope(2);
String filter = "(&(objectClass=user)(objectCategory=Person)(sAMAccountName=" +
samaccountname+ "))";
NamingEnumeration<SearchResult> answer = ctx.search(adManagedOU, filter,ctls);
while (answer.hasMore()) {
SearchResult rs = answer.next();
Attributes attrs = rs.getAttributes();
distinguishedName = rs.getNameInNamespace();
String[] lockouttime = null;
String lockOutValue=attrs.get("lockoutTime");
if (lockOutValue != null)
{
lockouttime = attrs.get("lockoutTime").toString().split(":");
if (Long.valueOf(lockouttime[1].trim()) > 0) {
ModificationItem[] mods1 = new ModificationItem[] {
new ModificationItem(2, new BasicAttribute("lockoutTime", "0") )
};
((DirContext) ctls).modifyAttributes(distinguishedName, mods1);
}
else
{
LOGGER.info(username + " Account Not Locked");
}
The only values that may be set on lockouttime is to set the value to "0" which will effectively un-lock the account.
To learn more on Microsoft Active Directory Lockouts.
Setting the value to a String instead of an int makes this work, at least with AWS Simple AD.
ModificationItem item = new ModificationItem(DirContext.REPLACE_ATTRIBUTE, new BasicAttribute("lockoutTime", "0"));
ldapTemplate.modifyAttributes(dn, new ModificationItem[] {item});

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

Windows Mobile C# application with database

I am writing a prototype application in Windows Mobile 6.5 device.
The objective of the app is to ask user for some inputs, collect data and store into local database and on a server.
I am done with creating GUI (in C#) of the application which takes all the necessary inputs from user.
Now, I need to insert this data into local DB and upload to server DB. Both the DBs will need to synced over HTTP when user selects to do so. I have not worked on databases much, except for writing some queries to fetch data from PostgreSQL in the past in Linux environment a few years ago.
So my question is, what is the easiest way to achieve the thing I am trying to? I don't need lot of features. The data is only strings and numbers (no files, multimedia stuff etc.)
What server I should install and run? What components should I use on client side?
Thanks
Ashish
To use database on windows mobile you need Microsoft SQL Server Compact 3.5 for Windows Mobile . http://www.microsoft.com/en-in/download/details.aspx?id=8831 . You can download and install from the link given. After installation C:\Program Files\Microsoft SQL Server Compact Edition\v3.5\Devices\wce500\armv4i will have all CAB files that needs to be installed to your mobile.
Install
sqlce.ppc.wce5.armv4i.CAB
sqlce.repl.ppc.wce5.armv4i.CAB
For more information on what to install refer http://msdn.microsoft.com/en-us/library/bb986876.aspx
I have written a small helper class to do all database transactions.
public class DataBaseHelper
{
public enum typeOfQuery
{
insert,
update,
delete,
getScalar,
getDataSet,
getDataTable
};
private string connectionString = Program.Connection;
public object ExecuteDatabaseQuery(string query, Dictionary<string, object> dictionary, typeOfQuery typeOfQuery)
{
try
{
using (SqlCeConnection oCon = new SqlCeConnection(connectionString))
{
oCon.Open();
string oSql = query;
using (SqlCeCommand oCmd = new SqlCeCommand(oSql, oCon))
{
oCmd.CommandType = CommandType.Text;
if (dictionary != null)
{
if (dictionary.Count != 0)
{
foreach (KeyValuePair<string, object> pair in dictionary)
{
if (pair.Value is DateTime)
oCmd.Parameters.Add(pair.Key, SqlDbType.DateTime).Value = pair.Value ?? DBNull.Value;
else if (pair.Value is bool || pair.Value is Boolean)
oCmd.Parameters.Add(pair.Key, SqlDbType.Bit).Value = pair.Value ?? DBNull.Value;
else
oCmd.Parameters.Add(pair.Key, SqlDbType.NVarChar).Value = pair.Value ?? DBNull.Value;
}
}
}
// check what type of query using the enums in the constants.cs file
if ((typeOfQuery == (typeOfQuery.insert)) || (typeOfQuery == typeOfQuery.update) ||
(typeOfQuery == typeOfQuery.delete))
{
return oCmd.ExecuteNonQuery();
}
else if (typeOfQuery == typeOfQuery.getDataSet)
{
SqlCeDataAdapter adapter = new SqlCeDataAdapter(oCmd);
DataSet dataSet = new DataSet();
adapter.Fill(dataSet);
return dataSet;
}
else if (typeOfQuery == typeOfQuery.getDataTable)
{
SqlCeDataAdapter adapter = new SqlCeDataAdapter(oCmd);
DataSet dataSet = new DataSet();
adapter.Fill(dataSet);
return dataSet.Tables[0];
}
else if (typeOfQuery == typeOfQuery.getScalar)
{
object returnValue = oCmd.ExecuteScalar();
if (returnValue == null)
{
return string.Empty;
}
else
return returnValue;
}
}
}
}
catch (SqlCeException ex)
{
throw;
}
catch (Exception ex)
{
throw;
}
finally
{
}
return false;
}
}
You can call this class as follows
string query = #"SELECT * FROM TABLE
WHERE COL1 = #COL1";
Dictionary<string, object> dictionaryToInsert = new Dictionary<string, object>();
dictionaryToInsert.Add("#COL1", Col1Value);
return (DataTable)new DataBaseHelper().ExecuteDatabaseQuery(query,
dictionaryToInsert, DataBaseHelper.typeOfQuery.getDataTable);
Similarly you can query database for other purposes also. use the enum and change the query and you will get the result.

Resources