I'm trying to read information from my database schema in C# (.NET 4.5, SQL Server 2014). I was having trouble with some fields such as MaxLength/ColumnLength until I found a forum that mentioned setting the DataAdapter.MissingSchemaAction to MissingSchemaAction.AddWithKey. Unfortunately the DefaultValue field is still blank even for columns that have a default set in the "Default Value or Binding" in the Column Properties in SQL Server.
SqlDataAdapter dbadapter = new SqlDataAdapter(SELECT_STRING, CONN_STRING);
dbadapter.MissingSchemaAction = MissingSchemaAction.AddWithKey;
DataTable tbl = new DataTable();
dbadapter.Fill(tbl);
// I actually looped through all rows/columns, but the net effect here is...
tbl.Columns[0].DefaultValue; // blank for all columns
// Also tried accessing the schema table available through DataReader
IDataReader reader = tbl.CreateDataReader();
DataTable schemaTbl = reader.GetSchemaTable();
/*
* There are different schema fields here than in DataColumn,
* but DefaultValue still blank. I looped through them all but...
*/
schemaTbl.Rows[0]["DefaultValue"]; // blank for all columns
How can I read the default value from a column in my table using .NET (preferably without resorting to querying SQL's sys.* tables).
Clarification
When creating or altering a table, you can set a default value of a column if none is provided. I'm trying to get the default value.
Example:
CREATE TABLE Person
(
Id int NOT NULL,
LastName varchar(255) NOT NULL,
FirstName varchar(255) NOT NULL,
HomeState varchar(2) DEFAULT 'NY'
)
Now if I say, INSERT INTO Person (Id, LastName, FirstName) VALUES (1, 'Doe', 'John') the HomeState will be 'NY' even though I didn't set it.
Try using the Microsoft.SqlServer.Smo library as below:
using (var connection = new SqlConnection("connectionString"))
{
var serverConnection = new ServerConnection(connection);
var server = new Server(serverConnection);
var database = server.Databases["databaseName"];
var table = database.Tables["tableName"];
foreach (Column column in table.Columns)
{
Console.WriteLine($"{column.Name} - default constraint: {column.DefaultConstraint?.Text ?? "None"}");
}
Console.ReadLine();
}
EDIT
Thanks to #Crowcoder who suggested the below simplification:
var serverConnection = new ServerConnection("serverName", "username", "password");
var server = new Server(serverConnection);
var database = server.Databases["databaseName"];
var table = database.Tables["tableName"];
foreach (Column column in table.Columns)
{
Console.WriteLine($"{column.Name} - default constraint: {column.DefaultConstraint?.Text ?? "None"}");
}
Console.ReadLine();
Related
I´m trying to get my DataAdapter to update/insert new values to my table from another database (same table but different values) It should just copy the values from DB1 and insert/update them in DB2. I´m getting no compilation errors and the DataTables seems to have the correct values. But nothing gets updated or inserted.
So it should Fill a Datatable (dataTable1) from my database (DB1),
then set all rows to be modified and insert them into DB2
But, it wont work.
DataTable dataTable1 = new DataTable("counters");
string command = "SELECT * FROM counters;"; // same layout and name on both tables
using (MySqlConnection connectionFrom = new MySqlConnection(constringfrom)) // DB1
{
MySqlDataAdapter dataAdapter1 = new MySqlDataAdapter(command, connectionFrom);
dataAdapter1.Fill(dataTable1);
}
using (MySqlConnection ConnectionTo = new MySqlConnection(constringto)) // DB2
{
MySqlDataAdapter dataAdapter2 = new MySqlDataAdapter(command, ConnectionTo);
MySqlCommandBuilder cmb2 = new MySqlCommandBuilder(dataAdapter2);
dataAdapter2.UpdateCommand = cmb2.GetUpdateCommand();
dataAdapter2.InsertCommand = cmb2.GetInsertCommand();
foreach (DataRow r in dataTable1.Rows)
{
r.SetModified(); // just for testing purposes..
}
dataAdapter2.Update(dataTable1); // Correct values but no update to the DB :(
}
Console.WriteLine("Updating..... DONE"); // well... no
If I have a table with a TINYINT(1) column and select this column with a DataReader MySQL creates a boolean column.
var query = "SELECT column FROM table";
using (var reader = ExecuteReader(query))
{
var schemaTable = reader.GetSchemaTable();
var row = schemaTable.DefaultView[0];
Assert.AreEqual(typeof(bool), row["DataType"]);
}
However, If I have a query that does not work.
var query = "SELECT false";
using (var reader = ExecuteReader(query))
{
var schemaTable = reader.GetSchemaTable();
var row = schemaTable.DefaultView[0];
Assert.AreEqual(typeof(bool), row["DataType"]);
}
This test fails because the DataType is System.Int64
Is it possible to force a query to return TINYINT(1) values? In the big picture I let Entity Framework generate my model and I have some views with boolean columns that are created as System.Int64 and I suppose this would solve the issue.
Have you tried using CONVERT or CAST?
var query = "SELECT CONVERT(0,TINYINT(1))"
var query = "SELECT CAST(0 AS TINYINT(1))"
Edit: this seems to be a limitation of the CAST/ CONVERT functions in MySQL. A suggested solution is to create a function to do the casting
CREATE FUNCTION x_cast_to_tinyint(number bigint) RETURNS tinyint
BEGIN return number;
END
Then call the function
select x_cast_to_tinyint(d.tiny_int*1) as tiny
Reference: http://idiot-howto.blogspot.com/2008/07/mysql-cast-limitation.html#!/2008/07/mysql-cast-limitation.html
I want to get list of columns of a table using GetSchema method in ADO.Net, my code is:
var dtCols = con.GetSchema("Columns", new[] { "DBName", "TableName" });
And i get an empty DataTable, what is the problem?
You must specify a parameter for the "owner" restriction.
var dtCols = con.GetSchema("Columns", new[] { "DBName", null, "TableName" });
This is my complete solution.
You just need to provide tableName and connectionString to this method:
// I took HUGE help from this Microsoft website: - AshishK
// https://learn.microsoft.com/en-us/dotnet/api/system.data.sqlclient.sqlconnection.getschema?view=netframework-4.7.2#System_Data_SqlClient_SqlConnection_GetSchema_System_String_System_String___
public static List<string> GetAllColumnNamesOfATable(string tableName, string connectionString)
{
var allColumnNames = new List<string>();
using (var connection = new SqlConnection(connectionString))
{
// Connect to the database then retrieve the schema information.
connection.Open();
// You can specify the Catalog, Schema, Table Name, Column Name to get the specified column(s).
// You can use four restrictions for Column, so you should create a 4 members array.
String[] columnRestrictions = new String[4];
// For the array, 0-member represents Catalog; 1-member represents Schema;
// 2-member represents Table Name; 3-member represents Column Name.
// Now we specify the Table_Name and Column_Name of the columns what we want to get schema information.
columnRestrictions[2] = tableName;
DataTable allColumnsSchemaTable = connection.GetSchema("Columns", columnRestrictions);
foreach (DataRow row in allColumnsSchemaTable.Rows)
{
var columnName = row.Field<string>("COLUMN_NAME");
//You can capture other fields as well, like so:
//var dataType = row.Field<string>("DATA_TYPE");
//var characterMaxLength = row.Field<int?>("CHARACTER_MAXIMUM_LENGTH");
allColumnNames.Add(columnName);
}
connection.Close();
}
return allColumnNames;
}
PS: If you'd like to capture other information about the columns this way, the following fields are also available:
Could both of these answers be generalized a bit with:
dtCols = con.GetSchema("Columns", new[] {con.DataSource, null, "TableName"});
This is assuming that "TableName" is the name of the table that you want the schema for.
I had a similar problem, the following worked..
using(SqlCommand command = new SqlCommand(sqlText, con)) {
var sqlReader = command.ExecuteReader();
var a = sqlReader.GetColumnSchema();
}
I need to be able to determine from the DataTable returned by DbConnection.GetSchema() whether a particular column in a SQL Server table is identity/auto-increment or not. I cannot resort to querying the system tables directly.
Oddly, if I connect to SQL Server via ODBC, the returned datatype for such a column is returned as "int identity" (or "bigint identity", etc.) but if I use the native SQL Server driver, there appears to be no distinction between an "int" column and an "int identity" column. Is there some other way I can deduce that information?
DataTable has Columns property and DataColumn has a property indicating auto-increment:
bool isAutoIncrement = dataTable.Columns[iCol].AutoIncrement
See This StackOverflow thread
The GetSchema() function will not return the info that you want. Nor will examining the DataTable schema properties. You'll have to go to a lower level and that will depend on the DBMS and likely its version.
The member below retrieves all tables with identity columns and then looks to match a specific table passed as an argument. The code can be modified to either return all the tables or the query optimized to look only for the table of interest.
// see: https://stackoverflow.com/questions/87747/how-do-you-determine-what-sql-tables-have-an-identity-column-programatically
private static string GetIdentityColumnName(SqlConnection sqlConnection, Tuple<string, string> table)
{
string columnName = string.Empty;
const string commandString =
"select TABLE_SCHEMA, TABLE_NAME, COLUMN_NAME from INFORMATION_SCHEMA.COLUMNS "
+ "where TABLE_SCHEMA = 'dbo' and COLUMNPROPERTY(object_id(TABLE_NAME), COLUMN_NAME, 'IsIdentity') = 1 "
+ "order by TABLE_NAME";
DataSet dataSet = new DataSet();
SqlDataAdapter sqlDataAdapter = new SqlDataAdapter();
sqlDataAdapter.SelectCommand = new SqlCommand(commandString, sqlConnection);
sqlDataAdapter.Fill(dataSet);
if (dataSet.Tables.Count > 0 && dataSet.Tables[0].Rows.Count > 0)
{
foreach (DataRow row in dataSet.Tables[0].Rows)
{
// compare name first
if (string.Compare(table.Item2, row[1] as string, true) == 0)
{
// if the schema as specified, we need to match it, too
if (string.IsNullOrWhiteSpace(table.Item1) || string.Compare(table.Item1, row[0] as string) == 0)
{
columnName = row[2] as string;
break;
}
}
}
}
return columnName;
}
I have run into the same. As far as I discovered here "An auto increment column is implemented differently depending upon the type of database you are working with. It isn't exposed via GetOleDbSchema.".
I didn't find any other way than #kelloti mentioned. So at the moment I'm fine with this solution because at the moment I need to know if column is .AutoIncrement. I already have the table in memory so I don't need to query the database again.
#pesaak Please convert this answer into a comment now that you should have enough reputation.
I have a database table with one field, called ID, being an auto increment integer.
Using a TableAdapter I can read and modify existing rows as well as create new ones.
However if I try to modify a newly inserted row I get an DBConcurrencyException:
OleDbConnection conn = new OleDbConnection(#"Provider=Microsoft.Jet.OLEDB.4.0;Data Source=Shift.mdb;Persist Security Info=True");
ShiftDataSetTableAdapters.ShiftTableAdapter shiftTA = new ShiftDataSetTableAdapters.ShiftTableAdapter();
shiftTA.Connection = conn;
ShiftDataSet.ShiftDataTable table = new ShiftDataSet.ShiftDataTable();
ShiftDataSet.ShiftRow row = table.NewShiftRow();
row.Name = "life";
table.Rows.Add(row);
shiftTA.Update(row); // row.ID == -1
row.Name = "answer"; // <-- all fine up to here
shiftTA.Update(row); // DBConcurrencyException: 0 rows affected
Separate question, is there any static type of the NewShiftRow() method I can use so that I don't have to create table everytime I want to insert a new row.
I guess the problem in the code comes from row.ID that is still -1 after the first Update() call. The Insert is successful and in the database the row has a valid value of ID.
How can I get that ID so that I can continue with the second Update call?
Update:
IT looks like this could have been done automatically using this setting.
However according to the answer on msdn social, OLEDB drivers do not support this feature.
Not sure where to go from here, use something else than oledb?
Update:
Tried SQLCompact but discovered that it had the same limitation, it does not support multiple statements.
Final question: is there any simple(single file based) database that would allow you to get the values of a inserted row.
Try this http://support.microsoft.com/kb/815629 , the sample code is in VB.NET though.
Or if multiline query is accepted in MS Access and it has built-in function/variable for retrieving the last id, use this (the database is SQLite though): anyway see why I get this "Concurrency Violation" in these few lines of code??? Concurrency violation: the UpdateCommand affected 0 of the expected 1 records , try to google for the function
[EDIT: Works on my Machine, I don't have SQL Server Compact, but I didn't use multi-statement]
public Form1()
{
InitializeComponent();
var c = Connect();
var da = new SqlDataAdapter("select emp_id, emp_firstname, emp_lastname from emp where 1 = 0", c);
var b = new SqlCommandBuilder(da);
var getIdentity = new SqlCommand("SELECT CAST(##IDENTITY AS INT)", c);
da.InsertCommand = b.GetInsertCommand();
da.UpdateCommand = b.GetUpdateCommand();
da.DeleteCommand = b.GetDeleteCommand();
da.RowUpdated += (xsender, xe) =>
{
if (xe.Status == UpdateStatus.Continue && xe.StatementType == StatementType.Insert)
{
xe.Row["emp_id"] = (int)getIdentity.ExecuteScalar();
}
};
var dt = new DataTable();
da.Fill(dt);
var nr = dt.NewRow();
nr["emp_firstname"] = "john";
nr["emp_lastname"] = "lennon";
var nrx = dt.NewRow();
nrx["emp_firstname"] = "paul";
nrx["emp_lastname"] = "mccartney";
dt.Rows.Add(nr);
dt.Rows.Add(nrx);
da.Update(dt);
dt.AcceptChanges();
nrx["emp_lastname"] = "simon";
da.Update(dt);
nr["emp_lastname"] = "valjean";
da.Update(dt);
}
SqlConnection Connect()
{
return new SqlConnection(#"data source=.\SQLEXPRESS;Database=Test;uid=sa;pwd=hey");
}
Why not select the MAX(RowId), as your RowId should increment for each INSERT? Is this possible for you?
As for your final answer, SQLite might be the perfect tool for you. I hope so! And it has its own .NET Data Provider, so no need for OLEDB or ODBC providers.