We recently upgraded our solution to the new Managed ODP.Net (v4.121.2.0, from v4.121.1.0) and have come across a problem with udpating to CLOB fields when the field has between 1001 and 4000 characters. When you attempt to do that an error ORA-1461 is thrown from ODP.Net.
Run the same code and data, using the earlier version of ODP.Net and it works fine. Also, you can insert records with 1001 and 4000 characters, you just can't update them.
I've created a sample program in C# that demonstrates the problem. The program does the following:
It creates a 3 column table, one column a CLOB, in the database
It creates an in-memory .Net DataSet object to match.
Creates a new record in the DataSet with 1400 characters in the CLOB field.
Saves the DataSet to the database with an INSERT statement.
Updates the CLOB field in the DataSet with some new data, again 1400 characters worth of data.
Saves the DataSet to the database, and the ORA-1461 is thrown.
I don't have an Oracle Support account, so where do you report to Oracle problems with ODP.Net?
Sample C# code that demonstrates the problem:
using System;
using System.Data;
using System.Data.Common;
using System.Diagnostics;
using Oracle.ManagedDataAccess.Client;
class CLOBTest
{
string _TableName = "CLOBTEST";
string _ServerName = "servername";
string _UserName = "username";
string _Password = "password";
public void CLOBTest1()
{
// Create a physical data table, if needed, in the Oracle DB that has the CLOB column
CreateTable();
// Create a dataset for the CLOBTEST table, fill it with a new row
DataSet CLOBInfo = BuildCLOBTestDataSet();
DataTable CLOBTable = CLOBInfo.Tables[_TableName];
DataRow CLOBRow = CLOBTable.NewRow();
CLOBRow["ACTION_CODE"] = DateTime.Now.ToString("s");
CLOBRow["DESCRIPTION"] = "CLOB Slim Test";
// The size of text in the CLOB field is critical to reproducing this defect.
// It *only* happens when the field has between 1001 and 4000 characters.
int LOBFieldSize = 1400;
string CLOBText = DateTime.Now.ToString("s") + " " + new string('-', LOBFieldSize);
CLOBRow["SCRIPT_TEXT"] = CLOBText.Substring(0, LOBFieldSize);
CLOBTable.Rows.Add(CLOBRow);
// Add that row to the DB, and then mark the DS with AcceptChanges
InsertRow(CLOBInfo);
// Update that row with some new data.
CLOBText = DateTime.Now.ToString("s") + " :: " + CLOBText;
CLOBRow["SCRIPT_TEXT"] = CLOBText.Substring(0, LOBFieldSize);
// Error (ORA-1461) happens in the UPDATE when the CLOB has 1001 - 4000 characters in it.
UpdateRow(CLOBInfo);
}
private void CreateTable()
{
if (TableExists())
return;
using (OracleConnection oc = OpenConnection())
{
using (OracleCommand ocmd = oc.CreateCommand())
{
string SQL = "CREATE TABLE " + _TableName + " (ACTION_CODE VARCHAR2(30) NOT NULL, DESCRIPTION VARCHAR2(50) NOT NULL, SCRIPT_TEXT CLOB, CONSTRAINT CLOBTEST_PK PRIMARY KEY (ACTION_CODE))";
ocmd.CommandText = SQL;
ocmd.ExecuteNonQuery();
AddLogMessage("Table created.");
}
}
}
private bool TableExists()
{
using (OracleConnection oc = OpenConnection())
{
using (OracleCommand ocmd = oc.CreateCommand())
{
string SQL = "SELECT COUNT(*) FROM USER_TABLES WHERE TABLE_NAME = '" + _TableName + "'";
ocmd.CommandText = SQL;
object teRaw = ocmd.ExecuteScalar();
bool te = (bool)(int.Parse(teRaw.ToString()) > 0);
AddLogMessage("Table exists? " + te.ToString());
return te;
}
}
}
private void InsertRow(DataSet CLOBInfo)
{
string SQL = "INSERT INTO " + _TableName + " (ACTION_CODE, DESCRIPTION, SCRIPT_TEXT) VALUES (:pACTION_CODE, :pDESCRIPTION, :pSCRIPT_TEXT)";
using (OracleConnection oc = OpenConnection())
{
using (OracleDataAdapter oda = new OracleDataAdapter())
{
using (OracleCommand ocmd = oc.CreateCommand())
{
CreateDataParameters(ocmd);
ocmd.CommandText = SQL;
oda.InsertCommand = ocmd;
DataRow[] updRows = CLOBInfo.Tables[_TableName].Select(null, null, DataViewRowState.Added);
if (updRows.Length > 0)
{
int rc = oda.Update(updRows);
CLOBInfo.AcceptChanges();
AddLogMessage("Row inserted into CLOBTEST. rc = " + rc.ToString());
}
else
AddLogMessage("No rows to insert.");
}
}
}
}
private void UpdateRow(DataSet CLOBInfo)
{
string SQL = "UPDATE " + _TableName + " SET ACTION_CODE = :pACTION_CODE, DESCRIPTION = :pDESCRIPTION, SCRIPT_TEXT = :pSCRIPT_TEXT WHERE ACTION_CODE = :pOLDACTION_CODE";
using (OracleConnection oc = OpenConnection())
{
using (OracleDataAdapter oda = new OracleDataAdapter())
{
using (OracleCommand ocmd = oc.CreateCommand())
{
ocmd.CommandText = SQL;
CreateDataParameters(ocmd);
OracleParameter kp = new OracleParameter();
kp.ParameterName = "pOLDACTION_CODE";
kp.SourceColumn = "ACTION_CODE";
kp.SourceVersion = DataRowVersion.Original;
ocmd.Parameters.Add(kp);
oda.UpdateCommand = ocmd;
DataRow[] updRows = CLOBInfo.Tables[_TableName].Select(null, null, DataViewRowState.ModifiedCurrent);
if (updRows.Length > 0)
{
int rc = oda.Update(updRows);
CLOBInfo.AcceptChanges();
AddLogMessage("CLOBTEST row updated. rc = " + rc.ToString());
}
else
AddLogMessage("No rows to update.");
}
}
}
}
private void CreateDataParameters(OracleCommand ocmd)
{
OracleParameter pActionCode = new OracleParameter();
pActionCode.ParameterName = "pACTION_CODE";
pActionCode.SourceColumn = "ACTION_CODE";
ocmd.Parameters.Add(pActionCode);
OracleParameter pDescription = new OracleParameter();
pDescription.ParameterName = "pDESCRIPTION";
pDescription.SourceColumn = "DESCRIPTION";
ocmd.Parameters.Add(pDescription);
OracleParameter pScriptText = new OracleParameter();
pScriptText.ParameterName = "pSCRIPT_TEXT";
pScriptText.SourceColumn = "SCRIPT_TEXT";
ocmd.Parameters.Add(pScriptText);
}
private DataSet BuildCLOBTestDataSet()
{
DataSet ads = new DataSet();
DataTable at = new DataTable("CLOBTEST");
DataColumn ac = at.Columns.Add("ACTION_CODE", typeof(string));
at.Columns.Add("DESCRIPTION", typeof(string));
at.Columns.Add("SCRIPT_TEXT", typeof(string));
at.PrimaryKey = new DataColumn[] { ac };
ads.Tables.Add(at);
return ads;
}
private OracleConnection OpenConnection()
{
OracleConnection oc = null;
try
{
OracleClientFactory ocf = new OracleClientFactory();
DbConnectionStringBuilder csb = ocf.CreateConnectionStringBuilder();
csb["Data Source"] = _ServerName;
csb["User ID"] = _UserName;
csb["Password"] = _Password;
string cs = csb.ConnectionString;
oc = new OracleConnection(cs);
oc.Open();
AddLogMessage("Connection opened.");
}
catch (Exception ex)
{
AddLogMessage("Error Opening Connection! " + ex.Message);
throw;
}
return oc;
}
private void AddLogMessage(string msg)
{
Debug.WriteLine(string.Format("{0:T} - {1}", DateTime.Now, msg));
}
}
Two workarounds are shown in this thread: https://community.oracle.com/thread/3649551
Workaround one:
Configure your CLOB parameters as OracleDbType.Clob and ParameterDirection.InputOutput.
Workaround two:
Configure your CLOB parameters as OracleDbType.Clob and explicitly set the value to a OracleClob object.
Fix:
The bug is now known to Oracle, so hopefully a fixed version will be released soon.
Edit: Patch 20361140 should fix it.
Related
I am getting data from excel and showing it in DataGridWiew.
I have two textboxes, one is for starting index for first record and other is for last record.
Code works fine. But lets suppose starting record is 1 and ending is 10 when I change 10 to 1 or 2 it gives me an error in this line:
adapter.Fill(dataTable);
Full Code is below:
public DataSet Parse(string fileName)
{
string connectionString = string.Format("provider = Microsoft.Jet.OLEDB.4.0; data source = {0}; Extended Properties = Excel 8.0;", fileName);
DataSet data = new DataSet();
foreach (var sheetName in GetExcelSheetNames(connectionString))
{
using (OleDbConnection con = new OleDbConnection(connectionString))
{
string query = "";
var dataTable = new DataTable();
if(tbStarting.Text.Trim()=="" && tbEnding.Text.Trim() == "")
{
query = string.Format("SELECT * FROM [{0}]", sheetName);
}
else
{
query = string.Format("SELECT * FROM [{0}] where SrNo between " + int.Parse(tbStarting.Text.Trim()) + " and " + int.Parse(tbEnding.Text.Trim()) + " order by SrNo", sheetName);
}
con.Open();
OleDbDataAdapter adapter = new OleDbDataAdapter(query, con);
adapter.Fill(dataTable);
data.Tables.Add(dataTable);
con.Close();
}
}
return data;
}
static string[] GetExcelSheetNames(string connectionString)
{
OleDbConnection con = null;
DataTable dt = null;
con = new OleDbConnection(connectionString);
con.Open();
dt = con.GetOleDbSchemaTable(OleDbSchemaGuid.Tables, null);
if (dt == null)
{
return null;
}
String[] excelSheetNames = new String[dt.Rows.Count];
int i = 0;
foreach (DataRow row in dt.Rows)
{
excelSheetNames[i] = row["TABLE_NAME"].ToString();
i++;
}
return excelSheetNames;
}
Why this is happening please help me?
Looking at the code, it seems that your procedure is working when you ask to retrieve all the record in each table. But you are not showing which table (Sheet) is actually used afterwars.
Chances are, you are using the first one only.
When you submit some parameters, only one of the tables (Sheets) can fulfill those requirements. The other(s) don't, possibly because a field named [SrNo] is not present.
This causes the More Parameters Required error when trying to apply a filter.
Not related to the error, but worth noting: you don't need to recreate the whole DataSet + DataTables to filter your DataSources.
The DataSet.Tables[N].DefaultView.RowFilter can be used to get the same result without destroying all the objects each time a filter is required.
RowFilter has some limitations in the language (e.g. does not support BETWEEN, Field >= Value1 AND Field <= Value2 must be used), but it's quite effective.
This is a possible setup:
(xDataSet is a placeholder for your actual DataSet)
//Collect the values in the TextBoxes in a string array
private void button1_Click(object sender, EventArgs e)
{
string[] Ranges = new string[] { tbStarting.Text.Trim(), tbEnding.Text.Trim() };
if (xDataSet != null)
FilterDataset(Ranges);
}
private void FilterDataset(string[] Ranges)
{
if (string.IsNullOrEmpty(Ranges[0]) & string.IsNullOrEmpty(Ranges[1]))
xDataSet.Tables[0].DefaultView.RowFilter = null;
else if (string.IsNullOrEmpty(Ranges[0]) | string.IsNullOrEmpty(Ranges[1]))
return;
else if (int.Parse(Ranges[0]) < int.Parse(Ranges[1]))
xDataSet.Tables[0].DefaultView.RowFilter = string.Format("SrNo >= {0} AND SrNo <= {1}", Ranges[0], Ranges[1]);
else
xDataSet.Tables[0].DefaultView.RowFilter = string.Format("SrNo = {0}", Ranges[0]);
this.dataGridView1.Update();
}
I've modified your code you code a bit to handle those requirements.
(I've left here those filters anyway; they're not used, but if you still want them, they are in a working condition)
DataSet xDataSet = new DataSet();
string WorkBookPath = #"[Excel WorkBook Path]";
//Query one Sheet only. More can be added if necessary
string[] WBSheetsNames = new string[] { "Sheet1" };
//Open the Excel document and assign the DataSource to a dataGridView
xDataSet = Parse(WorkBookPath, WBSheetsNames, null);
dataGridView1.DataSource = xDataSet.Tables[0];
dataGridView1.Refresh();
public DataSet Parse(string fileName, string[] WorkSheets, string[] ranges)
{
if (!File.Exists(fileName)) return null;
string connectionString = string.Format("provider = Microsoft.ACE.OLEDB.12.0; " +
"data source = {0}; " +
"Extended Properties = \"Excel 12.0;HDR=YES\"",
fileName);
DataSet data = new DataSet();
string query = string.Empty;
foreach (string sheetName in GetExcelSheetNames(connectionString))
{
foreach (string WorkSheet in WorkSheets)
if (sheetName == (WorkSheet + "$"))
{
using (OleDbConnection con = new OleDbConnection(connectionString))
{
DataTable dataTable = new DataTable();
if ((ranges == null) ||
(string.IsNullOrEmpty(ranges[0]) || string.IsNullOrEmpty(ranges[1])) ||
(int.Parse(ranges[0]) > int.Parse(ranges[1])))
query = string.Format("SELECT * FROM [{0}]", sheetName);
else if ((int.Parse(ranges[0]) == int.Parse(ranges[1])))
query = string.Format("SELECT * FROM [{0}] WHERE SrNo = {1}", sheetName, ranges[0]);
else
query = string.Format("SELECT * FROM [{0}] WHERE (SrNo BETWEEN {1} AND {2}) " +
"ORDER BY SrNo", sheetName, ranges[0], ranges[1]);
con.Open();
OleDbDataAdapter adapter = new OleDbDataAdapter(query, con);
adapter.Fill(dataTable);
data.Tables.Add(dataTable);
};
}
}
return data;
}
static string[] GetExcelSheetNames(string connectionString)
{
string[] excelSheetNames = null;
using (OleDbConnection con = new OleDbConnection(connectionString))
{
con.Open();
using (DataTable dt = con.GetOleDbSchemaTable(OleDbSchemaGuid.Tables, null))
{
if (dt != null)
{
excelSheetNames = new string[dt.Rows.Count];
for (int i = 0; i < dt.Rows.Count; i++)
{
excelSheetNames[i] = dt.Rows[i]["TABLE_NAME"].ToString();
}
}
}
}
return excelSheetNames;
}
I need to load data from multiple excel files to single table in sql server. But, I may get different headers in different files. Also, no of columns in excel will be less when compared to table. So, I'm trying to take columns from excel files and comparing against system table in database to get the corresponding column name. I'm using this inside a script task in SSIS package. Please see the code and sample data given below. Getting an error while doing column mapping.
Name EmpId Salary
Anna PD200 200
Julie PD300 300
Name EmpId Sal
Maria PD400 400
Treeza PD500 500
CREATE TABLE [dbo].[testLoad]
(
[Name] [nvarchar](255) NULL,
[EmpId] [nvarchar](255) NULL,
[Salary] [nvarchar](50) NULL
)
public void Main()
{
// TODO: Add your code here
string filepath = Dts.Variables["User::var_File_Path"].Value.ToString();
string tablename = Dts.Variables["User::var_Tbl_Name"].Value.ToString();
string filename = Dts.Variables["User::var_File_Name"].Value.ToString();
string tbl = tablename.Replace("[", "");
tbl = tbl.Replace("]", "");
tbl = tbl.Replace("dbo.", "");
SqlConnection sqlconnection = new SqlConnection();
sqlconnection = (SqlConnection)(Dts.Connections["ADOAUDIT"].AcquireConnection(Dts.Transaction));
string ConStr;
string HDR;
HDR = "YES";
ConStr = "Provider=Microsoft.ACE.OLEDB.12.0; Data Source=" + filepath + "; Extended Properties=\"EXCEL 12.0 XML; HDR="+HDR+"\";";
OleDbConnection cnn = new OleDbConnection(ConStr);
cnn.Open();
DataTable dtSheet = cnn.GetOleDbSchemaTable(OleDbSchemaGuid.Tables, null);
string sheetname = "";
string ExcelColumn = "";
string SqlColumn = "";
string SqlColumns = "";
string ExcelCol = "";
string query = "";
string querycol = "";
foreach (DataRow drSheet in dtSheet.Rows)
{
sheetname = drSheet["TABLE_NAME"].ToString();
OleDbCommand oconn = new OleDbCommand(" top 1 * from [" + sheetname + "]", cnn);
OleDbDataAdapter adp = new OleDbDataAdapter(oconn);
DataTable dt = new DataTable();
adp.Fill(dt);
cnn.Close();
for (int i = 0; i < dt.Columns.Count; i++)
{
ExcelCol = dt.Columns[i].ColumnName;
ExcelCol = ExcelCol.Substring(0, 5);
querycol = "select COLUMN_NAME from INFORMATION_SCHEMA.COLUMNS " +
"where TABLE_NAME = '" + tbl + "' " +
"and COLUMN_NAME like '" + ExcelCol + "%'";
SqlCommand sqlCommand = new SqlCommand(querycol, sqlconnection);
SqlColumn = (string)sqlCommand.ExecuteScalar();
if (!String.IsNullOrEmpty(SqlColumn))
{
SqlColumns = SqlColumns + "'" + SqlColumn + "',";
ExcelColumn = ExcelColumn + "'" + dt.Columns[i].ColumnName + "',";
}
}
SqlColumns = SqlColumns.TrimEnd(',');
ExcelColumn = ExcelColumn.TrimEnd(',');
query = "select " + ExcelColumn + " from [" + sheetname + "]";
OleDbConnection conn1 = new OleDbConnection(ConStr);
conn1.Open();
OleDbCommand oconn1 = new OleDbCommand(query, conn1);
OleDbDataAdapter adp1 = new OleDbDataAdapter(oconn1);
DataTable dt1 = new DataTable();
adp1.Fill(dt1);
conn1.Close();
//Load Data from DataTable to SQL Server Table.
using (SqlBulkCopy BC = new SqlBulkCopy(sqlconnection))
{
BC.DestinationTableName = tablename;
foreach (var column in dt1.Columns)
{
BC.ColumnMappings.Add(column.ToString(), SqlColumns.ToString());
}
BC.WriteToServer(dt);
}
sqlconnection.Close();
}
Dts.TaskResult = (int)ScriptResults.Success;
}
#region ScriptResults declaration
/// <summary>
/// This enum provides a convenient shorthand within the scope of this class for setting the
/// result of the script.
///
/// This code was generated automatically.
/// </summary>
enum ScriptResults
{
Success = Microsoft.SqlServer.Dts.Runtime.DTSExecResult.Success,
Failure = Microsoft.SqlServer.Dts.Runtime.DTSExecResult.Failure
};
#endregion
}
Thank You
Julie
Wow, this is going to be a pain to setup and maintain. Here is a way to merge 2 Excel files into 1.
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using Excel = Microsoft.Office.Interop.Excel;
using System.Reflection;
namespace WindowsFormsApplication5
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
Excel.Application app = new Excel.Application();
app.Visible = true;
app.Workbooks.Add("");
app.Workbooks.Add(#"C:\Users\Excel\Desktop\excel_files\Book1.xlsx");
app.Workbooks.Add(#"C:\Users\Excel\Desktop\excel_files\Book2.xlsx");
for (int i = 2; i <= app.Workbooks.Count; i++)
{
int count = app.Workbooks[i].Worksheets.Count;
app.Workbooks[i].Activate();
for (int j = 1; j <= count; j++)
{
Excel._Worksheet ws = (Excel._Worksheet)app.Workbooks[i].Worksheets[j];
ws.Select(Type.Missing);
ws.Cells.Select();
Excel.Range sel = (Excel.Range)app.Selection;
sel.Copy(Type.Missing);
Excel._Worksheet sheet = (Excel._Worksheet)app.Workbooks[1].Worksheets.Add(
Type.Missing, Type.Missing, Type.Missing, Type.Missing
);
sheet.Paste(Type.Missing, Type.Missing);
}
}
}
}
}
If I were you, I would not use C# for this kind of thing. I think you are better off using VBA, to push the date from each Excel file into SQL Server. Here are some options for you to consider.
https://www.excel-sql-server.com/excel-sql-server-import-export-using-vba.htm#Introduction
Or, used SQL to go grab the data out of the excel files and load everything into SQL Server.
SELECT *
FROM OPENROWSET(
'Microsoft.ACE.OLEDB.12.0',
'Excel 8.0;HDR=NO;Database=T:\temp\Test.xlsx',
'select * from [sheet1$]')
Or, even consider using a 3rd party resource to merge all excel files into 1, and then load that into SQL Server. Here is an example of what I'm talking about.
https://www.rondebruin.nl/win/addins/rdbmerge.htm
I am trying to bulk copy from one table to another by mapping the column names as the source and destination may not have same columns always.
Source can have 8 columns and destination can have 10 .. I need to map the columns and bulk copy.
Tried the below code..didn't work..getting Error: The given ColumnName 'moduleid' does not match up with any column in data source.
Source: existingtablecolumnsPresent has [collection time],[logtime],[moduleid],[node],[reason],[time],[timestamp],[usecaseid]
Destination: dataTable.Columns has [Node],[Time],[Reason],[Moduleid],[Usecaseid]
Please advise
public static void BatchBulkCopy(DataTable dataTable, string DestinationTbl, List<string> columnMapping,string filename)
{
var program = new Program();
// Get the DataTable
DataTable dtInsertRows = dataTable;
using (SqlBulkCopy sbc = new SqlBulkCopy(program.connectionStr.ToString()))
{
try {
sbc.DestinationTableName = DestinationTbl.ToLower();
string sourceTableQuery = "Select top 1 * from " + "[" + dataTable.TableName + "]";
DataTable dtSource = SqlHelper.ExecuteDataset(program.connectionStr.ToString(), CommandType.Text, sourceTableQuery).Tables[0];
for (int i = 0; i < dataTable.Columns.Count; i++)
{ //check if destination Column Exists in Source table
if (dtSource.Columns.Cast<DataColumn>().Select(a => "[" + a.ColumnName.ToLower() + "]").Contains(dataTable.Columns[i].ToString().ToLower()))//contain method is not case sensitive
{
List<string> existingtablecolumnsPresent = dtSource.Columns.Cast<DataColumn>().Select(a => "[" + a.ColumnName.ToLower() + "]").Distinct().OrderBy(t => t).ToList();
int sourceColumnIndex = existingtablecolumnsPresent.IndexOf(dataTable.Columns[i].ToString().ToLower());//Once column matched get its index
sbc.ColumnMappings.Add(dtSource.Columns[sourceColumnIndex].ToString(), dtSource.Columns[sourceColumnIndex].ToString());//give coluns name of source table rather then destination table so that it would avoid case sensitivity
}
}
sbc.WriteToServer(dtInsertRows);
sbc.Close();
}
catch (Exception ex)
{
Log.WriteLog("BatchBulkCopy" + " - " + filename, dataTable.TableName, ex.Message.ToString());
// To move a file or folder to a new location:
//if (File.Exists(program.sourceFile + filename))
// System.IO.File.Move(program.sourceFile + filename, program.failedfiles + filename);
}
As requested (create a DataTable with the columns you want to insert in them- leave the others out. Make sure any columns you leave out are marked in the table for NULL or have a DEFAULT VALUE constraint (I can't show you how to do that unless you show me your table);
//This first method is psuedoCode to explain how to create your datatable. You need to do it in the way that makes sense for you.
public DataTable createDataTable(){
List<string> excludedColumns = new List<string>();
excludedColumns.Add("FieldToExclude");
//...
DataTable dt = new DataTable();
foreach(string col in getColumns(myTable)){
if(!excludedColumns.Contains(name)){
DataColumn dC = new DataColumn(name,type);
DataTable.Add(dC);
}
return dt;
}
public List<string> getColumns(string tableName)
{
List<string> ret = new List<string>();
using (SqlConnection conn = getConn())
{
conn.Open();
using (SqlCommand com = conn.CreateCommand())
{
com.CommandText = "select column_Name from information_schema.COLUMNS where table_name = #tab";
com.Parameters.AddWithValue("#tab", tableName);
SqlDataReader read = com.ExecuteReader();
While(read.Read()){
ret.Add(Convert.ToString(read[0]);
}
conn.Close();
}
return ret;
}
//Now, you have a DataTable that has all the columns you want to insert. Map them yourself in code by adding to the appropriate column in your datatable.
public bool isCopyInProgess = false;//not necessary - just part of my code
public void saveDataTable(string tableName, DataTable table)
{
using (SqlConnection conn = getConn())
{
conn.Open();
using (var bulkCopy = new SqlBulkCopy(conn))//, SqlBulkCopyOptions.KeepIdentity))//un-comment if you want to use your own identity column
{
// my DataTable column names match my SQL Column names, so I simply made this loop. However if your column names don't match, just pass in which datatable name matches the SQL column name in Column Mappings
foreach (DataColumn col in table.Columns)
{
//Console.WriteLine("mapping " + col.ColumnName+" ("+does_Column_Exist(col.ColumnName,"Item").ToString()+")");
bulkCopy.ColumnMappings.Add(col.ColumnName, "["+col.ColumnName+"]");
// Console.WriteLine("ok\n");
}
bulkCopy.BulkCopyTimeout = 8000;
bulkCopy.DestinationTableName = tableName;
bulkCopy.BatchSize = 10000;
bulkCopy.EnableStreaming = true;
//bulkCopy.SqlRowsCopied += BulkCopy_SqlRowsCopied;
//bulkCopy.NotifyAfter = 10000;
isCopyInProgess = true;
bulkCopy.WriteToServer(table);
}
conn.Close();
}
}
Also, use this as your bolumn checker:
public bool does_Column_Exist(string colName,string tableName)
{
bool ret = false;
using (SqlConnection conn = getConn())
{
conn.Open();
using (SqlCommand com = conn.CreateCommand())
{
com.CommandText = "select count(*) from information_schema.COLUMNS where column_name = #col and table_name = #tab";
com.Parameters.AddWithValue("#tab", tableName);
com.Parameters.AddWithValue("#col", colName);
ret = Convert.ToInt32(com.ExecuteScalar()) == 0 ? false : true;
}
conn.Close();
}
return ret;
}
Is there a specific reason you need C# for this? It seems like the path of least resistance would be to use SQL to do the job.
INSERT INTO table2
(column_name(s))
SELECT column_name(s)
FROM table1;
As the title indicates, I'm having trouble updating a datagrid in WPF. Basically what I'm trying to accomplish is a datagrid, that is connected to a SQL Server database, that updates automatically once a user enters information into a few textboxes and clicks a submit button. You'll notice that I have a command that joins two tables. The data from the Quote_Data table will be inserted by a different user at a later time. For now my only concern is getting the information from the textboxes and into the General_Info table, and from there into my datagrid. The code, which I'll include below compiles fine, but when I hit the submit button, nothing happens. This is the first application I've ever built working with a SQL Database so many of these concepts are new to me, which is why you'll probably look at my code and wonder what is he thinking.
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
public DataSet mds; // main data set (mds)
private void Window_Loaded_1(object sender, RoutedEventArgs e)
{
try
{
string connectionString = Sqtm.Properties.Settings.Default.SqtmDbConnectionString;
using (SqlConnection connection = new SqlConnection(connectionString))
{
connection.Open();
//Merging tables General_Info and Quote_Data
SqlCommand cmd = new SqlCommand("SELECT General_Info.Quote_ID, General_Info.Open_Quote, General_Info.Customer_Name,"
+ "General_Info.OEM_Name, General_Info.Qty, General_Info.Quote_Num, General_Info.Fab_Drawing_Num, "
+ "General_Info.Rfq_Num, General_Info.Rev_Num, Quote_Data.MOA, Quote_Data.MOQ, "
+ "Quote_Data.Markup, Quote_Data.FOB, Quote_Data.Shipping_Method, Quote_Data.Freight, "
+ "Quote_Data.Vendor_Price, Unit_Price, Quote_Data.Difference, Quote_Data.Vendor_NRE_ET, "
+ "Quote_Data.NRE, Quote_Data.ET, Quote_Data.STI_NET, Quote_Data.Mfg_Time, Quote_Data.Delivery_Time, "
+ "Quote_Data.Mfg_Name, Quote_Data.Mfg_Location "
+ "FROM General_Info INNER JOIN dbo.Quote_Data ON General_Info.Quote_ID = Quote_Data.Quote_ID",
connection);
SqlDataAdapter da = new SqlDataAdapter(cmd);
DataTable dt = new DataTable();
da.Fill(dt);
MainGrid.ItemsSource = dt.DefaultView;
mds = new DataSet();
da.Fill(mds, "General_Info");
MainGrid.DataContext = mds.Tables["General_Info"];
}
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
// renaming column names from the database so they are easier to read in the datagrid
MainGrid.Columns[0].Header = "#";
MainGrid.Columns[1].Header = "Date";
MainGrid.Columns[2].Header = "Customer";
MainGrid.Columns[3].Header = "OEM";
MainGrid.Columns[4].Header = "Qty";
MainGrid.Columns[5].Header = "Quote Number";
MainGrid.Columns[6].Header = "Fab Drawing Num";
MainGrid.Columns[7].Header = "RFQ Number";
MainGrid.Columns[8].Header = "Rev Number";
MainGrid.Columns[9].Header = "MOA";
MainGrid.Columns[10].Header = "MOQ";
MainGrid.Columns[11].Header = "Markup";
MainGrid.Columns[12].Header = "FOB";
MainGrid.Columns[13].Header = "Shipping";
MainGrid.Columns[14].Header = "Freight";
MainGrid.Columns[15].Header = "Vendor Price";
MainGrid.Columns[16].Header = "Unit Price";
MainGrid.Columns[17].Header = "Difference";
MainGrid.Columns[18].Header = "Vendor NRE/ET";
MainGrid.Columns[19].Header = "NRE";
MainGrid.Columns[20].Header = "ET";
MainGrid.Columns[21].Header = "STINET";
MainGrid.Columns[22].Header = "Mfg. Time";
MainGrid.Columns[23].Header = "Delivery Time";
MainGrid.Columns[24].Header = "Manufacturer";
MainGrid.Columns[25].Header = "Mfg. Location";
}
private void submitQuotebtn_Click(object sender, RoutedEventArgs e)
{
CustomerData newQuote = new CustomerData();
int quantity;
quantity = Convert.ToInt32(quantityTxt.Text);
string theDate = System.DateTime.Today.Date.ToString("d");
newQuote.OpenQuote = theDate;
newQuote.CustomerName = customerNameTxt.Text;
newQuote.OEMName = oemNameTxt.Text;
newQuote.Qty = quantity;
newQuote.QuoteNumber = quoteNumberTxt.Text;
newQuote.FdNumber = fabDrawingNumberTxt.Text;
newQuote.RfqNumber = rfqNumberTxt.Text;
newQuote.RevNumber = revNumberTxt.Text;
try
{
string insertConString = Sqtm.Properties.Settings.Default.SqtmDbConnectionString;
using (SqlConnection insertConnection = new SqlConnection(insertConString))
{
insertConnection.Open();
SqlDataAdapter adapter = new SqlDataAdapter(Sqtm.Properties.Settings.Default.SqtmDbConnectionString, insertConnection);
SqlCommand updateCmd = new SqlCommand("UPDATE General_Info " + "Quote_ID = #Quote_ID, "
+ "Open_Quote = #Open_Quote, " + "OEM_Name = #OEM_Name, " + "Qty = #Qty, "
+ "Quote_Num = #Quote_Num, " + "Fab_Drawing_Num = #Fab_Drawing_Num, "
+ "Rfq_Num = #Rfq_Num, " + "Rev_Num = #Rev_Num "
+ "WHERE Quote_ID = #Quote_ID");
updateCmd.Connection = insertConnection;
System.Data.SqlClient.SqlParameterCollection param = updateCmd.Parameters;
//
// Add new SqlParameters to the command.
//
param.AddWithValue("Open_Quote", newQuote.OpenQuote);
param.AddWithValue("Customer_Name", newQuote.CustomerName);
param.AddWithValue("OEM_Name", newQuote.OEMName);
param.AddWithValue("Qty", newQuote.Qty);
param.AddWithValue("Quote_Num", newQuote.QuoteNumber);
param.AddWithValue("Fab_Drawing_Num", newQuote.FdNumber);
param.AddWithValue("Rfq_Num", newQuote.RfqNumber);
param.AddWithValue("Rev_Num", newQuote.RevNumber);
adapter.UpdateCommand = updateCmd;
adapter.Update(mds.Tables[0]);
mds.AcceptChanges();
}
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
Thanks in advance to anyone who can help, I really appreciate it,
Andrew
You are not setting the Quote_ID parameter. So your update it likely running WHERE Quote_ID = null so nothing updates.
using LINQ I was able to resolve the issue. Here's the code:
var sqtmDC = new SqtmLinqDataContext();
var mainTable = from generalInfo in sqtmDC.GetTable<General_Info>()
//join quoteData in sqtmDataContext.GetTable<Quote_Data>() on generalInfo.Quote_ID equals quoteData.Quote_ID
select generalInfo;
myGrid.ItemsSource = mainTable;
}
private void submitBtn_Click(object sender, RoutedEventArgs e)
{
var sqtmDC = new SqtmLinqDataContext();
// string theDate = System.DateTime.Today.Date.ToString("d");
int quantity = Convert.ToInt32(quantityTxt.Text);
General_Info insert = new General_Info();
insert.Open_Quote = DateTime.UtcNow;
insert.Customer_Name = customerNameTxt.Text;
insert.OEM_Name = oemNameTxt.Text;
insert.Qty = quantity;
insert.Quote_Num = quoteNumberTxt.Text;
insert.Fab_Drawing_Num = fabDrawingNumTxt.Text;
insert.Rfq_Num = rfqNumberTxt.Text;
insert.Rev_Num = revNumberTxt.Text;
sqtmDC.General_Infos.InsertOnSubmit(insert);
sqtmDC.SubmitChanges();
int quoteID = insert.Quote_ID;
var mainTable = from generalInfo in sqtmDC.GetTable<General_Info>()
select generalInfo;
myGrid.ItemsSource = mainTable;
Are you trying to update an existing row or insert a new row?
Cause if you need to insert then the proper command is insert (not update).
To get the Identity value of the inserted row you use Scope_Identity().
And you can only insert into one table at a time.
Scope_Identity() is NOT a param
Do not try and use it as a param
See example below
INSERT INTO Sales.Customer ([TerritoryID],[PersonID]) VALUES (8,NULL);
GO
SELECT SCOPE_IDENTITY() AS [SCOPE_IDENTITY];
There are lots of examples on MSDN.Microsoft.com
I'm currently trying to populate a datagrid using a member of a class that uses SQLCommand to execute a stored procedure and return the results.
My class member (and where I believe the issues lies) is:
public DataView DisplayHealthIndicator(DateTime startDate, DateTime endDate)
{
string queryString =
"DECLARE #RC int"
+ "DECLARE #date_from datetime = dateadd(day, 0, datediff(day, 0, getdate()))"
+ "DECLARE #date_to datetime = dateadd(day, 0, datediff(day, 0, getdate()))"
+ "EXECUTE #RC = [Testing].[marlin].[support_retrieve_workflow_history] "
+ "#date_from "
+ ",#date_to"
+ "GO";
using (SqlConnection connection = new SqlConnection(GetConnectionString()))
{
using (var cmd = new SqlCommand(queryString, connection))
{
connection.Open();
var reader = cmd.ExecuteReader();
var dt = new DataTable();
dt.Load(reader);
return dt.DefaultView;
}
}
}
and I'm calling this member using:
var db = new DatabaseHandle();
dataGridWorkflow.ItemsSource = db.DisplayHealthIndicator(DateTime.Now, DateTime.Now);
However! I'm currently receiving the error:
Incorrect syntax near #date_from
Must declare the scalar variable #RC
To a degree I understand the error - I believe that I can't declare variables in my sqlQuery string... but then, how do I do this?
I'm fairly sure that it doesn't have any bearing on this, but in case it does, this is the contents of the stored procedure:
create procedure marlin.support_retrieve_workflow_history
(
#date_from datetime,
#date_to datetime
)
as
select dateadd(day, 0, datediff(day, 0, e.event_date)) as 'Date',
c.setting_secondary 'Workflow Category' ,
d.setting_main as 'Error Type' ,
sum(e.event_count) as 'Total'
from marlin.support_events e
inner join marlin.support_config c
on e.event_category = c.setting_code
and c.config_code = 60
inner join marlin.support_config d
on e.event_type = d.setting_code
and d.config_code = 70
where e.event_date between #date_from and #date_to
group by
e.event_date,
c.setting_secondary ,
d.setting_main
cmd.Parameters["#ReturnValue"] contains the return value - you don't need to add a parameter in dynamic SQL
Add your parameters to the cmd
cmd.Parameters.AddWithValue("ParamName", Value);
Also change the cmd.CommandType (might not be called that, check members of cmd) to StoredProcedure
e.g.
using (SqlConnection connection = new SqlConnection(GetConnectionString()))
{
using (var cmd = new SqlCommand(queryString, connection))
{
connection.Open();
cmd.CommandType = ??.StoredProcedure; // Can't remember what enum name is prob SqlCommandType or something
cmd.Parameters.AddWithValue("date_from", DateTime.blah.blah);
cmd.Parameters.AddWithValue("date_to", DateTime.blah.blah);
var reader = cmd.ExecuteReader();
var dt = new DataTable();
dt.Load(reader);
return dt.DefaultView;
}
}
Disclaimer: Some of these prop names, the name of the return value param might not be correct so check the docs :)
This post is a bit old...But, I wanted to share how I am dynamically populating the WPF DataGrid
private void Fill_DataGrid_ServiceName()
{
this.Cursor = Cursors.Wait;
// create an instance
DatabaseClass objDatabaseClass = new DatabaseClass(_connectionString);
// if we are able to open and close the SQL Connection then proceed
if (objDatabaseClass.CheckSQLConnection())
{
try
{
// create an instance. variable 'con' will hold the instance
SqlConnection con = new SqlConnection(_connectionString);
con.Open();
// Query to populate the Grid
string Query = #"SELECT
cm_mktdata_mdsservice_fits_to_finance_id_unique AS [Id Unique]
,cm_mktdata_mdsservice_fits_to_finance_MDSService_fits AS [FITS MDSService]
,cm_mktdata_mdsservice_fits_to_finance_MDSService_finance AS [Finance MDSService]
,'[ ' + CONVERT(varchar, user_detail_user_info_id_user) + ' ] ' + user_detail_user_info_nm_login AS [Last Modified By]
,cm_mktdata_mdsservice_fits_to_finance_record_version AS [Record Version]
,cm_mktdata_mdsservice_fits_to_finance_dt_modified AS [Dt Modified]
,cm_mktdata_mdsservice_fits_to_finance_ind_active AS [Ind Active]
FROM
dbo.v_mktdata_ui_mdsservice_fits_to_finance_detail
WHERE
cm_mktdata_mdsservice_fits_to_finance_ind_operational = 1
ORDER BY
cm_mktdata_mdsservice_fits_to_finance_MDSService_fits";
SqlCommand createCommand = new SqlCommand(Query, con);
createCommand.ExecuteNonQuery();
// transfer the results of createCommand to the dataGrid
SqlDataAdapter dataAdapter = new SqlDataAdapter(createCommand);
DataTable dt = new DataTable("vcm_mktdata_mdsservice_fits_to_finance");
dataAdapter.Fill(dt);
dataGrid_ServiceName.ItemsSource = dt.DefaultView;
dataAdapter.Update(dt);
con.Close();
// Enable the Refresh Grid Button
btn_RefreshGrid_ServiceName.IsEnabled = true;
// get DataGrid row count
lbl_dataGrid_RowCount_ServiceName.Content = dataGrid_ServiceName.Items.Count.ToString() + " rows";
//return true;
}
catch (SqlException ex)
{
MessageBox.Show(ex.ToString());
//return false;
}
}
else
{
MessageBox.Show("Connection not established to the SQL Server. " + Environment.NewLine + "The SQL Server may be offline or valid credentials are not yet granted.", "SQL Server Connection Error", MessageBoxButton.OK, MessageBoxImage.Error);
this.Close();
}
this.Cursor = Cursors.Arrow;
}
The DatabaseClass is as follows
class DatabaseClass
{
// Variables
private string _connectionString = "";
public DatabaseClass(string connectionString)
{
_connectionString = connectionString;
}
/// Check to see if Connection can be opened
///
/// Returns True if the connection can be open else it returns False
///
public bool CheckSQLConnection()
{
SqlConnection con = new SqlConnection(_connectionString);
try
{
con.Open();
con.Close();
return true;
}
catch (SqlException ex)
{
return false;
}
}
}
And for the connection string it will look as follows
public static string SQLDataSourceStr = "Data Source=MySQL-DB-DV;Initial Catalog=My_Data;Integrated Security=True";