I'm trying to create an installer file that installs a database using install shield 2015.
I'm following this link
Walkthrough: Using a Custom Action to Create a Database at Installation
Since this link refers to an older install shield I didn't managed to make it work. I have created an install file in my main windows form application, which you can see in the below code.
public partial class DeployInstaller : System.Configuration.Install.Installer
{
System.Data.SqlClient.SqlConnection masterConnection = new System.Data.SqlClient.SqlConnection();
public DeployInstaller()
{
InitializeComponent();
}
private string GetSql(string Name)
{
try
{
// Gets the current assembly.
Assembly Asm = Assembly.GetExecutingAssembly();
// Resources are named using a fully qualified name.
Stream strm = Asm.GetManifestResourceStream(Asm.GetName().Name + "." + Name);
// Reads the contents of the embedded file.
StreamReader reader = new StreamReader(strm);
return reader.ReadToEnd();
}
catch (Exception ex)
{
//Interaction.MsgBox("In GetSQL: " + ex.Message);
throw ex;
}
}
private void ExecuteSql(string Sql)
{
masterConnection.ConnectionString = "data source=.//SQLEXPRESS;initial catalog=master;integrated security=True; MultipleActiveResultSets=True; Application Name=EntityFramework";
System.Data.SqlClient.SqlCommand Command = new System.Data.SqlClient.SqlCommand(Sql, masterConnection);
// Initialize the connection, open it, and set it to the "master" database
Command.Connection.Open();
try
{
Command.ExecuteNonQuery();
}
finally
{
// Closing the connection should be done in a Finally block
Command.Connection.Close();
}
}
protected void AddDBTable()
{
try
{
// Creates the database.
//ExecuteSql("master", "CREATE DATABASE " + strDBName);
// Creates the tables.
ExecuteSql( GetSql("sql.txt"));
}
catch (Exception ex)
{
// Reports any errors and abort.
//Interaction.MsgBox("In exception handler: " + ex.Message);
throw ex;
}
}
public override void Install(System.Collections.IDictionary stateSaver)
{
base.Install(stateSaver);
AddDBTable();
}
}
Supposedly this piece of code provides the user with the ability of doing both the installation of the system and the corresponding database. For some reason only the system is being installed. When attempting to install the database, something is preventing from successfully completing. Unfortunately, I cannot pinpoint the reason why because it doesn't give out an error with the reason. It could be that its never being started or that I did something wrong.
Any guide would be appreciated.
Related
I have read the numerous posts on why you should give the using statement preference over manually doing .Open() then .Close() and finally .Dispose().
When I initially wrote my code, I had something like this:
private static void doIt(string strConnectionString, string strUsername)
{
SqlConnection conn = new SqlConnection(strConnectionString);
try
{
conn.Open();
string strSqlCommandText = $"CREATE USER {strUsername} for LOGIN {strUsername} WITH DEFAULT SCHEMA = [dbo];";
SqlCommand sqlCommand = new SqlCommand(strSqlCommandText, conn);
var sqlNonReader = sqlCommand.ExecuteNonQuery();
if (sqlNonReader == -1) Utility.Notify($"User Added: {strUsername}");
}
catch (Exception ex)
{
Console.WriteLine($"Error: {ex.Message}");
}
finally
{
conn.Close();
conn.Dispose();
}
}
and this works... no problem. but only ONCE.
so, if I do something like this:
private static void doItLots(string strConnectionString, string strUsername)
{
for(int i=0; i<10; i++)
{
doIt(strConnectionString, $"{strUsername}_{i}");
}
}
it works the FIRST time when i=0, but any subsequent iterations fail with Cannot open database "myDbName" requested by the login. The login failed.
However, if I go back and comment out the conn.Dispose(); line, then it works fine on all iterations.
The problem is simply that if I want to do the .Dispose() part outside of the method, then I am forced to pass a SqlConnection object instead of simply passing the credentials, potentially making my code a bit less portable and then I need to keep the connection around longer as well. I was always under the impression that you want to open and close connections quickly but clearly I'm misunderstanding the way the .Dispose() command works.
As I stated at the outset, I also tried doing this with using like this...
private static void doIt(string strConnectionString, string strUsername)
{
using (SqlConnection conn = new SqlConnection(strConnectionString))
{
try
{
conn.Open();
string strSqlCommandText = $"CREATE USER {strUsername} for LOGIN {strUsername} WITH DEFAULT SCHEMA = [dbo];";
SqlCommand sqlCommand = new SqlCommand(strSqlCommandText, conn);
var sqlNonReader = sqlCommand.ExecuteNonQuery();
if (sqlNonReader == -1) Utility.Notify($"User Added: {strUsername}");
}
catch (Exception ex)
{
Console.WriteLine($"Error: {ex.Message}");
}
finally
{
conn.Close();
}
}
}
and this does the exact same thing as the initial code with .Dispose() called manually.
Any help here would be greatly appreciated. I'd love to convert to the using statements but having trouble figuring out how to write reusable methods that way...
UPDATE:
I have narrowed it down a bit. The issue is NOT the iterations or making the calls over-and-over again. But I am still getting an access error. Here is the code:
string strConnectionString = $#"Data Source={StrSqlServerDataSource};Initial Catalog={StrDatabaseName};User id={StrSqlServerMasterUser};Password={StrSqlServerMasterPassword}";
using (SqlConnection connUserDb = new SqlConnection(strConnectionString))
{
try
{
Utility.Notify($"Connection State: {connUserDb.State.ToString()}"); // Responds as 'Closed'
connUserDb.Open(); // <-- throws error
Utility.Notify($"Connection State: {connUserDb.State.ToString()}");
Utility.Notify($"MSSQL Connection Open... Adding User '{strUsername}' to Database: '{strDatabaseName}'");
string sqlCommandText =
//$#"USE {StrDatabaseName}; " +
$#"CREATE USER [{strUsername}] FOR LOGIN [{strUsername}] WITH DEFAULT_SCHEMA = [dbo]; " +
$#"ALTER ROLE [db_datareader] ADD MEMBER [{strUsername}]; " +
$#"ALTER ROLE [db_datawriter] ADD MEMBER [{strUsername}]; " +
$#"ALTER ROLE [db_ddladmin] ADD MEMBER [{strUsername}]; ";
using (SqlCommand sqlCommand = new SqlCommand(sqlCommandText, connUserDb))
{
var sqlNonReader = sqlCommand.ExecuteNonQuery();
if (sqlNonReader == -1) Utility.Notify($"User Added: {strUsername} ({sqlNonReader})");
}
result = true;
}
catch (Exception ex)
{
Utility.Notify($"Creating User and Updating Roles Failed: {ex.Message}", Priority.High);
}
finally
{
connUserDb.Close();
Utility.Notify($"MSSQL Connection Closed");
}
}
return result;
}
The error I am getting here is: Cannot open database requested by the login. The login failed.
One clue I have is that prior to this, I was running this same code with two changes:
1) uncommented the USE statement in the sqlCommandText
2) connected to the Master database instead
When I did that, it didn't work either, and instead I got this error: The server principal is not able to access the database under the current security context.
If I go into SSMS and review the MasterUser they are listed as db_owner and I can perform any activities I want, including running the command included in the code above.
I rewrote all the code to make use of a single connection per the recommendations here. After running into the "server principal" error, I added one more connection to attempt to directly connect to this database rather than the master.
UPDATE 2:
Here is another plot twist...
This is working from my local computer fine (now). But, not (always) working when run from an Azure Webjob that targets an Amazon Web Services (AWS) Relational Database Server (RDS) running MSSQL.
I will have to audit the git commits tomorrow, but as of 5p today, it was working on BOTH local and Azure. After the last update, I was able to test local and get it to work, but when run on Azure Webjob it failed as outlined above.
SqlConnection implements IDisposable. You don't call dispose or close.
try{
using (SqlConnection conn = new SqlConnection(strConnectionString))
{
conn.Open();
string strSqlCommandText = $"CREATE USER {strUsername} for LOGIN {strUsername} WITH DEFAULT SCHEMA = [dbo];";
SqlCommand sqlCommand = new SqlCommand(strSqlCommandText, conn);
var sqlNonReader = sqlCommand.ExecuteNonQuery();
if (sqlNonReader == -1) Utility.Notify($"User Added: {strUsername}");
}}
catch (Exception ex)
{
Console.WriteLine($"Error: {ex.Message}");
}
I write a C# program which uses a database, Matches.mdf.
I want to test the database file existence, using File.Exists routine (codes will come at the end of the question). If the file doesn't exist, the program creates a new database with the above name. To test the database existence routine, I renamed the database file, but when I wanted to create the database, I got the following error message: Database "Matches" already exists, please specify a different name.
At a second test, I used a database dropping routine before calling the creating routine. Big mistake. Every time I try to create the Matches.mdf database, I get the following error message:
I am sure that the cause of this error message is me, tinkering around, because the same database creation and deletion routines worked fine before.
I know I can solve the problem by changing the path of the database file, but I want to know what exactly I broke up here so I know for next time.
What I am asking is: what can I do to solve the above error?
Later edit: I tried to manually recreate the Matches.mdf using the query tool from SQL Server Object Explorer from VS 2019. Worked perfectly, but I don't think it's a good solution long term.
Necessary codes:
Variable declarations:
static readonly string DatabaseFolder = Path.GetDirectoryName(Application.ExecutablePath) + "\\db";
readonly string DatabaseFile = DatabaseFolder + "\\Matches.mdf";
readonly string DatabaseLog = DatabaseFolder + "\\MatchesLog.ldf";
The function that checks the database file existence:
public bool DatabaseExists()
{
return File.Exists(DatabaseFile);
}
The database creation routine:
private bool CreateDatabaseFile()
{
SqlConnection MyConn = new SqlConnection(CreateDatabaseConnectionString);
string Str = "Create Database Matches on Primary (Name=Matches, Filename='#DatabaseFile') log on (Name=MatchesLog, Filename='#DatabaseLog')";
SqlCommand DatabaseCreationCommand = new SqlCommand(Str, MyConn);
DatabaseCreationCommand.Parameters.Add("#DatabaseFile", SqlDbType.Text).Value = DatabaseFile;
DatabaseCreationCommand.Parameters.Add("#DatabaseLog", SqlDbType.Text).Value = DatabaseLog;
try
{
MyConn.Open();
DatabaseCreationCommand.ExecuteNonQuery();
}
catch (SqlException S)
{
MessageBox.Show(S.Message);
return false;
}
catch (IOException I)
{
MessageBox.Show(I.Message);
return false;
}
catch (InvalidOperationException I)
{
MessageBox.Show(I.Message);
return false;
}
catch (InvalidCastException I)
{
MessageBox.Show(I.Message);
return false;
}
finally
{
MyConn.Close();
}
return true;
}
The database deleting routine:
public void DeleteDatabase()
{
string Str;
SqlConnection MyConn = new SqlConnection(CreateDatabaseConnectionString);
Str = "Alter database Matches set single_user with rollback immediate\r\ndrop database Matches";
SqlCommand command = new SqlCommand(Str, MyConn);
try
{
MyConn.Open();
command.ExecuteNonQuery();
}
catch (SqlException S)
{
MessageBox.Show(S.Message);
}
catch (IOException I)
{
MessageBox.Show(I.Message);
}
catch (InvalidOperationException I)
{
MessageBox.Show(I.Message);
}
catch (InvalidCastException I)
{
MessageBox.Show(I.Message);
}
finally
{
MyConn.Close();
}
}
As it is said here and confirmed by Jeroen Mostert, Create database does not accept queries. The database was created before, using some string concatenation. Afterwards the query string was parametrized, without realizing that this command doesn't take parameters. This is why changing the creating database query to
Create Database Matches
works perfectly.
Well, live and learn!
I have problem with connecting a database to my UWP.
public sealed partial class MainPage : Page
{
string connstring;
public MainPage()
{
this.InitializeComponent();
connstring = #" Data Source = tcp:stdgydlcdk.database.windows.net;Initial Catalog=doc4cloud_db;User ID=****;Password = ****; version = 3";
}
private void Add_Click(object sender, RoutedEventArgs e)
{
using (SqliteConnection con = new SqliteConnection(connstring))
try
{
con.Open();
if(con.State == System.Data.ConnectionState.Open)
{
NewBlogUrl.Text = "succes";
}
}
catch(Exception ex)
{
NewBlogUrl.Text = ex.Message +ex.InnerException;
}
}
}
(instead of stars in connection string, I have valid ID and password)
As you can see in my simple test program here, I am trying to make a connection to a database and print stuff depending on result. The poject runs how it is supposed to but the problem comes when the click action is called. First exception is a ArgumentException, which say that keywords Initial Catalog= , User ID= , Password = and version = ,from my connection string ,are not supported,and if I remove one of them, the Exception is thrown for the next one, the second problem is, if I remove all of them(even if I should not), I get rid of ArgumentException but, I get a connection exception now, The given path's format is not supported to be more specific. I have all necesary dlls
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseSqlite("Data Source = tcp:stdgydlcdk.database.windows.net;Initial Catalog=doc4cloud_db;User ID=****;Password = ****; version = 3");
}
Here I made a model to get acces to my database. Maybe I did something wrong in my model, maybe connection string declaration is wrong, I am not sure. Any feedback is welcome. Thank you very much in advance.
I got two problems:
First, i'm trying to connect my windows form app with my embedded database (.dbf) and i keep getting this message no matter what i do to the connection string:
"error isam instalable cant be found"
Second, i would like to make the path relative to the executable.
Thanks, here is the code i'm using to test the whole thing:
private void bGuardar_Click(object sender, EventArgs e)
{
try
{
string cadena = "Provider = Microsoft.Jet.OLEDB.4.0; Data Source =D:\\; Extended Properties = dBASE IV; UserID =; Password =;";
OleDbConnection con = new OleDbConnection();
con.ConnectionString = cadena;
con.Open();
MessageBox.Show("conected");
con.Close();
}
catch (OleDbException exp)
{
MessageBox.Show("Error: " + exp.Message);
}
}
For the second part, you can get the path of your executable using System.IO.Path.GetDirectory(Application.ExecutablePath). There are more ways do this based upon your need (see Best way to get application folder path).
To avoid further difficulties instead of
'OleDbConnection con = new OleDbConnection();'
try
using (OleDbConnection con = new OleDbConnection())
{
; // your command and executes here
}
this way you call the dispose / close method always (using generally wraps up your code so that the part between { and } is wrapped in a try / catch block with finally that calls a dispose() / close() on the OleDbConn object.
I have this method:
static void DbConnect()
{
// Connects to the Db using a simplified Connection string.
try
{
mySqlConnect = new MySqlConnection("server=" + Setup_Controller.server + ";uid=root;");
mySqlConnect.Open();
}
// If a password is required, tries again using the password provided.
catch (MySqlException)
{
mySqlConnect = new MySqlConnection("server=" + Setup_Controller.server + ";uid=root;password='" + Setup_Controller.password + "';");
mySqlConnect.Open();
}
}
However every time it catches an exception, it won't load open the connection because of the current exception caused by the first connection attempt. I am beginning to wonder whether it's because the try/catch is not allowing the program to continue, because there is an unhandled exception in play?
If you really want to use this method, you need to first close your connection before you can use it again.
static void DbConnect()
{
// Connects to the Db using a simplified Connection string.
try
{
mySqlConnect = new MySqlConnection("server=" + Setup_Controller.server +
";uid=root;");
mySqlConnect.Open();
}
// If a password is required, tries again using the password provided.
catch (MySqlException)
{
//make sure connection is close
if(mySqlConnection != null)
mySqlConnect.Close();
}
/* i don't know what this class looks like but most data class expose some method
to see if the connection is still open or close. If there is not an IsOpen check
for IsClose or something like this
*/
if(!mySqlConnect.IsOpen())
{
try
{
mySqlConnect = new MySqlConnection("server=" + Setup_Controller.server +
";uid=root;password='" + Setup_Controller.password + "';");
mySqlConnect.Open();
}
catch (MySqlException)
{
//make sure connection is close
mySqlConnect.Close();
}
}