I have a datatable that comes from an SQL request. While I am really working against a table using OLEDB, even if I get the table from my SQL server, I have the same problem.
If I fill the datatable and then query the DataColumns - they all say AllowDBNull== true and allowNull == true. But if I look at the table in SSMS, it states otherwise.
string selectStmt= "Select * from foobar; "
DataSet NewData = new DataSet();
using (SqlConnection DataConn = new SqlConnection(MyConnectionString))
{
SqlDataAdapter DataAdapter = new SqlDataAdapter(selectStmt, DataConn );
var Results = DataAdapter.Fill(NewData, tableName);
}
DataColumn Col = NewData.Tables[0].Columns[0];
// Col.AllowDBNull is always true as is Col.AllowNull
I also can't seem to figure out where to get the length of a string field.
This makes it a little difficult to implement some simple client side error checking before I try to upload data.
If I were only dealing with SQL server based tables, I could use Microsoft.SqlServer.Management.Sdk and Microsoft.SqlServer.Management.Smo. Since I am not, that's out.
Try
var Results = DataAdapter.FillSchema(NewData, SchemaType.Source, tableName);
See if that gives you the level of schema detail you need.
A ResultSet isn't going to know column schema data like that, it would be too intensive an operation to do that per command execution, instead the runtime will create schema information on the fly only using the data it gets back in the data/result-set. For full blown schema you'd have to use something like EF or code the schema yourself. The only thing you can rely on for runtime schema's is the data type (unless the data columns were specifically coded with their attributes).
To properly test for DbNull you do this:
if ( dataRow[colNameOrIndex].Value == DbNull.Value){
//null
}
Related
I've got an error when i'm using MySql Stored Procedure and
Data Access Layer (MVC)have a method used to read data from database using MySqlDataAdapter and MySqlParameter and DataTable to read data called storedatai was tested this method before in a login controller and it's work as well : the main error here when i need to store last accountId using MySql last_insert_id() function but it always returns null value i tried to Convert it to int then add 1 in every time form opening , but that is useless because it's returns an error
System.ArgumentException: 'Parameter 'accountId' not found in the collection.'
here is the method in Data Access Layer that i use it to store data from database
public DataTable storeData(string trChannel,MySqlParameter[] list_OF)
{
InitializeDb();// Database configration method
MySqlCommand Transmeter = new MySqlCommand
{
CommandType = CommandType.StoredProcedure,
CommandText = trChannel,
Connection = dbcon // dbcon its Connection string comming from DAL
};
if (list_OF != null)
{
Transmeter.Parameters.AddRange(list_OF);
}
MySqlDataAdapter massenger = new MySqlDataAdapter(Transmeter);
DataTable _mainContainer = new DataTable();
massenger.Fill(_mainContainer);
disConnect();
return _mainContainer;
}
The stored procedure that i used it like this after i tried to use last_insert_id() and i failed so i change the sp to this and its work and give me result when i call it inside MySQL Server
CREATE DEFINER=`root`#`localhost` PROCEDURE `getAccountId`(out accountId int(5))BEGIN select Max(acotId)from accounts;set #accountId=last_insert_id(acotId); END
In this case the server going throw an error
Unknown "acotId" in fields list
but i don't need to fix it because it's give me a result , so when i called this procedure inside application using a method that return a DataTable value give the first error that i wrote it before System.ArgumentException: 'Parameter 'accountId' not found in the collection.' for the record i got more than five methods contains the same error most of them with input or output parameter and most of them without ..
public DataTable getAcotId()
{
DataTable pdbContainer = new DataTable();
pdbContainer = _socket.storeData("getAccountId",null);//_socket it's a link to data access layer
return pdbContainer;
}
I tried also method like this with a parameter and it's also didn't works
public DataTable getCusttId()
{
MySqlParameter[] parCut = new MySqlParameter[1];
parCut[0] = new MySqlParameter("?custId", MySqlDbType.Int16, 5) ;
parCut[0].Direction = ParameterDirection.Output;
DataTable pdbContainer = new DataTable();
pdbContainer = _socket.storeData("getCustomerId", parCut);
return pdbContainer;
}
Finally i want to convert the result from this methods to int and i think about using this code
int customerFinalId = getCustId.Rows.Fields[i].Feilds<int>("custId")
That is all guys and i'm dire to need help immediately for more necessary
Thank you a lot for helping me ..
You got MySQL, that is a problem for this problem. It is very common that you need to figure out "the Primary Key of the thing you just inserted". Usually to update the (G)UI.
If this was SQL, the OUTPUT clause would be your friend. This thing alone is worth its memory footprint in gold. But MySQL does not have any equivalent Syntax. That means you have to do it the hard way.
When figuring this value out, it is very important to guard against race conditions. The reliable way is to replace the implicit Transactions and Table locks of the DML statement with a explicit one that covers both the DML statement and the DQL Satement (SELECT) that follows.
Unfortunatley I am not that solid in MySQL Syntax, so somebody else will have to give you exact code.
There are multiple issues with your code
You NEED to provide all the parameters your SP expects or else it will just error out. Since you do not have any try and catch in your SP your entire query will bail on first error.
#accountId is a sql variable and you need to declare that if you want to use it
last_insert_id takes no parameter unless you want to set it explicitly which destroys the whole purpose of it in your example.
The answer here is the second method of getCusttId replacing the ? by #, which works for me.
I am updating a database using System.Data.Odbc.OdbcDataAdapter. I also don't know what table or fields I'm updating until runtime. The code I have works and is roughly equivalent to the following:
{
mConn = new OdbcConnection(connectionStr);
mConn.Open();
mDataSet = new DataSet();
mDataAdapter = new OdbcDataAdapter(selectStatement, mConn);
mCmdBldr = new OdbcCommandBuilder(mDataAdapter);
var trans = mConn.BeginTransaction();
mDataAdapter.SelectCommand.Transaction = trans;
mDataAdapter.Fill(mDataSet, tableName);
mDataTable = mDataSet.Tables[tableName];
mDataTable.Rows.Add(mNewDataTableRow);
mDataAdapter.Update(mDataSet, tableName);
trans.Commit();
}
The above is stripped down from my actual code, but hopefully gives an idea of how I'm using the DataAdapter. As I say, it works, for the most part...
It does of course fail if I try to insert to many characters into a SQL Server VARCHAR column, in which case I get the following error (in this case, I'm writing to SQL Server, but the database could be something else):
System.Data.Odbc.OdbcException occurred HResult=0x80131937
Message=ERROR [22001] [Microsoft][ODBC SQL Server Driver][SQL
Server]String or binary data would be truncated.
My question is this:
Is there any way in which I can get the OdbcDataAdapter to automatically truncate text fields?
I understand I can turn off ANSI_WARNINGS in the database if I'm using SQL Server, but I might not be, and I don't want to do that anyway because in most cases I would want the above exception to be thrown. I also understand that I can inspect the constraints on the fields myself and truncate the data before I insert it, but I'm looking for something less manual. I'll write that code if I have to, but I'd rather not have to.
I've looked at setting the OdbcParameter.Size property, but this simply throws an exception in a different place. Similarly with DataColumn.MaxLength.
I've also looked at setting DataSet.EnforceConstraints, but that doesn't prevent the above error when actually updating.
I'm using SQL CE 4.0 and am running into a StackOverflowException when the SqlCeDataAdapter attempts to Fill the DataTable.
The code is pretty standard :
using (var cmd = new SqlCeCommand(sql, conn, tran))
using (var dt = new DataTable())
{
using (var da = new SqlCeDataAdapter(cmd))
da.Fill(dt);
IList<DataRow> rows = dt.Rows.OfType<DataRow>().ToList();
foreach (DataRow row in rows)
{
// Processing ...
}
}
The sql string is in the form of :
SELECT * FROM [Table] WHERE Id IN ( ... )
The total character count is 1,068,369 due to the column value list being filled with 27,393 values of type uniqueidentifier.
Executing that query should return results with 1-for-1 rows from the database. Instead, the StackOverflowException occurs. I feel as though I am running up against some sort of SQL CE 4.0 limitation.
I've read Understanding Databases (SQL Server Compact) > Database Objects and under the Queries section it reads :
Characters in an SQL statement | Unlimited
1,068,369 characters is less than unlimited; PASS.
I've read DataTable Class and under the Remarks section it reads :
The maximum number of rows that a DataTable can store is 16,777,216
27,393 rows is less than 16,777,216; PASS.
Is there something else that I'm missing?
"The code is pretty standard" (!) - but the SQL statement is not!
It is obviously an engine bug/doc error, but do not expect a fix from Microsoft.
You will have to change the code to work with smaller batches of ids, resulting in a statement that is not 1 MB in size with 27.000 values.
This sounds like your sql server runs out of temp space when running this query. You can test this by changing the query to return only one row and not using the IN statement. if this works then as mentioned before check or ask your DBA to check that the system has been allocated the resources to run the query and build the result set before returning it to your application. PS. IF multiple persons use this application at the same time then this problem will scale horribly.
I'm running into some trouble trying to load data into a DataTable using an IDataReader. To keep it really simple, I just call ExecuteReader() on the command, create a DataTable, call Load() on it and feed it the object implementing IDataReader:
...
if(dataReader.HasRows)
{
DataTable tempDT = new DataTable();
tempDT.Load(dataReader);
....
}
...
This works in the vast majority of cases. However, in (rare) circumstances, I get the following exception (column name is obviously variable - in this case, it's ID):
Error - MaxLength applies to string data type only. You cannot set Column `ID` property MaxLength to be a non-negative number
I investigated the source table I was trying to load, and I suspect that the problem stems from it having a VARCHAR(256) ID column, that is a Required, Unique, Key (the issue doesn't seem to occur when the PK is a regular old int). This type of situation is really uncommon in the source data, and while it definitely isn't ideal, I can't modify the schema of the source data.
I took a look at the SchemaTable in more detail, and I am at a loss:
ColumName - ID
ColumnSize - 256
ProviderType - NVarChar
DataType - {Name = "String" FullName = "System.String"}
IsIdentity - True
IsKey - True
IsAutoIncrement - True
IsUnique - True
It just doesn't make sense to me. The source table uses unique codes as the ID, and while it isn't the way I would've designed it, it's.. fine. But I don't understand how a String/Varchar can ever be an identity, auto-increment, etc.
Unfortunately, I'm at the mercy of this source data and can't mess with it, so I'm hoping someone here might have more insight into what exactly is going on. Can anyone conceive of a way for me to Load() my DataTable without applying all the constraints from the IDataReader source data? Is there an entirely alternative approach that would avoid this problem?
Thanks for reading, thanks in advance for any assistance. It's my first question so be gentle. If there's any more information that would help, please let me know!
EDIT: Some people asked for the full code for loading the DataTable. Appended here. Should add that the CacheCommand/etc. comes in from this 'InterSystems.Data.CacheClient' assm. Kinda hoping the problem can be approached more generically. In this case, the Query string is just a 'SELECT TOP 10 *' test.
using (CacheConnection cacheConnection = new CacheConnection())
{
cacheConnection.ConnectionString = connectionString;
cacheConnection.Open();
using (CacheCommand cacheCommand = new CacheCommand(Query, cacheConnection))
{
using (CacheDataReader cacheDataReader = cacheCommand.ExecuteReader())
{
if (cacheDataReader.HasRows)
{
DataTable tempDT = new DataTable();
tempDT.Load(cacheDataReader); // Exception thrown here.
cacheConnection.Close();
return tempDT;
}
else
{
cacheConnection.Close();
return null;
}
}
}
}
EDIT 2: In case it's not clear, I'm trying to extract the entirety of a (small) table from the Cache DB into a DataTable. I normally do this by calling dataTable.Load(cacheDataReader), which works fine 99% of the time, but breaks when the source table in the Cache DB has an identity column of type VARCHAR.
Calling Load() on my DataTable object (which is empty) causes it to infer the schema based on the result set from the imported IDataReader (in this case, CacheDataReader). The problem is that the schema in the CacheDataReader specifies the data in the list above^, and DataTable doesn't seem to allow the MaxLength property, even though the type is VARCHAR/String.
SELECT TOP 10 * FROM table
WHERE IsNumeric(ColumName) = 0
This will return only data where the Primary Key is of type Int
I have an option to choose between local based data storing (xml file) or SQL Server based.
I already created a long time ago a typed dataset for my application to save data local in the xml file.
Now, I have a bool that changes between Server based version and local version. If true my application get the data from the SQL Server.
I'm not sure but It seems that Sql Adapter's Fill Method can't fill the Data in my existing schema
SqlCommand cmd = new SqlCommand("Select * FROM dbo.Categories WHERE CatUserId = 1", _connection);
cmd.CommandType = CommandType.Text;
_sqlAdapter = new SqlDataAdapter(cmd);
_sqlAdapter.TableMappings.Add("Categories", "dbo.Categories");
_sqlAdapter.Fill(Program.Dataset);
This should fill my data from dbo.Categories to Categories (in my local, typed dataset).
but it doesn't. It creates a new table with the name "Table". It looks like it can't handle the existing schema.
I can't figure it out. Where is the problem?
btw. of course the database request I do isn't very useful that way. It's just a simplified version for testing...
The Fill overload you are using, passing in a DataSet will always create a NEW DataTable in the supplied DataSet with name "Table" (MSDN).
So, I think you either need to change your TableMapping to:
_sqlAdapter.TableMappings.Add("Table", "Categories");
(MSDN) assuming your DataTable name is "Categories".
Or, don't use TableMappings and just supply the second argument - the name of the DataTable in that DataSet you want to populate (MSDN). The approach I usually use is actually to pass the DataTable itself that you want to populate, instead of the DataSet (MSDN).
Try _sqlAdapter.TableMappings.Add("Table", "Categories");, but as i remember you will have to add column mapping also. btw, you can try to create typed dataadapter, it is useless thing, but you can take mapping from there.