Possible that I'm overlooking something here, but when I load a DataTable from SqlCommand.ExecuteReader() I am finding that the MaxLength property of my String field is ignored and reset to '50' in the resulting DataColumn. Here's an example table:
CREATE TABLE MySqlServerTable (
[instance_id] INT NOT NULL,
[field_id] INT NOT NULL,
[value] VARCHAR (MAX) NOT NULL);
and the method I'm using to initialize the local DataTable is
public DataTable GetDT()
{
string query = "SELECT top 0 * FROM MySqlServerTable;";
SqlCommand cmd = new SqlCommand(query, _msSqlConn);
DataTable dt = new DataTable();
dt.Load(cmd.ExecuteReader());
return dt;
}
where _msSqlConn is the already opened SqlConnection. If I then scroll through the DataColumns, I find that the value column (the string column) has been assigned a MaxLength of 50.
Console.WriteLine(GetDT().Columns["value"].MaxLength);
So what gives?
Kinda related to this attempted answer but still unresolved.
What's the right way to do this such that my string column MaxLengths are properly retrieved from the SqlServer2012 DB?
I think you need to use DataAdapter.FillSchema() to retrieve meta data. Try this something like this:
_msSqlConn.Open();
using (SqlDataAdapter da = new SqlDataAdapter(query, _msSqlConn))
{
DataTable dt = new DataTable();
da.FillSchema(dt, SchemaType.Mapped); //Or may be SchemaType.Source
return dt;
}
Related
I am trying to use SqlBulkCopy with .net core, but since I use a geometry column the following code requires Microsoft.SqlServer.Types which is not fully .net core compatible, especially on Linux.
using (SqlDataAdapter adapter = new SqlDataAdapter("SELECT TOP 0 * FROM " + tableName, sqlConnection))
{
DataTable dt = new DataTable();
adapter.Fill(dt);
return dt;
}
Without the dependency the Fill() fails as it cannot find the type.
Normally I use NetTopologySuite.IO.SqlServerBytes, but in this case the magic happens somewhere in the SqlDataAdapter and I don't know how to overwrite it.
I tried to create the DataTable columns manually without using Fill(), but it seems whatever type I specify I later get an error in the SqlBulkCopy.
The given value of type XYZ from the data source cannot be converted to type udt of the specified target column
I tried with SqlBytes and byte[], but nothing seems to work.
Update 1:
I got it working with a manually created DataTable with byte[] as the type for that column.
Still it would be nice to have a way using adapter.Fill(dt); or similar so that I do not have to manually list all the columns.
using (SqlDataAdapter da = new SqlDataAdapter("SELECT TOP 5 * FROM " + tableName, sqlConnection)
{
using (SqlCommandBuilder builder = new SqlCommandBuilder(da))
{
DataTable dt = new DataTable();
DataSet dataSet = new DataSet();
dt = dgPurchase.DataSource as DataTable;
//here assign the data table
da.Fill(dataSet, dt);
da.UpdateCommand = builder.GetUpdateCommand(true);
//da.InsertCommand = builder.GetInsertCommand(true);
//If new row then open it
da.Update(dataSet, dt);
}
}
// Can update though parameters .Follow the link
https://learn.microsoft.com/en-us/dotnet/framework/data/adonet/updating-data-sources-with-dataadapters
//Useing inline code .Follow the link
http://csharp.net-informations.com/dataadapter/updatecommand-sqlserver.htm
-Hello, World!-
I am working on a C# with ASP.NET project and I have run into a snag. The project is to dynamically load metadata and records from tables to edit them without statically defining what tables could be edited. As such I need to get the schema/metadata of different tables.
Here's what I have so far:
// initialize the connection
using (SqlConnection con = new SqlConnection(metadata.DatabaseString))
{
// open the connection
con.Open();
// initialize a new SqlCommand to get the schema
SqlCommand command = con.CreateCommand();
command.CommandType = CommandType.Text;
// 0 = 1 ensures it's always an empty data set
command.CommandText = "SELECT * FROM " + metadata.TableName + " WHERE 0=1;";
// set to SchemaOnly to improve performance (i think)
SqlDataReader reader = command.ExecuteReader(CommandBehavior.SchemaOnly);
// GetSchemaTable() gets the table's metadata
DataTable dataTable = reader.GetSchemaTable();
// loops through all the rows of the data table
foreach (DataRow row in dataTable.Rows)
{
// field names found here: https://msdn.microsoft.com/en-us/library/system.data.datatablereader.getschematable(v=vs.110).aspx#Remarks
metadata.ColumnMetadata.Add(new ColumnWrapper()
{
ColumnType = GetTypeFromSql(row.Field<string>("DataTypeName")),
ColumnRawType = row.Field<string>("DataTypeName"),
ColumnName = row.Field<string>("ColumnName"),
ByteSize = row.Field<int>("ColumnSize"),
IsKey = row.Field<bool?>("IsKey") ?? false
});
}
}
The issue is the IsKey field is always empty. My SQL Server Table was created using the following query:
CREATE TABLE [dbo].[Dtm_LKUP_Role] (
[DtmRoleId] INT IDENTITY (1, 1) NOT NULL,
[RoleName] VARCHAR(32) NOT NULL,
[IsActive] BIT DEFAULT ((1)) NOT NULL,
PRIMARY KEY CLUSTERED ([DtmRoleId] ASC)
);
Here's what I have tried so far:
Use a different table, same results
Access dataTable.Columns["IsKey"]
No matter where I look I can't find the information I need. Does anyone have any ideas on what could cause this? In case it is relevant, I am using an MDF file and the LocalDB for my database connection rather than a live server.
Houston, we have lift off!
Based on the help from mjwills, I managed to get it working by changing my code to the following:
// initialize the connection
using (SqlConnection con = new SqlConnection(metadata.DatabaseString))
{
// open the connection
con.Open();
// initialize a new SqlCommand to get the schema. 0 = 1 ensures an empty data set
SqlDataAdapter adapter = new SqlDataAdapter("SELECT * FROM " + metadata.TableName + " WHERE 0=1", con);
// GetSchemaTable() gets the table's metadata
DataTable dataTable = new DataTable();
// tell the adapater to fill in the missing schema
adapter.MissingSchemaAction = MissingSchemaAction.AddWithKey;
// fill the datatable with the schema
adapter.FillSchema(dataTable, SchemaType.Mapped);
// loops through all the rows of the data table
foreach (DataColumn column in dataTable.Columns)
{
// field names found here: https://msdn.microsoft.com/en-us/library/system.data.datatablereader.getschematable(v=vs.110).aspx#Remarks
metadata.ColumnMetadata.Add(new ColumnWrapper()
{
ColumnType = column.DataType,
ColumnName = column.ColumnName,
ByteSize = column.MaxLength,
IsKey = dataTable.PrimaryKey.Contains(column)
});
}
}
I appreciate all the help from those who commented on my original question :)
I have a several data column from my MySQL database, the type is tinyint(1)
my code in form
DataTable dt = new DataTable();
dt = PC.getValue(textBox.Text);
dataGridView1.DataSource = dt;
dataGridView1.AutoResizeColumns();
and MyQuery
public DataTable getValue(string yearmonth)
{
connSIMRS.Open();
MySqlCommand command = new MySqlCommand();
string sql = "select * from table1 where yearmonth= '"+yearmonth+"'";
command.CommandText = sql;
command.Connection = connSIMRS;
//command.EndExecuteNonQuery();
MySqlDataAdapter da = new MySqlDataAdapter(command);
DataTable dt = new DataTable();
da.Fill(dt);
//MessageBox.Show("");
connSIMRS.Close();
return dt;
}
can't post a picture but it turn into a checkbox. Could it be turn into a boolean? How can I show it as a string?
Yes, it is being turned into a boolean. Many ways to turn it into a string. Use a custom format, custom column, cast it from the database, etc.
Force MySql Connector to do it:
According to the MySQL Connector Docs , just add TreatTinyAsBoolean=false to your connection string.
However, I would just use a custom column in the GridView, as most cases I would want the booleans to be displayed as a checkbox, and this allows finer control over which ones I want displayed as text vs checkbox.
Add the obligitory string sql = "select * from table1 where yearmonth= '"+yearmonth+"'"; is bad. Don't do that EVER.
a question from a newbie to c# and apologies for the lenght of it. I have the following scenario. I have a small console application that populates a datatable by connecting to an external system and then needs to update existing records and insert new ones into an oracle table. The columns in the datatable are not named the same as the oracle table columns and not in the same order. I read another post on here with a similar scenario (loading from a file into a table) and it mentioned that doing an update/insert with an OracleDataAdapter would work. A simplified datatable and oracle table are
DataTable table = new DataTable();
table.Columns.Add("Product", typeof(String));
table.Columns.Add("Price", typeof(double));
table.Columns.Add("Effective_Date", typeof(DateTime));
//sample data
table.Rows.Add("abcd", 1.011, DateTime.Today);
table.Rows.Add("efg", 1.00, DateTime.Today);
table.Rows.Add("hijk", 20, DateTime.Today);
The oracle table has the structure
ITEM VARCHAR2(20 BYTE) NOT NULL ENABLE,
EFF_DATE DATE,
VALUE NUMBER
I have tried the following code to use the datatable and an adapter to update the oracle table but I'm missing something. I'm also wondering if I'm barking up the wrong tree. The majority of examples I have seen of using a dataadapter first does a select from the table and then puts the results into a grid where a user would be able to add, update, insert, or delete records and then uses the dataadapter to update the table. In my case I'm wondering if I get it to work if all records in the datatable will be treated as an insert anyway as there is no connection between the datatable and the oracle table.
I'm using the Oracle.ManagedDataAccess.Client to connect and do the updates
public static void UpdateOrSaveItems(DataTable dt)
{
String insert_statement, update_statement, select_statement;
select_statement = "SELECT * from items";
insert_statement = "INSERT INTO items (item, eff_date, value) values (:pInsItem,:pInsEffDate,:pInsValue)";
update_statement = "UPDATE items set eff_date = :pUpdEffDate, value = :pUpdValue where item = :pUpdItem";
using (OracleConnection conn = theDatabase.ConnectToDatabase())
{
using (OracleDataAdapter oraAdapter = new OracleDataAdapter(select_statement, conn))
{
//build update/insert commands and parameters
oraAdapter.UpdateCommand = new OracleCommand(update_statement, conn);
oraAdapter.InsertCommand = new OracleCommand(insert_statement, conn);
oraAdapter.UpdateCommand.BindByName = true;
oraAdapter.InsertCommand.BindByName = true;
OracleParameter pUpdItem = new OracleParameter("pUpdItem", OracleDbType.Varchar2);
pUpdItem.SourceColumn = dt.Columns[0].ColumnName;
OracleParameter pUpdEffDate = new OracleParameter("pUpdEffDate", OracleDbType.Date);
pUpdEffDate.SourceColumn = dt.Columns[2].ColumnName;
OracleParameter pUpdValue = new OracleParameter("pUpdValue", OracleDbType.Double);
pUpdValue.SourceColumn = dt.Columns[1].ColumnName;
OracleParameter pInsItem = new OracleParameter("pInsItem", OracleDbType.Varchar2);
pUpdItem.SourceColumn = dt.Columns[0].ColumnName;
OracleParameter pInsEffDate = new OracleParameter("pInsEffDate", OracleDbType.Date);
pInsEffDate.SourceColumn = dt.Columns[2].ColumnName;
OracleParameter pInsValue = new OracleParameter("pInsValue", OracleDbType.Double);
pInsValue.SourceColumn = dt.Columns[1].ColumnName; oraAdapter.UpdateCommand.Parameters.Add(pUpdItem);
oraAdapter.UpdateCommand.Parameters.Add(pUpdEffDate);
oraAdapter.UpdateCommand.Parameters.Add(pUpdValue);
oraAdapter.InsertCommand.Parameters.Add(pInsItem);
oraAdapter.InsertCommand.Parameters.Add(pInsEffDate);
oraAdapter.InsertCommand.Parameters.Add(pInsValue);
oraAdapter.Update(dt);
}
}
}
When I run this I get an error that I cannot insert a null into column that is defined as a key. In the datatable none of them are null. I'm missing something on telling it where the data is but am unsure what it is. Also wondering if this is the right way to do this sort of thing. I wanted to avoid
loop through datatable
select to see if record is in oracle table
if in table update else insert
because the volume of records could a couple of hundred thousand and wasn't sure what the performance would be like.
Are you initializing ColumnName properties of the DataTable object that you are passing in? If not, they could be reading as null.
For instance
public static void Main()
{
Datatable myDataTable = new DataTable();
myDataTable.Columns = new Columns[3];
myDataTable.Columns[0].ColumnName = "Employees";
myDataTable.Columns[1].ColumnName = "Salary";
myDataTable.Columns[2].ColumnName = "Department";
UpdateOrSaveItems(myDataTable);
}
I found the error. I hadn't set the source column on one of my insert parameters. I had set the source column on the pUdpItem twice instead of setting it for pUdpItem and pInsItem
I need to get the real column name from DataTable which I filled it by FillSchema() method,
this is the query I used (SELECT ID AS [SNO],CATEG_NAME AS [Category] FROM Categories)
var dt = new DataTable();
SqlDataAdapter da = new SqlDataAdapter("SELECT ID AS [SNO],CATEG_NAME AS [Category] FROM Categories",conn);
da.FillSchema(dt, SchemaType.Source);
dt.FillBySql(sql);
When I check dt.columns I find that there are 2 columns with the same alias name which mentioned in query. What can I do to get the real column names(id, categ_name)?
I tried to add one column with the name same as alias column name but just let it blank. see column
then I execute this query:
SELECT
status_action,
isread,
CONCAT(
FLOOR(HOUR(TIMEDIFF(work_given, sysdate())) / 24),
' days, ',
MOD(HOUR(TIMEDIFF(work_given, sysdate())), 24),
' hours, ',
MINUTE(TIMEDIFF(work_given, sysdate())),
' minutes') **as** **workgivendays**
from progressreport
And then I call it use MySqlDataReader
like this :
MySqlDataReader mdr = cmd.ExecuteReader();
workgivendays = mdr.GetString("workgivendays");
and it works
I believe that your best bet is to alter them in C#, not alias them in SQL. This is not a best practice, but this workaround has worked for me.
On a databound datagridview, you have to change the underlying datatable. This sample will change the name of the column from 'ID' to 'SNO'. Make sure you alter a copy the table, so the original remains unchanged.
var dt = new DataTable();
SqlDataAdapter da = new SqlDataAdapter("SELECT ID,CATEG_NAME AS [Category] FROM Categories",conn);
da.FillSchema(dt, SchemaType.Source);
dt.FillBySql(sql);
DataTable clonedWithColumnsChanged= dt.Copy();
clonedWithColumnsChanged.Columns["ID"].ColumnName = "SNO";
dgvReports.DataSource = clonedWithColumnsChanged;