retrieving value via SQLDataReader.get_Item() - axapta

I am trying to access external sql-tables via the following code. Accessing works as intended, but I have problems to process the values, if datatype has to be casted for futher processsing.
The following code can be executed as job in ax:
static void Job1(Args _args)
{
str serverName;
str catalogName;
str ConnectionString;
str sqlQuery;
System.Data.SqlClient.SqlConnectionStringBuilder connectionStringBuilder;
System.Data.SqlClient.SqlConnection connection;
System.Data.SqlClient.SqlCommand command;
System.Data.SqlClient.SqlParameterCollection parameterCollection;
System.Data.SqlClient.SqlDataReader dataReader;
;
new InteropPermission( InteropKind::ClrInterop ).assert();
sqlQuery = "SELECT TOP 10 * FROM PRODROUTE";
serverName = SysSQLSystemInfo::construct().getLoginServer();
catalogName = SysSQLSystemInfo::construct().getloginDatabase();
connectionStringBuilder = new System.Data.SqlClient.SqlConnectionStringBuilder();
connectionStringBuilder.set_DataSource(serverName);
connectionStringBuilder.set_IntegratedSecurity(true);
connectionStringBuilder.set_InitialCatalog(catalogName);
ConnectionString = connectionStringBuilder.get_ConnectionString();
connection = new System.Data.SqlClient.SqlConnection(ConnectionString);
command = new System.Data.SqlClient.SqlCommand(sqlQuery);
command.set_Connection(connection);
try
{
connection.Open();
try
{
dataReader = command.ExecuteReader();
while(dataReader.Read())
{
//info( dataReader.get_Item( "PRODID" )); // ok
//info( dataReader.get_Item( "LEVEL" )); // not working
info ( int2str( dataReader.GetInt32( 23 ))); // not working
//info( any2str(dataReader.get_Item( "LEVEL" ))); // not working
}
dataReader.Dispose();
}
catch
{
dataReader.Dispose();
}
catch(Exception::CLRError)
{
dataReader.Dispose();
}
connection.Dispose();
}
catch
{
connection.Dispose();
}
catch(Exception::CLRError)
{
connection.Dispose();
}
command.Dispose();
CodeAccessPermission::revertAssert();
}
There are four codelines to access data:
//info( dataReader.get_Item( "PRODID" )); // ok
//info( dataReader.get_Item( "LEVEL" )); // not working
info ( int2str( dataReader.GetInt32( 23 ))); // not working
//info( any2str(dataReader.get_Item( "LEVEL" ))); // not working
As you see, only one line won't throw an error, since datatype of field fits to the desired operation. Info-logging is just an example. If I try to assign data to ax-table-fields, the same problem occurs.
Casting via int2str(), any2str() and so on doesn't work as well.
So what's the right way to handle data read, for further processing?
#edit: ErrorMsg
Fehler während der Verarbeitung: Elementtyp für Variablenzuweisung ungültig.

I just tried running your query directly in SSMS, and found that the field LEVEL didn't exist on the actual table on sql server, but there is a field named "LEVEL_" (which is the 3rd field on my table).
Additionally, this talks about any2str not doing what you would expect it to do, but I don't think that is your problem on retrieving the LEVEL field.
http://abraaxapta.blogspot.com/2012/02/kernel-function-madness-any2str.html

Related

Anybody know how to do an Asynchronous creation of tables using Microsoft.SqlServer.Management.Smo.Table?

I currently have a problem with the datareader when creating Microsoft.SqlServer.Management.Smo.Table asynchronously. Note: I derived my SmoTable from TableView and IDisposable.
private async Task Generate()
{
await Task.Run(()=>
{
MSSMSDatabase db = CreateDB(txtDBname.Text);
List<string> tableNames = GetTableNameList();
for(string tableName in tableNames)
{
using(SmoTable tbl = new Table(db, tableName)) // <=== after a few loops, the error occurs within here.
{
foreach(var col in columnList)
{
tbl.AddColumns(col);
}
tbl.Create();
}
}
});
}
Microsoft.SqlServer.Management.Smo.FailedOperationException: InvalidOperationException: There is already an open DataReader associated with this Connection which must be closed first.
I tried implementing IDisposable to my SmoTable class that I derived from the TableView class but still have the same error.
Thanks in advance.
I did a trial and error and found out that you need to create a new connection for each table creation to create a separate datareader for it. So, if you include the instantiation of Server in the foreach loop it will create a new connection and hence a new datareader.
for(string tableName in tableNames)
{
using(SmoTable tbl = new Table(db, tableName)) // <=== after a few loops, the error occurs within here.
{
foreach(var col in columnList)
{
_server = GetSQLServer(); // <=== this is basically Server server = new Server(); return server; kind of method.
db = _server.Databases[_databaseName];
tbl.AddColumns(col);
}
tbl.Create();
}
}

