I've written a generic database class which can be called to carry out common database (CRUD) operations to save re-writing the ADO.NET code in multiple solutions. To make this flexible, there are a number of constructor overloads based on the different database authentication types and instance types etc. The class is as follows:
class Database
{
// default instance with Windows authentication
// constructor 1
public Database(string server, string database, bool persistSecurityInfo)
{
_server = server;
_database = database;
_persistSecurityInfo = persistSecurityInfo;
_integratedSecurity = "True";
_connectionString = "Data Source=" + server + ";Initial Catalog=" + database + ";Persist Security Info=" + persistSecurityInfo.ToString() + ";Integrated Security=True";
}
// named instance using Windows authentication
// constructor 2
public Database(string server, string instance, string database, bool persistSecurityInfo) : this(server, database, persistSecurityInfo)
{
_instance = instance;
_integratedSecurity = "True";
_connectionString = "Data Source=" + server + "\\" + instance + ";Initial Catalog=" + database + ";Persist Security Info=" + persistSecurityInfo.ToString() + ";Integrated Security=True";
}
// default instance with SQL authentication
// constructor 3
public Database(string server, string database, bool persistSecurityInfo, string userName, string password) : this(server, database, persistSecurityInfo)
{
_userName = userName;
_password = password;
_integratedSecurity = "False";
_connectionString = "Data Source=" + server + ";Initial Catalog=" + database + ";Persist Security Info=" + persistSecurityInfo.ToString() + ";User ID=" + userName + ";Password=" + password;
}
// named instance with SQL authentication
// constructor 4
public Database(string server, string instance, string database, bool persistSecurityInfo, string userName, string password) : this(server, database, persistSecurityInfo, userName, password)
{
_instance = instance;
_integratedSecurity = "False";
_connectionString = "Data Source=" + server + "\\" + instance + ";Initial Catalog=" + database + ";Persist Security Info=" + persistSecurityInfo.ToString() + ";User ID=" + userName + ";Password=" + password;
}
private string _server;
private string _instance;
private string _database;
private bool _persistSecurityInfo;
private string _userName;
private string _password;
private string _integratedSecurity;
private string _connectionString;
private string _query;
//CRUD Methods here
}
I have written a console application which is writing to a database. When the application is executed, the user provides some command line switches.
Some of the switches are as follows (There are others relating to the program's operation which I have not included here):
/s : database server name
/i : database instance name
/d : database name
/n : integrated security (True or False)
/u : db Username
/p : db Password
/i, /u and /p are optional (EG if an instance name isn't supplied, the program assumes it is to connect to a default instance on /s)
Therefore, I need the program to, at run time decide which constructor to call based on which arguments have been provided.
pseudo example here
Class Program
{
static void Main(string[] args)
{
foreach (string arg in args[])
{
//code to work out which parameters have been provided here and adds them to array. Also other code which checks integrity such as ensuring there is no username without a password and vice versa etc.
string[] suppliedParameters;
//if there is a /i , /u , /p parameters, use constructor 4
//if there is a /u and /p but no /i, use constructor 3
//if there is an /i but no /u or /n use constructor 2
//if there is no /i, /u or /n, use constructor 1
}
}
}
I know I can use reflection to execute the relevant constructor and that I could achieve selection of the constructor using a switch statement in the Main method which carries out the tests in the logic above but am just wondering if there is a maybe a more elegant way to do this?
If you want to use Reflection then, use Activator.CreateInstance method which accepts Type and Array of objects as parameters. It will automatically call required constructor based on number of items in array and item types.
object[] arguments = //Create array based on input
DataBase db=(DataBase)Activator.CreateInstance(typeof(Database), arguments); // This will call matching constructor based on array passed
I suggest a quite simpler approach. Use one single constructor that sets your members appriately:
public MyClass(params string[] switches)
{
if(switches.Contains("/i") this.i = ...
...
}
You can also create a simple list with all the options whose switch is true.
To call this constructor simply use this:
var instance = Activator.CreateInstance(myType, suppliedParameters);
Related
I am currently developing a C# WPF application. It is only used by a small amount of people/devices.
To make things easier I decided to talk directly to the MySQL db.
Now I wanted to be able to switch the current User / db Credentials with the click of a button, or be able to implement a logout feature.
I just currently tried this:
public DBConnect()
{
Initialize(null, null);
}
private void Initialize(string uid, string password)
{
string connectionstring;
connectionstring = "SERVER=" + server + ";" + "DATABASE=" + database + ";" + "UID=" + uid + ";" + "PASSWORD=" + password + ";";
connection = new MySqlConnection(connectionstring);
}
public void setCredentials(string uid, string password)
{
Initialize(uid, password);
}
public void destroyCredentialsAndConnection()
{
connection = null;
}
But apparently while Debugging I found out, that the old connection string is still cached statically by the MySQLConnection Class in the background.
Currently it does look like my approach is working, but I'm actually worried about the security of that implementation to list a few concerns:
memory dumps (usage of strings for passwords that can not be encrypted and may not be removed for quite some time by the garbage collector)
memory dumps (the fact that the connection string is being cached even after a "logout")
network traffic sniffing (is the connection between the database and my C# application encrypted)?
physical access to the server (is the MySQL database stored encrypted on the harddrive)?
Is there any better (more secure) way to switch credentials or to completely log the user out?
I did not really find any similar attempts here or anywhere else while doing research.
And if I would try to develop a php backend would that be safer without much experience? And could I still use my audit tables that I have created based on MySQL Triggers?
If I am understanding what you are asking correctly, you could try making a method to build the connection string based on user input, and inject the user's credentials into the connection string each time the method is called.
static SqlConnection Connection()
{
string UserName = UserNameField.Text;
string Password = PasswordField.Text;
SqlConnection Connection = new SqlConection("Server=ServerName,Port;Initial Catalog=Catalog;User Id=" + UserName + ";Password=" + Password + ";");
return Connection;
}
I have an app that the connection string changes by per user and machine. that said I have made a test connection string just to get it testing. Then once that is working I will add the dynamic connection string.
After some research I have figured out that there is a problem with the connection string.
If someone could please tell me where I am wrong I would appreciate it.
var s = #"metadata=res://*/ProcurementModel.csdl|res://*/ProcurementModel.ssdl|
res://*/ProcurementModel.msl;provider=System.Data.SqlClient;
provider connection string=&';data source=JAMES-DESKTOP\SQLEXPRESS;initial catalog=MyDatabase;persist security info=True;user id=User;password=*****;MultipleActiveResultSets=True;App=EntityFramework&';";
Update:
public ProcurementContext()
{
var s =
#"metadata=res://*/ProcurementModel.csdl|res://*/ProcurementModel.ssdl|
res://*/ProcurementModel.msl;provider=System.Data.SqlClient;
provider connection string=&';data source=JAMES-DESKTOP\SQLEXPRESS;initial catalog=MyDatabase;persist security info=True;user id=jd;password=P#ssw0rd;MultipleActiveResultSets=True;App=EntityFramework&';";
_connectionString = s;
}
Getting data:
public List<Procurement> ParcelListByUser(string userName)
{
using (var context = new ProcurementEntities(_connectionString))
{
return context.Procurements.Where(p => p.UserName == userName).ToList();
}
}
Constructor:
public ProcurementEntities(string connectionString)
: base(connectionString)
{
}
Error message:
Format of the initialization string does not conform to specification
starting at index 165.
Configuration:
The configuration of the Procurement
public class ProcurementConfiguration:DbConfiguration
{
public ProcurementConfiguration()
{
SetExecutionStrategy("System.Data.SqlClient", () => new SqlAzureExecutionStrategy());
SetDefaultConnectionFactory(new SqlConnectionFactory(ConnectionStrings.LocalConnectionString));
}
}
I think the issue is in your connection string. I've made the same thing and I have a function to create the connection string in an automatic way.
The code:
public static string GetConnectionString()
{
// Build the provider connection string with configurable settings
string cn = "server=" + mdlImpostazioni.p.dbServer;
cn += ";database=" + mdlImpostazioni.p.dbName;
cn += ";uid=" + mdlImpostazioni.p.dbUser;
cn += ";pwd=" + mdlImpostazioni.p.dbPassword + ";";
var providerSB = new SqlConnectionStringBuilder(cn);
var efConnection = new EntityConnectionStringBuilder();
// or the config file based connection without provider connection string
efConnection.Provider = "System.Data.SqlClient";
efConnection.ProviderConnectionString = providerSB.ConnectionString;
// based on whether you choose to supply the app.config connection string to the constructor
efConnection.Metadata = #"res://*"; //-----> very important
return efConnection.ToString();
}
In this way, I pass the return value to my db context constructor, like in your code. My constructor is:
public partial class Entities : DbContext
{
public Entities(string nameOrConnectionString)
: base(nameOrConnectionString)
{
}
public void Close()
{
this.Dispose();
}
}
Try it and ask if you have some problem.
One way to change your connection string dynamically is to only change entity framework underlying connection's connection string. Something like this:
myDbContext.Database.Connection.ConnectionString = "Data source=JAMES-DESKTOP\SQLEXPRESS;initial catalog=MyDatabase;persist security info=True;user id=DynamicUser;password=DynamicPassword;MultipleActiveResultSets=True;App=EntityFrameworkForUser";
This way, your application does not have to hardcode tokens related to entity framework (i.e. metadata).
In C# WinForms, where should I put my SQL connection string variable if I want to access it all over my application?
Right now I'm copy-pasting it wherever I'm using it.
//Sql connection string
SqlConnection con = new SqlConnection(#"Data Source=" + globalvariables.hosttxt + "," + globalvariables.porttxt + "\\SQLEXPRESS;Database=ha;Persist Security Info=false; UID='" + globalvariables.user + "' ; PWD='" + globalvariables.psw + "'");
SqlCommand command = con.CreateCommand();
You can simply use the app.config (or web.config) for that. It has a special section for connection-strings. See the MSDN-article about that. Then you can retrieve the string in code as use1515791 has already pointed out, like this:
ConfigurationManager.ConnectionStrings["NameOfConnectionStringInConfig"].ConnectionString
I rather not using app.config file, because it's not secure and hackable easily (every connection info is stored in plain text format here).
If I'm not using any of my DLLs, I place the SqlConnection in Program.cs with public getter and disabled setter. I make all the initializations in Program.cs, so it looks something like this:
static class Program
{
private static SqlConnection con;
internal static SqlConnection Con
{
get
{
return con = new SqlConnection(#"Data Source=" + globalvariables.hosttxt + "," + globalvariables.porttxt + "\\SQLEXPRESS;Database=ha;Persist Security Info=false; UID='" + globalvariables.user + "' ; PWD='" + globalvariables.psw + "'");
}
}
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new Form1());
}
}
Now can access it (for example) from you Form1.cs:
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
SqlCommand cmd = new SqlCommand();
cmd.Connection = Program.Con;
}
}
I've created a DLL to connect to SQL easily, check it here if you're interested.
Thats a rather general question imo.. depends on the application structure and so on. I tend to use a class "ApplicationContext" with properties for such things. However, maybe
ConfigurationManager.ConnectionStrings["whatever"].ConnectionString
is used a lot i think (add reference to System.Configuration)
Btw, if you use it "all over the place", don't you need an adapter class instead?
EDIT
public class DataAcces
{
private string Connectionstr;
public DataAcces()
{
this.Connectionstr = ConfigurationManager.ConnectionStrings["whatever"].ConnectionString;
}
public object ReturnSomething()
{
using(SqlConnection con = new SqlConnection(this.Connectionstr))
{
//do stuff to db and return something
}
}
}
and then when you need it
....
{
var whatYouNeed = (new DataAcces()).ReturnSomething();
}
typos happen :) this is the easiest to use i can come up with, not the best.
i am developing window application and i want the user of the app can change the connection string so i create aform to save connection string to setting and able to retrieve it but the problem is how to use this setting
private void button1_Click(object sender, EventArgs e)
{
var serv = Properties.Settings.Default.server;
var db = Properties.Settings.Default.database;
var userid = Properties.Settings.Default.userid;
var pass = Properties.Settings.Default.password;
SqlConnection conn = new SqlConnection("Data Source=serv;Initial Catalog=db;User ID=userid password=pass");
SqlDataAdapter sda = new SqlDataAdapter("SELECT count(*) FROM users WHERE username='" + txtUsername.Text + "' and password='" + txtPassword.Text + "'", conn);
}
Put your connection string in the App.config/Web.config, it will make it that much easier to alter later on if need be.
Also remember to always make use of the using statement when working with SqlConnection in general.
For example:
In the App.config/Web.config add the following:
<appSettings>
<add key="myConnectionString" value="Data Source=serv;Initial Catalog=db;User ID=userid password=pass" />
</appSettings>
Then you can easily access it anywhere in your project:
using (SqlConnection conn = new SqlConnection(ConfigurationManager.AppSettings["myConnectionString"]))
{
using(SqlCommand sqlCommandConn = new SqlCommand(InsertStatement))
{
sqlCommandConn.Connection = conn;
//TODO: Open connection, Execute queries...
}
}
Note
You can alter these settings via code as well if you wish:
private void UpdateConfig(string key, string value)
{
var configFile = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None);
configFile.AppSettings.Settings[key].Value = value;
configFile.Save();
}
Try
SqlConnection conn = new SqlConnection("Data Source=" + serv.ToString() + ";Initial Catalog=" + db.ToString() + ";User ID=" + userid.ToString() + ";password= " + pass.ToString());
As you can see from your code snippet, the connection string value is just a string that you are passing to the SqlConnection constructor, so for your case you could pull the value at runtime and load that value dynamically.
While it is possible to manipulate the app.config file which does hold connection string values, I typically prefer to manipulate secondary files. A better option could be to use a secondary file in XML for example and preform CRUD like operations on it as your users change their connection strings. At runtime, you can pull their specific connection string value and load it into the constructor as your doing above. A sample XML structure could be as follows:
<connections>
<connection userID="12345">Data Source=servA;Initial Catalog=db123;User ID=jSmith password=pass1</connection>
<connection userID="43532">Data Source=servB;Initial Catalog=db456;User ID=rJSmith password=abc321</connection>
</connections>
If all that is changing is the user, pass, catalog, and datasource values and the remainder of the connection string is static, you could just store these individual values as opposed to the entire connection string and then inject those dynamically to build the connection string at runtime.
Reading the XML is not difficult when using something like LINQ to XML which would allow you to query the XML file and get a specific connection string by the userID field. A good reference for LINQ to XML is at the following: http://msdn.microsoft.com/en-us/library/bb387098.aspx
How can I create a database programmatically and what is the minimum information I need to do this?
Please no "SQL Server Management Object API " suggestions.
You can either use the SQL Server Management Object API (see task "creating, altering and removing databases"):
var srv = new Server();
var db = new Database(srv, "mydb");
db.Create();
Information on how to get started is here. During SQL server installation you need to install the client SDK, the SMO assemblies are in C:\Program Files\Microsoft SQL Server\100\SDK\Assemblies
Or if you don't want the dependency on these assemblies, you can also simply run DDL statements using ADO.Net (e.g. see this question):
using (var connection = new SqlConnection(myConnectionString))
{
connection.Open();
var command = connection.CreateCommand();
command.CommandText = "CREATE DATABASE mydb";
command.ExecuteNonQuery();
}
Obviously you need a correct connection string: known sql server instance and a user with CREATE DATABASE permission.
Create database 'Databasename'
From the creators:
// your connection string
string connectionString = "Server=(local)\\netsdk;uid=sa;pwd=;database=master";
// your query:
var query = GetDbCreationQuery();
var conn = new SqlConnection(connectionString);
var command = new SqlCommand(query, conn);
try
{
conn.Open();
command.ExecuteNonQuery();
MessageBox.Show("Database is created successfully", "MyProgram",
MessageBoxButtons.OK, MessageBoxIcon.Information);
}
catch (Exception ex)
{
MessageBox.Show(ex.ToString());
}
finally
{
if ((conn.State == ConnectionState.Open))
{
conn.Close();
}
}
To create in default location with default settings, just:
static string GetDbCreationQuery()
{
// your db name
string dbName = "MyDatabase";
// db creation query
string query = "CREATE DATABASE " + dbName + ";";
return query;
}
Or, to create it in a specific location:
static string GetDbCreationQuery()
{
// your db name
string dbName = "MyDatabase";
// path to your db files:
// ensure that the directory exists and you have read write permission.
string[] files = { Path.Combine(Application.StartupPath, dbName + ".mdf"),
Path.Combine(Application.StartupPath, dbName + ".ldf") };
// db creation query:
// note that the data file and log file have different logical names
string query = "CREATE DATABASE " + dbName +
" ON PRIMARY" +
" (NAME = " + dbName + "_data," +
" FILENAME = '" + files[0] + "'," +
" SIZE = 3MB," +
" MAXSIZE = 10MB," +
" FILEGROWTH = 10%)" +
" LOG ON" +
" (NAME = " + dbName + "_log," +
" FILENAME = '" + files[1] + "'," +
" SIZE = 1MB," +
" MAXSIZE = 5MB," +
" FILEGROWTH = 10%)" +
";";
return query;
}
Even in case the execution fails, give it another try. The db files might have got created.
You need to open a connection to the server, i.e. you need a server and instance name.
You also need the proper access rights to create a database, so you might need some user name and password depending on the authentication settings on the server.
From the server name and authentication information you can construct a connection string and open a connection.
Then you can use the CREATE DATABASE SQL command (see here on MSDN). The only needed parameter for this command is a database name.
You need connection information: server, possibly instance, a user having create database rights on that server/instance and the corresponding password. Then you can use SMO for creating the database. Here is a small PowerShell example that you can very easily "translate" to C#, for example:
[System.Reflection.Assembly]::LoadWithPartialName('Microsoft.SqlServer.SMO') | Out-Null
$s = New-Object Microsoft.SqlServer.Management.Smo.Server($ServerInstance)
# Instantiate the database object and add the filegroups
$db = New-Object Microsoft.SqlServer.Management.Smo.Database($s, $DatabaseName)
$primaryFG = New-Object Microsoft.SqlServer.Management.Smo.FileGroup($db, 'PRIMARY')
$db.FileGroups.Add($primaryFG)
# Create Data file
$syslogname = $DatabaseName + '_SysData'
$dbdsysfile = New-Object Microsoft.SqlServer.Management.Smo.DataFile($primaryFG, $syslogname)
$primaryFG.Files.Add($dbdsysfile)
$dbdsysfile.FileName = $s.MasterDBPath + '\' + $syslogname + '.mdf'
$dbdsysfile.Size = [double](5.0 * 1024.0)
$dbdsysfile.GrowthType = 'KB'
$dbdsysfile.Growth = 10000.0
$dbdsysfile.IsPrimaryFile = 'True'
# Create Log file
$loglogname = $DatabaseName + '_Log'
$dblfile = New-Object Microsoft.SqlServer.Management.Smo.LogFile($db, $loglogname)
$db.LogFiles.Add($dblfile)
$dblfile.FileName = $s.MasterDBLogPath + '\' + $loglogname + '.ldf'
$dblfile.Size = [double](10.0 * 1024.0)
$dblfile.GrowthType = 'KB'
$dblfile.Growth = 10000.0
# Create database with READ_COMMITTED_SNAPSHOT isolation level.
# Other options can be set on $db object before calling Create.
$db.IsReadCommittedSnapshotOn = $true
$db.RecoveryModel = [Microsoft.SqlServer.Management.Smo.RecoveryModel]::Simple
$db.Create()
Assuming you have the rights to fire off a CREATE DATABASE statement you can do so as you would any other query.
I should stress that being able to do so requires quite high privileges on the server and this would be restricted to DBAs in QA and Production environments.
For that reason I would make sure that your connection uses Windows Integrated Security. That way when the appropriate DBA runs your application the app will function as requested.
Once you have created your database you will also need to fire off the T-SQL to create logins and create users. I'm taking it as obvious that CREATE TABLE/VIEW statements will be needed.