For testing purposes I have a method that takes in a SqlConnection or a connection string and a string. The method examines the SqlConnection or the DbContext.Database.Connection.ConnectionString against a set of 'dangerous' strings, if the connection string has any 'dangerous' strings, it doesn't execute its query.
I basically need to know how to execute (via C#) a truncate/delete all data in table HOWEVER the problem is foreign dependencies. I am trying the following method. Remove all dependencies, delete the table and then reinstate all dependencies however I am having an issue with my reinstate all dependencies code. How can I do this via C#?
Main method that should delete the table.
public int DeleteFromDatabase(SqlConnection sqlConnection, string tableName)
{
int success = 0;
string sqlTrunc = "Delete from " + tableName;
if (isSafeSqlConnection(sqlConnection))
{
DropAllConstraints();
using (sqlConnection)
{
SqlCommand cmd = new SqlCommand(sqlTrunc, sqlConnection);
sqlConnection.Open();
success = cmd.ExecuteNonQuery();
sqlConnection.Close();
}
ReinstateAllConstraints(); //<=error happens here.
}
return success;
}
This drops all constraints:
public void DropAllConstraints()
{
string[] queries = File.ReadAllLines(#"Utility\UnsafeStrings\dropallcontraint.txt");
using (var connection = new SqlConnection(connectionString))
{
connection.Open();
foreach (var item in queries)
{
var command = new SqlCommand(item, connection);
command.ExecuteNonQuery();
}
connection.Close();
}
}
This checks that the connection passed in isn't a live server:
private bool isSafeSqlConnection(SqlConnection connection)
{
string pathToUNsafeStrings = #"Utility\UnsafeStrings\UnsafeStrings.txt";
string[] unsafeStrings = File.ReadAllLines(pathToUNsafeStrings);
foreach (var item in unsafeStrings)
{
if (connection.ConnectionString.Contains(item))
return false;
}
return true;
}
This method essentially executes every entry returned from this query:
select
'ALTER TABLE dbo.' + object_name(fk.parent_object_id) +
' ADD CONSTRAINT ' + fk.name +
' FOREIGN KEY(' + c1.name + ') REFERENCES dbo.' +
object_name(fk.referenced_object_id) + '(' + c2.name + ')' as col1
from
sys.foreign_keys fk
inner join
sys.foreign_key_columns fkc ON fk.object_id = fkc.constraint_object_id
inner join
sys.columns c1 ON fkc.parent_column_id = c1.column_id and c1.object_id = fkc.parent_object_id
inner join
sys.columns c2 ON fkc.referenced_column_id = c2.column_id and c2.object_id = fkc.referenced_object_id
public void ReinstateAllConstraints()
{
string[] reinstateAllConstraints = File.ReadAllLines(#"Utility\UnsafeStrings\reisntateconstraint.txt");
using (var connection = new SqlConnection(connectionString))
{
connection.Open();
foreach (var item in reinstateAllConstraints)
{
var command = new SqlCommand(item, connection);
command.ExecuteNonQuery();
}
connection.Close();
}
}
You can do this workflow:
Disable the foreign key check.
Delete all registers from tables that references the table that you want delete.
Delete all registers from table that you want delete.
Enable foreign key check.
You can see in this code below(I am using C# 6)
public bool TruncateTable(string tableName)
{
string sqlTrunc = $"Delete from {tableName}";
if (isSafeSqlConnection(sqlConnection))
{
DisableAllForeignKeys();
using (sqlConnection)
{
DeleteAllDependencies(tableName, sqlConnection);
SqlCommand cmd = new SqlCommand(sqlTrunc, sqlConnection);
sqlConnection.Open();
success = cmd.ExecuteNonQuery();
sqlConnection.Close();
}
EnableAllForeignKeys();
}
return success;
}
public void DisableAllForeignKeys(SqlConnection sqlConnection)
{
using(var command = new SqlCommand($"EXEC sp_msforeachtable \"ALTER TABLE ? NOCHECK CONSTRAINT all\"", sqlConnection))
command.ExecuteNonQuery();
}
public void EnableAllForeignKeys(SqlConnection sqlConnection)
{
using(var command = new SqlCommand($"EXEC sp_msforeachtable \"ALTER TABLE ? WITH CHECK CHECK CONSTRAINT all\"", sqlConnection))
command.ExecuteNonQuery();
}
private static void DeleteAllDependencies(string tableName, SqlConnection sqlConnection)
{
var sql =
$#"SELECT t.name AS 'Table that contains FK',
fk.name AS 'FK Name',
t1.Name AS 'Table that is being referenced'
FROM sys.foreign_key_columns fkc
INNER JOIN sys.tables t ON t.object_id = fkc.parent_object_id
INNER JOIN sys.tables t1 ON t1.object_id = fkc.referenced_object_id
INNER JOIN sys.columns c1 ON c1.object_id = fkc.parent_object_id AND c1.column_id = fkc.parent_column_id
INNER JOIN sys.foreign_keys fk ON fk.object_id = fkc.constraint_object_id
INNER JOIN sys.schemas sc ON t.schema_id = sc.schema_id
WHERE (sc.name + '.' +t1.name) = 'dbo.{
tableName}';";
var command = sqlConnection.CreateCommand();
command.CommandText = sql;
List<Tuple<string, string, string>> tuples;
using (var dataReader = command.ExecuteReader())
{
var enumerator = dataReader.GetEnumerator();
tuples = Enumerable.Range(1, int.MaxValue)
.TakeWhile(i => enumerator.MoveNext())
.Select(i => (IDataRecord)enumerator.Current)
.Select(dr => Tuple.Create(dr.GetString(0), dr.GetString(1), dr.GetString(2)))
.ToList();
}
foreach (var tuple in tuples)
{
using (var sqlCommand = sqlConnection.CreateCommand())
{
sqlCommand.CommandText = $"DELETE FROM {tuple.Item1}";
sqlCommand.ExecuteNonQuery();
}
}
}
You might want to configure the relations to propagate changes accordingly ("cascade"), so that when one part of the relation is deleted, the other gets deleted or updated as well.
The DBMS is right in saying that you can not activate a fk relation if the items you are referring to from the other table are missing.
Related
I am using FluentMigrator to migrate one database schema to another. I have a case in which I want to check if a foreign key exists before deleting it.
Previously, I just delete the foreign key by doing:
Delete.ForeignKey("FK_TableName_FieldName").OnTable("TableName");
How do I check that the foreign key exists first?
This is how to delete a foreign key if it exists using FluentMigrator:
if (Schema.Table("TableName").Constraint("FK_TableName_FieldName").Exists())
{
Delete.ForeignKey("FK_TableName_FieldName").OnTable("TableName");
}
Based on this https://stackoverflow.com/a/17501870/10460456 you can use Execute.WithConnection function to test if foreign key exist before delete it.
Execute.WithConnection((connection, transaction) =>
{
DeleteForeignKeyIfExist(connection, transaction, "yourReferencedTable", "yourTable", "foreignColumnName", "foreignKeyName");
});
public bool DeleteForeignKeyIfExist(IDbConnection connection, IDbTransaction transaction, string referenceTable, string table, string foreignKeyColumn, string foreignKeyConstrainName)
{
using (var cmd = transaction.Connection.CreateCommand())
{
cmd.Transaction = transaction;
cmd.CommandType = CommandType.Text;
cmd.CommandText = ForeignKeyExistCommand(referenceTable, foreignKeyColumn);
bool foreignKeyExist = false;
using (var reader = cmd.ExecuteReader())
{
while (reader.Read())
{
// If this code is reached, the foreign key exist
foreignKeyExist = true;
break;
}
}
if (foreignKeyExist)
{
cmd.CommandText = $"ALTER TABLE [{table}] DROP CONSTRAINT [{foreignKeyConstrainName}];";
cmd.ExecuteNonQuery();
return true;
}
}
return false;
}
private string ForeignKeyExistCommand(string foreignTable, string innerColumn)
{
return $"SELECT OBJECT_NAME(f.parent_object_id) TableName, " +
"COL_NAME(fc.parent_object_id, fc.parent_column_id) ColName " +
"FROM sys.foreign_keys AS f INNER JOIN sys.foreign_key_columns AS fc " +
"ON f.OBJECT_ID = fc.constraint_object_id INNER JOIN sys.tables t " +
$"ON t.OBJECT_ID = fc.referenced_object_id WHERE OBJECT_NAME(f.referenced_object_id) = '{foreignTable}' " +
$"and COL_NAME(fc.parent_object_id,fc.parent_column_id) = '{innerColumn}'";
}
Question says it all really, I just want the all column names from a table, I'm looking for as basic way to do this as possible.
If you run the following SQL you'll get an empty rowset. From which you can interpret the column names by using a SqlCommand and DataReader.
using (var conn = new SqlConnection("your_conn_string"))
{
var command = new SqlCommand("select * from [dbo].[tableName] where 1 = 2");
conn.Open();
using(var dr = command.ExecuteReader())
{
var columns = new List<string>();
for(int i=0;i<reader.FieldCount;i++)
{
columns.Add(reader.GetName(i));
}
}
}
That depends entirely on the database. Almost all databases have some sort of metadata about the database. Most implement some version of the information_schema method.
For instance, a common way to get information about columns is:
select column_name
from information_schema.columns
where table_name = ? and table_schema = ?;
? are place-holders for the table name and schema.
This should do what you want.
public string[] getColumnsName()
{
List<string> listacolumnas=new List<string>();
using (SqlConnection connection = new SqlConnection(Connection))
using (SqlCommand command = connection.CreateCommand())
{
command.CommandText = "select c.name from sys.columns c inner join sys.tables t on t.object_id = c.object_id and t.name = 'Usuarios' and t.type = 'U'";
connection.Open();
using (var reader = command.ExecuteReader())
{
while (reader.Read())
{
listacolumnas.Add(reader.GetString(0));
}
}
}
return listacolumnas.ToArray();
}
Get column name from SQL Server
Hello I want to delete a row from my table , I have 3 tables for Recipe , Ingredient and i'm using the 3rd table to combine the Recipe and Ingredient tables,
my question I can add a row but I don't know how to delete a row I really dont have an idea how to do it .
the code down plz help ^^"
private void btnAddToRecpie_Click(object sender, EventArgs e)
{
string query = "INSERT INTO RecpieIngredient VALUES (#RecpieId,#IngredientId)";
using (connection = new SqlConnection(connectionString))
using (SqlCommand cmnd = new SqlCommand(query, connection))
{
connection.Open();
cmnd.Parameters.AddWithValue("#RecpieId", lstRecpie.SelectedValue);
cmnd.Parameters.AddWithValue("#IngredientId",lstAllIngredient.SelectedValue);
cmnd.ExecuteScalar();
}
PopulateRecpie();
}
private void butToDelete_Click(object sender, EventArgs e)
{
string query = "DELETE FROM RecpieIngredient WHERE (#RecpieId , #IngredientId)=#RecpieId , #IngredientId ";
// a.Name FROM Ingredient a INNER JOIN RecpieIngredient x ON a.Id = x.IngredientId INNER JOIN Recpie m ON m.Id=x.RecpieId WHERE m.Id=" + index;
using (connection = new SqlConnection(connectionString))
using (SqlCommand cmnd = new SqlCommand(query, connection))
{
connection.Open();
cmnd.Parameters.AddWithValue("#RecpieId", lstRecpie.SelectedValue);
cmnd.Parameters.AddWithValue("#IngredientId", lstAllIngredient.SelectedValue);
cmnd.ExecuteNonQuery();
}
PopulateRecpie();
}
This:
INSERT INTO RecpieIngredient VALUES (#RecpieId,#IngredientId)
Is really shorthand for:
INSERT INTO RecpieIngredient (RecpieId, IngredientId) VALUES (#RecpieId, #IngredientId)
The DELETE statement does not have such a shorthand variant, and requires you to mention the column names explicitly:
DELETE FROM RecpieIngredient WHERE RecpieId = #RecpieId AND IngredientId = #IngredientId
You came pretty close in fixing the problem yourself though :)
You delete statement is wrong. It should be
string query = "DELETE FROM RecpieIngredient
WHERE RecpieId = #RecpieId
AND IngredientId = #IngredientId";
I need to generate an XML like the below one with constraints as tags and column name as values for a specific DB table in c#.
<tablename>
<key>ProductId</key>
<composite>
<column>ProductId</column>
<column>ProductCode</column>
<composite>
<ForeignKey>
<column>ProductBaseId</column>
</ForeignKey>
</tablename>
Can anyone help on this?
Here you will find the primary key:
SELECT Col.Column_Name from
INFORMATION_SCHEMA.TABLE_CONSTRAINTS Tab,
INFORMATION_SCHEMA.CONSTRAINT_COLUMN_USAGE Col
WHERE
Col.Constraint_Name = Tab.Constraint_Name
AND Col.Table_Name = Tab.Table_Name
AND Constraint_Type = 'PRIMARY KEY'
AND Col.Table_Name = 'tablename'
If you have multiple PRIMARY KEYS, then you will get them from the same query above. You can just count the primary key and if they are more than 1 then you can list them in Composite. Otherwise just put them as Key.
Here is another point, If there are two primary keys, then what will be in <KEY> tag?
Here you will find Foreign Keys:
SELECT Col.Column_Name from
INFORMATION_SCHEMA.TABLE_CONSTRAINTS Tab,
INFORMATION_SCHEMA.CONSTRAINT_COLUMN_USAGE Col
WHERE
Col.Constraint_Name = Tab.Constraint_Name
AND Col.Table_Name = Tab.Table_Name
AND Constraint_Type = 'FOREIGN KEY'
AND Col.Table_Name = 'tablename'
Just join two information and write in XML. XML writing is so easy, here you can find it. XML Writing - DotNetPerls
UPDATE 1 :
Here is a code, where you can find the pK and FK both and with type from single query.
SELECT Col.Column_Name,
Case When( CHARINDEX('PK_' , Col.CONSTRAINT_NAME) >0 ) Then 'PK' ELSE 'FK' END as Type
from
INFORMATION_SCHEMA.TABLE_CONSTRAINTS Tab,
INFORMATION_SCHEMA.CONSTRAINT_COLUMN_USAGE Col
WHERE
Col.Constraint_Name = Tab.Constraint_Name
AND Col.Table_Name = Tab.Table_Name
AND (Constraint_Type = 'PRIMARY KEY' OR Constraint_Type = 'Foreign KEY')
AND Col.Table_Name = 'table_1'
The Query output will like below:
Column_Name Type
AnotherID FK
Code PK
ID PK
I know it's not good that, I am providing codes fully. but as it was interesting to me, I have wrote some lines that may helpful to others also.
Here is the full code :
namespace XMLWritingFromDB
{
public class Program
{
public static string connectionstring = "Data Source=RAHIMPC\\SQLEXPRESS;Initial Catalog=Test;Integrated Security=false;uid=******;pwd=********";
public static SqlConnection conn;
static void Main(string[] args)
{
//Get the table Informations
DataTable dt = GetDataSchema("table_1"); // PUT YOUR TABLE NAME HERE.
string xmlDocument = PrepareXml(dt);
Console.Write(xmlDocument);
Console.ReadKey();
}
public static DataTable GetDataSchema(string TableName)
{
DataTable dt = new DataTable();
string query = "SELECT Col.Column_Name, Case When( CHARINDEX('PK_' , Col.CONSTRAINT_NAME) >0 ) Then 'PK' ELSE 'FK' END as Type " +
" from INFORMATION_SCHEMA.TABLE_CONSTRAINTS Tab, INFORMATION_SCHEMA.CONSTRAINT_COLUMN_USAGE Col " +
" WHERE Col.Constraint_Name = Tab.Constraint_Name AND Col.Table_Name = Tab.Table_Name " +
"AND (Constraint_Type = 'PRIMARY KEY' OR Constraint_Type = 'Foreign KEY') AND Col.Table_Name = '"+TableName+"'";
using (conn = new SqlConnection(connectionstring))
{
conn.Open();
using (SqlCommand sc = new SqlCommand(query, conn))
{
using (SqlDataReader dr = sc.ExecuteReader())
{
dt.Load(dr);
}
}
conn.Close();
}
return dt;
}
public static string PrepareXml(DataTable dt)
{
int pkCount = 0, fkCount = 0;
List<string> lstPK = new List<string>();
List<string> lstFK = new List<string>();
//Build data for xml
foreach (DataRow dr in dt.Rows)
{
if (dr[1].ToString().Contains("PK"))
{
pkCount++;
lstPK.Add(dr[0].ToString());
}
if (dr[1].ToString().Contains("FK"))
{
fkCount++;
lstFK.Add(dr[0].ToString());
}
}
List<TableName> lstXml = new List<TableName>();
TableName xml = new TableName();
xml.key = lstPK[lstPK.Count() - 1].ToString();
xml.ForeignKey = lstFK;
if (pkCount > 1)
xml.Composite = lstPK;
var stringwriter = new System.IO.StringWriter();
var serializer = new XmlSerializer(xml.GetType());
serializer.Serialize(stringwriter, xml);
return stringwriter.ToString();
}
}
[XmlRoot("TableName")]
public class TableName
{
public string key { get; set; }
[XmlArray(ElementName = "Composite")]
[XmlArrayItem("ColumnName", Type = typeof(string))]
public List<string> Composite { get; set; }
[XmlArray(ElementName = "ForeignKey")]
[XmlArrayItem("ColumnName", Type = typeof(string))]
public List<string> ForeignKey { get; set; }
}
}
You can use dataTableObject.WriteXml() method, this method contains various overloads.
How can one retrieve the tables' names into a List<string> from a specific database on a server?
System.Data.SqlClient has what you need without a formal query on sys.Tables (though that's what it's using in the background). Use the GetSchema() method on the SqlConnection object and designate that you want the "Tables" and it will send you a DataTable object back with a row for each table. It sends back database name, table schema name, table name, and table type in each row (in that column order). The code would look like this:
public static List<string> GetTables(string connectionString)
{
using (SqlConnection connection = new SqlConnection(connectionString))
{
connection.Open();
DataTable schema = connection.GetSchema("Tables");
List<string> TableNames = new List<string>();
foreach (DataRow row in schema.Rows)
{
TableNames.Add(row[2].ToString());
}
return TableNames;
}
}
For SQL Server 2005 and higher:
using (SqlConnection connection = new SqlConnection(#"Data Source=(local);Integrated Security=True;Initial Catalog=DB_Name;")) {
connection.Open();
using (SqlCommand command = connection.CreateCommand()) {
command.CommandText =
#"SELECT s.name, o.name
FROM sys.objects o WITH(NOLOCK)
JOIN sys.schemas s WITH(NOLOCK)
ON o.schema_id = s.schema_id
WHERE o.is_ms_shipped = 0 AND RTRIM(o.type) = 'U'
ORDER BY s.name ASC, o.name ASC";
using (SqlDataReader reader = command.ExecuteReader()) {
while (reader.Read()) {
string schemaName = reader.GetString(0);
string tableName = reader.GetString(1);
// your code goes here...
}
}
}
}
To fetch all the user defined tables from the DB ( SQlServer), we have to query system catalogs.
SELECT Name from Sysobjects where xtype = 'u'
this query will return all the user defined tables in the DataBase
I think that this SQL should return a table containing tableNames in your DB:
SELECT * FROM information_schema.tables
You can use LINQ query to get all tables name fairly easily:
public List<string> GetSqlTables(SqlConnection conn)
=> (from DataRow row in conn.GetSchema("Tables").Rows.Cast<DataRow>()
select row["TABLE_NAME"].ToString()).ToList();
.Net Core extension using SqlConnection that opens/closes the connection.
public static class SQLExtensions
{
public static List<string> GetTableNames(this SqlConnection connection)
{
using (connection)
{
connection.Open();
return connection.GetSchema("Tables")
.Rows
.Cast<DataRow>()
.Select(row => row["TABLE_NAME"].ToString())
.OrderBy(nme => nme)
.ToList();
} // Auto Close exiting the block
}
}
Usage (Linqpad)
var connectionStr = #"Data Source=.;Integrated Security=SSPI;Initial Catalog=Rasa";
(new SqlConnection(connectionStr).GetTableNames()).Dump();
this my solution:
public void opencon()
{
if (conn == null)
{
conn = new SqlConnection(#"Your connection");
}
if (conn.State == ConnectionState.Closed)
{
conn.Open();
}
}
public void Closecon()
{
if ((conn != null) && (conn.State == ConnectionState.Open))
{
conn.Close();
}
}
public void GetTables(ComboBox cb)
{
chuoiketnoi();
DataTable schema = conn.GetSchema("Tables");
foreach (DataRow row in schema.Rows)
{
cb.Items.Add(row[2].ToString());
}
dongketnoi();
}