retrieve a row of data from documentum via DQL

I have a list of users in my list view which is populated by retrieving data from documentum . If I click on any row of this least (each row represent one user) I should be able to see all of their information listed down .(This is my problem )
public void selectedItemFromListView(){
selected = lwAllUserGrp.getSelectionModel().getSelectedItem();
System.out.println(selected);
String query =" select * from dm_user where user_name = '#aclName'" ;
String test = query.replace("#aclname", selected);
GetDataWithDqlProfile(_session , query , "user_login_name" , "user_address" , "user_state" );
System.out.println(user.getAddress());
System.out.println(user.getState());
System.out.println(user.getUsername());
}
if I click on a row of list view I can successfully see who is selected and I need to retrieve all the other attributes of that username (same person) from documentum via DQL .
private void GetDataWithDqlProfile(IDfSession session, String Query, String username , String address , String state ) {
try {
IDfQuery UpdateQuery = new DfQuery();
UpdateQuery.setDQL(Query);
IDfCollection col = UpdateQuery.execute(_session, IDfQuery.DF_QUERY);
user.setAddress(col.getString(username));
user.setUsername(col.getString(address));
user.setState(col.getString(state));
col.close();
} catch (Exception e) {
Alert alert = new Alert(Alert.AlertType.ERROR, e.getMessage());
alert.showAndWait();
Logs.WriteLog(LoginController.Repository + ",User:" + LoginController.Username, "DQL Query", e.toString());
e.getStackTrace();
}
and my output is :
User's name
null
null
null
I've tried the DQL query in DQL tester and it works well
In order to fetch rows from IDfCollection you have to call next() on the collection object. This method both advances to the next row and returns a boolean if successful. Use a boolean test (e.g., while or if) to iterate, like this:
IDfCollection col = UpdateQuery.execute(_session, IDfQuery.DF_QUERY);
if (col.next()) {
user.setAddress(col.getString(username));
user.setUsername(col.getString(address));
user.setState(col.getString(state));
}
col.close();
The iteration is necessary even if the collection contains only one row. In other words, you need to manually advance to the first row.
1) As #eiviamu already mentioned, you have to call IDfCollection.next() to get the next row.
2) Your code, among other problems, has one documentum-related: closing of collection must happen always in finally block.
Otherwise you can get unclosed collection which might lead to memory leaks and weird application behavior (e.g. if I'm not mistaken there are 9 simultaneous open collections are allowed for one DCTM session by default, and if you exceed this limit an exception will be thrown)
For those of you referring to this question later here is how I solved the problem :
public ArrayList<User> GetDataWithDqlpro(IDfSession session, String Query, String username , String state , String address) {
try {
IDfQuery UpdateQuery = new DfQuery();
UpdateQuery.setDQL(Query);
IDfCollection col = UpdateQuery.execute(_session, IDfQuery.DF_QUERY);
while (col.next()) {
list.add( new User(col.getString(username),col.getString(address) , col.getString(state)));
}
col.close();
}catch (Exception e) {
Alert alert = new Alert(Alert.AlertType.ERROR, e.getMessage());
alert.showAndWait();
Logs.WriteLog(LoginController.Repository + ",User:" + LoginController.Username, "DQL Query", e.toString());
e.getStackTrace();
}
return (ArrayList<User>) list;
}
public void selectedItemFromListView(){
selected = lwAllUserGrp.getSelectionModel().getSelectedItem();
System.out.println(selected);
String Query = "select user_login_name , user_state , user_address from dm_user where user_login_name ='#aclname'";
Query = Query.replace("#aclname",selected );
ArrayList<User> allUserNames = GetDataWithDqlpro(_session, Query, "user_login_name","user_address","user_state");
for (int i = 0 ; i <= allUserNames.size()-1 ; i++ ){
if (selected.compareToIgnoreCase(allUserNames.get(i).getUsername() ) == 0){
System.out.println(allUserNames.get(i).getState() );
System.out.println(allUserNames.get(i).getAddress() );
System.out.println(allUserNames.get(i).getUsername() );
}
}
}
Worth mentioning that I have a class called User with constructor and get and set methods
I hope it will help some one :)

Executing stored procedure with asp.net

