I'm very new to C#, and I just learn coding as one of my hobbies.
I'm trying to develop a stand-alone Windows application using Visual Studio and an Access database.
I have some difficulties how to access the database.
Project is my table in the Access database and projectname and path are my columns in the project table.
I'm trying to read the folders Name in a certain path and writing the name and path of a folder into my table.
How do I compare and insert only the new folder created?
This is showing an error in my insert syntax!
string[] files = Directory.GetDirectories(#"C:\\SomePath\\Project_1\\Project_1\\Resources");
foreach (string file in files)
{
string name = new DirectoryInfo(file).Name;
String Root = Path.GetFullPath(file);
connection_2.Open();
OleDbCommand ListWrite = new OleDbCommand();
ListWrite.Connection = connection_2;
ListWrite.CommandText= "insert into Project (projectname,path) values ('" + name + "','" +Root+ "') where'"
+ name + "' != projectname ";
ListWrite.ExecuteNonQuery();
connection_2.Close();
}
An insert command does not allow a where statement the way you use it.
You will need to query the table for each of the directory names. If the result of the query is not empty, the specific directory name it is already present in the table. Otherwise, you can insert the new name with an insert.
I would suggest to write a new method for that check:
public bool DoesFolderAlreadyExistInTable(string folder_name, string path, OleDbConnection connection)
{
using (var ListWrite = new OleDbCommand("select count(*) as c from Project where name=#name and path=#path", connection)) {
ListWrite.Parameters.AddWithValue("#name", folder_name);
ListWrite.Parameters.AddWithValue("#path", path);
var result = ListWrite.ExecuteReader();
return result.Read() && result.GetInt32(0) > 0;
}
}
You just need to leave a space after where:
..where '...
However, the best solution for not having such issues as well as SQL injection is to use parameters:
ListWrite.CommandText= "insert into Project (projectname,path) values (#name, #path)";
ListWrite.Parameters.Add("#name",SqlDbType.NVarChar).Value = name;
ListWrite.Parameters.Add("#path",SqlDbType.NVarChar).Value = Root;
Also notice that the where clause does not have any sense in an insert statement. You have to handle it from code or putting a unique constraint on the projectname column.
A WHERE clause is not valid in an insert statement. I assume what you were attempting to do was prevent duplicate project names. One way you can do this in SQL Server is as shown, I've made the assumption it's valid in Access as well but this isn't always the case.
SQL
INSERT INTO Project (projectname, path)
SELECT DISTINCT 'yourpath', 'yourroot'
FROM Project
WHERE NOT EXISTS (SELECT projectname FROM Project WHERE projectname = 'yourpath')
C#
ListWrite.CommandText= "insert into Project (projectname,path) select distinct '" + name + "','" + Root + "') from Project where not exists (select projectname from Project where projectname = '" + name + "'";
As pointed out by LosWochos and apomene, you should also look into parameterized SQL.
Related
I've got a segment of C# code that restores a SQL Server database. This works well. The issue is running it again on another .bak file. Even though the second backup has a different name, it needs to write to the same directory as the first backup and also has the same naming scheme for the .mdf and .ldf files.
I'm just curious if there is a way to modify the naming scheme of the .mdf and .ldf files, or if there is some other method to create subdirs under the initial SQL Server directory for these files to be restored to.
Error message that I'm currently getting:
Additional information: The file XXXXXX.MDF cannot be overwritten. It is being used by database XAXAXAXAX
I figure I could use a move statement, but I am trying to keep from needing all of the directory values hardcoded or logged in a config somewhere.
string sql = "SELECT database_id FROM sys.databases WHERE Name = '"+yuy+"'";
SqlConnection con = new SqlConnection(#"" + singleconn.Replace(#"\\", #"\"));
SqlCommand command = new SqlCommand(sql, con);
con.Open();
object resultObj = command.ExecuteScalar();
con.Close();
if (resultObj == null)
{
string sql2 = "Restore Database " + yuy + " FROM DISK = '" + #"\" + localdir.Replace(#"\\", #"\") + #"\" + FileName + "'";
SqlCommand command2 = new SqlCommand(sql2, con);
con.Open();
command2.ExecuteNonQuery();
con.Close();
con.Dispose();
File.Delete(#"\" + localdir.Replace(#"\\", #"\") + #"\" + FileName);
MessageBox.Show("Database recovered successfully!");
}
else
{
Random rnd = new Random();
int card = rnd.Next(52);
MessageBox.Show("There is already a database under this name; renaming the DB to " + yuy + card.ToString());
string sql2 = "Restore Database " + yuy + card.ToString() + " FROM DISK = '" + #"\" + localdir.Replace(#"\\", #"\") + #"\" + FileName + "'";
SqlCommand command2 = new SqlCommand(sql2, con);
con.Open();
command2.ExecuteNonQuery();
con.Close();
con.Dispose();
File.Delete(#"\" + localdir.Replace(#"\\", #"\") + #"\" + FileName);
MessageBox.Show("Database Recovered Successfully!");
}
Figured most of this out thanks to scsimon, for now the last thing I am getting in terms of errors is this.
Additional information: Logical file 'XXXXXX.mdf' is not part of database 'Databasename'. Use RESTORE FILELISTONLY to list the logical file names.
The thing is I am pulling this straight from the Databasename properties.
any help would be appreciated.
I'm just curious if there is a way to modify the naming scheme of the
.mdf and .ldf files, or if there is some other method to create
subdirs under the initial SQL Server directory for these files to be
restored to.
You can use the MOVE clause as you suggested with the RESTORE command to rename and/or move your data files. It looks like this:
RESTORE DATABASE myDatabase FROM DISK = '\\somedir\someSubDir\mybackup.bak'
WITH
MOVE 'datafile1' TO 'E:\somedir\new_datafile2.mdf',
MOVE 'logfile' TO 'E\somedir\new_log.ldf'
This was created to move the files from the default location in the backup to another directory (which you can do) but it can also be done to rename them too. Naturally you'd do it for all your .ndf too.
I am trying to keep from needing all of the directory values hardcoded
or logged in a config somewhere.
No worries there, just restore the database with the HEADERONLY to view the contents of the backup like name, date, and other useful info. For the file paths specifically, you'd use FILELISTONLY. This would prevent you from hard coding them. More on that here.
CREATE TABLE #DataFiles (LogicalName nvarchar(128)
,PhysicalName nvarchar(260)
,[Type] char(1)
,FileGroupName nvarchar(128) null
,Size numeric(20,0)
,MaxSize numeric(20,0)
,FileID bigint
,CreateLSN numeric(25,0)
,DropLSN numeric(25,0)
,UniqueID uniqueidentifier
,ReadOnlyLSN numeric(25,0) null
,ReadWriteLSN numeric(25,0) null
,BackupSizeInBytes bigint
,SourceBlockSize int
,FileGroupID int
,LogGroupGUID uniqueidentifier null
,DifferentialBaseLSN numeric(25,0) null
,DifferentialBaseGUID uniqueidentifier null
,IsReadOnly bit
,IsPresent bit
,TDEThumbprint varbinary(32) null
,SnapshotURL nvarchar(360) null
)
INSERT INTO #DataFiles
EXEC('RESTORE FILELISTONLY FROM DISK = ''E:\DB Backups\YourBackup.bak''')
SELECT LogicalName, PhysicalName FROM #DataFiles
DROP TABLE #DataFiles
I'm making a table, if the name doesn't already exist
string sc1 = #"IF EXISTS(SELECT * FROM INFORMATION_SCHEMA.TABLES WHERE
TABLE_NAME = #name) SELECT 1 ELSE SELECT 0";
string sc2 = #"CREATE TABLE dbo.Data" + pv.tablecounter + "(testdescription VARCHAR(25), testvalue VARCHAR(5))";
SqlCommand check1 = new SqlCommand(sc1, connection);
check1.Parameters.Add("#name", SqlDbType.VarChar).Value = "Data" + pv.tablecounter;
SqlCommand check2 = new SqlCommand(sc2, connection);
exists = Convert.ToInt32(chec.ExecuteScalar());
if(exists == 1)
{
MessageBox.Show("table exists, will not create new one");
}
else
{
MessageBox.Show("table wasn't found, creating now");
check2.ExecuteNonQuery();
}
i can verify that this is all "working" (the correct message will show, and it will find the newly created table and give me the right message). My connection string works fine
my question is, where did it go? i can't browse and find it in the server explorer. this is probably basic but i cannot find any info about it. I have tried specifying the database
#"CREATE TABLE testdb.dbo.Data" + pv.tablecounter + "(testdescription VARCHAR(25), testvalue VARCHAR(5))";
Wth an error that database testdb does not exist , even though it's in my server explorer and i have been writing to it elsewhere using the same connection string
using Visual Studio 2017
thanks
I am creating store procedures from one source database to another destination database using c# console application. For that i have list of store procedures from source database. I iterate through that list and get definition of that sp by this command
Select definition from sys.sql_modules where object_id = OBJECT_ID ('" + item.SPName + "')"
and whatever defintion i get for that sp i am executing that result in destination database by
var idrSPCreate = cmdSPCreate.ExecuteNonQuery();
by this method i can successfully create store procedures in destination database. but my problem is that i am losing all formatting/indentation in destination database. How to preserve sp's formatting/indentation? is there any way or my question is invalid?
Getting sp's definition's code is below :
var cmdTextSP = string.Format("Select definition from sys.sql_modules where object_id = OBJECT_ID ('" + item.SPName + "')");
cmd.CommandText = cmdTextSP;
var idrSP = cmd.ExecuteReader();
and executing that definition in destination database using below code :
while (idrSP.Read())
{
if (idrSP.HasRows)
{
var SPDefinition = idrSP.GetString(0);
connDest.Open();
cmdSPCreate = connDest.CreateCommand();
cmdSPCreate.CommandType = System.Data.CommandType.Text;
cmdSPCreate.CommandText = string.Format(SPDefinition);
var idrSPCreate = cmdSPCreate.ExecuteNonQuery();
connDest.Close();
}
}
I need to have a second copy of a MS Access table that will be saved in a network drive. And since splitting is not an option because it drastically slows the application down, I decided to just manually "merge" the data after a certain user action like clicking an exit button.
I have this query string
public const string MERGETOMAIN = #"INSERT INTO tbl_name (UserID, ...)" +
" IN 'C:\Users\nathan\Desktop\copy.accdb' SELECT TOP 1 UserId, ... " +
" FROM tbl_name WHERE UserID = #currentUser ORDER BY ROWID DESC";
...and it works when I do this:
using (OleDbCommand cmd = new OleDbCommand(Helpers.Queries.MERGETOMAIN, mergeConn))
{
cmd.Parameters.AddWithValue("#currentUser", currentUserID);
cmd.ExecuteNonQuery();
}
But, as you notice, the path to the copy is hardcoded. I wanted it to be dynamic so I tried using a parameter like I always do, so I replaced it with a question mark:
public const string MERGETOMAIN = #"INSERT INTO tbl_name (UserID, ...)" +
" IN ? SELECT TOP 1 UserId, ... " +
" FROM tbl_name WHERE UserID = #currentUser ORDER BY ROWID DESC";
But this results in the following error:
Your network access was interrupted. To continue close the database and then open it again.
So instead of a question mark, I used #parameterName. But, when I do, I get the following error:
The file "foo\foo\#parameterName" cannot be found.
The weird thing is, the other paremeter, #currentUser, is working just fine.
I'm not really sure what is happening here, but the only thing that works right now is to hardcode the path. I tried looking through similar questions but no dice.
Any idea will be greatly appreciated.
Thank you.
You can write a method which creates the string for you.
public string getMergeToMain(string path)
{
string strRet;
if (path != null)
{
strRet = #"INSERT INTO FSO_LogSheet (UserID, ...)" +
" IN '" + path + "' SELECT TOP 1 UserId, ... " +
" FROM FSO_LogSheet WHERE UserID = #currentUser ORDER BY ROWID DESC";
return strRet;
}
else
{ //Error, not agood habit to return null...
return null;
}
}
With the method parameter "path", you can set the path dynamically.
You can call it for example by
OleDbCommand cmd = new OleDbCommand(fooClass.getMergeToMain(#"C:\users\bar.accdb"), mergeConn)
I assume that you know the difference between public, private and static methods, so please do not forget to set it right for your own needs.
I have the following code:
IEnumerable<Table> tables = dbtables.ToList()
.Where(x => x.Name.StartsWith("PartStats"));
foreach (var tbl in tables)
{
Table JoinTable = new Table(db, "ProductMaterial");
JoinTable.Columns.Add(tbl.Columns[0]);
string tblName = tbl.Columns[0].Name;
string script =
#"ALTER TABLE [dbo].[ProductMaterial]
WITH CHECK ADD CONSTRAINT [FK_" + tbl.Name +
"] PRIMARY KEY([" + s;
string script2 = #"]) REFERENCES [dbo].[" + tbl.Name + "] ([" + tblName + "])" ;
string newscr = string.Concat(script, script2);
AddPrimaryKey(tbl, newscr);
This used to work, but now when I run it, I get this error:
"Add object to collection failed for ColumnCollection of table ProductMaterial".
AddPrimaryKey() just executes the script on the SQL Server, and is fine. The problem is in the preceding code to that method call.
The server is on, I am using local admin account for the SQL Server (my windows account is an admin for the SQL Server).
Thanks
Perhaps wrap the call in a check whether the column already exists on the table.
if (!JoinTable.Columns.Contains(tbl.Columns[0].Name)
{
JoinTable.Columns.Add(tbl.Columns[0]);
}
Could it be that you're trying to add multiple Tables named "ProductMaterial"?