I am trying to execute a stored procedure in asp.net. The stored procedure requires 3 parameters, all 3 are ID's(ints). The 3 parameters are : TaskID, ExhibitID, and InvestigatorID.
I have a hidden field that contains an array of ExhibitID's that came from a javascript function.
My question is how do I get the query to execute as I am looping through the array?
Here is an example of my stored procedure:
var cnSaveTask = new SqlConnection(ConfigurationManager.ConnectionStrings["OSCIDConnectionString"].ToString());
var comLinkExhibitToTask = new SqlCommand("p_CaseFileTasksExhibitLinkAdd", cnSaveTask) { CommandType = CommandType.StoredProcedure };
foreach (string exhibit in hidExhibitsIDs.Value.Split(','))
{
comLinkExhibitToTask.Parameters.AddWithValue("#TaskID", taskID);
comLinkExhibitToTask.Parameters.AddWithValue("#ExhibitID", Convert.ToInt32(exhibit));
comLinkExhibitToTask.Parameters.AddWithValue("#InvestigatorID", int.Parse(Session["InvestigatorID"].ToString()));
}
try
{
cnSaveTask.Open();
comLinkExhibitToTask.ExecuteNonQuery();
}
It is not working in my DB though. Nothing gets added. My guess is that since it is iterating and not executing, it just keeps replacing the "exhibitID" everytime then eventually tries to execute it. But I don't think just adding "comLinkExhibitToTask.ExecuteNonQuery()"
outside the try is a good idea. Any suggestions?
you can either move the try block into the foreach loop or wrap the foreach loop with a try block. (depending on what error handling you wish - continue with the next exhibit on error or completely abort execution)
I've never used AddWithValue, so I can't speak to its functionality. Here's how I typically write a DB call like this.
using (SqlConnection cnSaveTask = new SqlConnection(ConfigurationManager.ConnectionStrings["OSCIDConnectionString"].ConnectionString))
{
cnSaveTask.Open();
using (SqlCommand comLinkExhibitToTask = new SqlCommand("p_CaseFileTasksExhibitLinkAdd", cnSaveTask))
{
comLinkExhibitToTask.CommandType = CommandType.StoredProcedure;
comLinkExhibitToTask.Parameters.Add(new SqlParameter("#TaskID", SqlDbType.Int) {Value = taskID});
// etc.
comLinkExhibitToTask.ExecuteNonQuery();
}
}
The solution:
var cnSaveTask = new SqlConnection(ConfigurationManager.ConnectionStrings["OSCIDConnectionString"].ToString());
try
{
var comLinkExhibitToTask = new SqlCommand("p_CaseFileTasksExhibitLinkAdd", cnSaveTask) { CommandType = CommandType.StoredProcedure };
cnSaveTask.Open();
comLinkExhibitToTask.Parameters.Add(new SqlParameter("#TaskID", SqlDbType.Int));
comLinkExhibitToTask.Parameters.Add(new SqlParameter("#ExhibitID", SqlDbType.Int));
comLinkExhibitToTask.Parameters.Add(new SqlParameter("#InvestigatorID", SqlDbType.Int));
foreach (string exhibit in hidExhibitsIDs.Value.Split(','))
{
comLinkExhibitToTask.Parameters["#TaskID"].Value = taskID;
comLinkExhibitToTask.Parameters["#ExhibitID"].Value = Convert.ToInt32(exhibit);
comLinkExhibitToTask.Parameters["#InvestigatorID"].Value = int.Parse(Session["InvestigatorID"].ToString());
comLinkExhibitToTask.ExecuteNonQuery();
}
}
catch (Exception ex)
{
ErrorLogger.Log(0, ex.Source, ex.Message);
}
finally
{
if (cnSaveTask.State == ConnectionState.Open)
{
cnSaveTask.Close();
}
}
Since I was in a loop it kept adding parameters. So just declare the parameters outside the loop, and only pass the values in the loop. That way there are only 3 parameters, and the values will be passed in accordingly

ASP.NET MySQL update multiple records

I have a web page that needs to update multiple records. This page gets all the information and then begins a transaction sending multiple UPDATE queries to the data base.
foreach row
{
Prepare the query
Hashtable Item = new Hashtable();
Item.Add("Id", Id);
Item.Add("Field1", Field1);
Item.Add("Field2", Field2);
Item.Add("Field3", Field3);
...
}
Then we launch the ytransaction
DO CHANGES()
public void execute_NonQuery_procedure_transaction(string StoredProcedure, List<Hashtable> Params)
{
using (MySqlConnection oConnection = new MySqlConnection(ConfigurationManager.AppSettings[DB]))
{
MySqlTransaction oTransaction;
bool HasErrors = false;
oConnection.Open();
oTransaction = oConnection.BeginTransaction();
try
{
MySqlCommand oCommand = new MySqlCommand(StoredProcedure, oConnection);
oCommand.CommandType = CommandType.StoredProcedure;
oCommand.Transaction = oTransaction;
foreach (Hashtable hParams in Params)
{
oCommand.Parameters.Clear();
IDictionaryEnumerator en = hParams.GetEnumerator();
while (en.MoveNext())
{
oCommand.Parameters.AddWithValue("_" + en.Key.ToString(), en.Value);
oCommand.Parameters["_" + en.Key.ToString()].Direction = ParameterDirection.Input;
}
oCommand.ExecuteNonQuery();
}
}
catch (Exception e)
{
HasErrors = true;
throw e;
}
finally
{
if (HasErrors)
oTransaction.Rollback();
else
oTransaction.Commit();
oConnection.Close();
}
}
}
Is there another way to do this or this is the most efficient way?
It depends on the situation, like if you have multiple row updates or adding new rows or deleting some rows or a combination of these, which modifies the database table then, the efficient way to do this is to have Batch Update...
Please go through this link Batch Update
Hope this helps...
it looks fine to me, you could eventually do not clear the Command.Parameters list but just assign the values on following iterations but probably this leads to no visible improvements.
pay attention your throw is wrong, in C# don't use throw e; but simply throw;.

SQL CE 3.5 problem with TableDirect table access

I try to insert hundreds of records into empty database table using TableDirect type of SqlCeCommand. The problem is I get an exception SqlCeException "Unspecified error" when calling SqlCeResultSet::Insert. Below is my code. Any hints?
Thanks
public bool StoreEventsDB2(List<DAO.Event> events)
{
try
{
SqlCeCommand command = new SqlCeCommand("Event");
command.CommandType = System.Data.CommandType.TableDirect;
SqlCeResultSet rs = _databaseManager.ExecuteResultSet(command, ResultSetOptions.Updatable | ResultSetOptions.Scrollable );
foreach (DAO.Event theEvent in events)
{
SqlCeUpdatableRecord record = rs.CreateRecord();
record.SetInt32( 0, theEvent.ID );
record.SetInt32( 1, theEvent.ParentID);
record.SetString(2, theEvent.Name);
record.SetDateTime(3, theEvent.DateTime);
record.SetDateTime(4, theEvent.LastSynced);
record.SetInt32(5, theEvent.LastSyncedTS);
record.SetString(6, theEvent.VenueName);
record.SetBoolean(7, theEvent.IsParentEvent);
record.SetDateTime(11, DateTime.Now);
rs.Insert(record);
}
}
catch (SqlCeException e)
{
Log.Logger.GetLogger().Log(Log.Logger.LogLevel.ERROR, "[EventManager::StoreEventsDB] error: {0}", e.Message);
return false;
}
catch (Exception e)
{
Log.Logger.GetLogger().Log(Log.Logger.LogLevel.ERROR, "[EventManager::StoreEventsDB] error: {0}", e.Message);
return false;
}
return true;
}
I am unsure how your connection is managed with the database manager which could be the culprit - make sure you are using one connection (sqlce doesn't play nice). Also the results set option "ResultSetOption.Scrollable" is not needed (at least I have never used it for an insert).
Below is the syntax I use when doing direct table inserts. Every database/data access object is wrapped in a using statement to dispose of objects after use - this is very important especially with the compact framework and sqlce as the garbage collection is less than ideal (you WILL get out of memory exceptions!). I have added a transaction to your code also so that the option is all or nothing.
Hope this helps:
using (var transaction = connection.BeginTransaction())
{
using (var command = connection.CreateCommand())
{
command.Transaction = transaction;
command.CommandType = CommandType.TableDirect;
command.CommandText = "Event";
using (var rs = command.ExecuteResultSet(ResultSetOptions.Updatable))
{
var record = rs.CreateRecord();
foreach (DAO.Event theEvent in events)
{
record.SetInt32(0, theEvent.ID);
record.SetInt32(1, theEvent.ParentID);
record.SetString(2, theEvent.Name);
record.SetDateTime(3, theEvent.DateTime);
record.SetDateTime(4, theEvent.LastSynced);
record.SetInt32(5, theEvent.LastSyncedTS);
record.SetString(6, theEvent.VenueName);
record.SetBoolean(7, theEvent.IsParentEvent);
record.SetDateTime(11, DateTime.Now);
rs.Insert(record);
}
}
transaction.Commit();
}
}

Resources