ExecuteNonQuery returning a value of 2 when only 1 record was updated - c#-4.0

Running thru examples of Enterprise Library 5.0 and when I use ExecuteNonQuery to run an update sproc, it returns 2. The update is based on ProductID, the table's Primary Key (yes, I checked, and it is unique).
Here is the simple sproc:
ALTER PROCEDURE [dbo].[UpdateProductsTable]
#ProductID int,
#ProductName varchar(50) = NULL,
#CategoryID int = NULL,
#UnitPrice money = NULL
AS
BEGIN
UPDATE Products
SET
ProductName = #ProductName,
CategoryID = #CategoryID,
UnitPrice = #UnitPrice
WHERE ProductID = #ProductID
END
Executing this sp in SSMS shows "1 rows" in bottom right hand corner of the query results window, and a return value of 0 in the grid. Clicking on the messages tab shows
(1 row(s) affected)
(1 row(s) affected)
(1 row(s) affected)
not sure why I'm seeing this 3 times here, but I don't believe that is the issue.
Here is the code calling the sp:
public static void Exec_Update_Query()
//updates a column of a single row, checks that the update succeeded, and then update it again to return it to the original value
{
string oldName = "Chai";
string newName = "Chai Tea";
SqlDatabase defaultDB = EnterpriseLibraryContainer.Current.GetInstance<Database>() as SqlDatabase;
// Create command to execute the stored procedure and add the parameters.
DbCommand cmd = defaultDB.GetStoredProcCommand("UpdateProductsTable");
defaultDB.AddInParameter(cmd, "ProductID", DbType.Int32, 1);
defaultDB.AddInParameter(cmd, "ProductName", DbType.String, newName);
defaultDB.AddInParameter(cmd, "CategoryID", DbType.Int32, 1);
defaultDB.AddInParameter(cmd, "UnitPrice", DbType.Currency, 18);
// Execute the query and check if one row was updated.
int i = defaultDB.ExecuteNonQuery(cmd);
if (i == 1)
{
// Update succeeded.
}
else
{
Console.WriteLine("ERROR: Could not update just one row.");
}
// Change the value of the second parameter
defaultDB.SetParameterValue(cmd, "ProductName", oldName);
// Execute query and check if one row was updated
if (defaultDB.ExecuteNonQuery(cmd) == 1)
{
// Update succeeded.
}
else
{
Console.WriteLine("ERROR: Could not update just one row.");
}
}
I'm using int i to view the return value from the method and it returns 2. Any ideas why this would be? This is Enterprise Libarary 5.0 in VS2010 running against SQL 2005. Pretty straightforward but perplexing.

If I recall correctly, the result of any triggers that fire as a result of your commands will also be included in the returned row count. Most likely, this is your issue.
EDIT: Documentation
From MSDN SqlCommand.ExecuteNonQuery:
When a trigger exists on a table being inserted or updated, the return value includes the number of rows affected by both the insert or update operation and the number of rows affected by the trigger or triggers.

Related

Update a row in google sheets based on duplicate

I'm designing a script that takes an object (jsonData[data]) and inputs its values into a different sheet based on which product it is.
Currently the script inputs all the data into a new row each time the form reaches a new stage, however the form goes through 4 stages of approval and so I'm finding each submission being entered into 4 different rows. Each submission has an "Id" value within the object which remains the same (but each submission could also be on any row in the sheet as it's used a lot).
I'm checking whether the ID exists in the sheet and using iteration to find the row number:
function updatePlatformBulkInfo(jsonData) {
var sheetUrl = "https://docs.google.com/spreadsheets/d/13U9r9Lu2Fq1WTT8pQ128heCm6_gMmH1R4O6u8e7kvBo/edit#gid=0";
var sheetName = "PlatformBulkSetup";
var doc = SpreadsheetApp.openByUrl(sheetUrl);
var sheet = doc.getSheetByName(sheetName);
var rowList = [];
var formId = jsonData["Id"];
var allSheetData = sheet.getDataRange().getValues();
setLog("AllSheetData = " + allSheetData[1][11]) //Logs to ensure data is collected correctly
var rowEdited = false;
var rowNumber = 0;
//Check whether ID exists in the sheet
for (var i = 0; i < allSheetData.length; i++) {
if(allSheetData[i][11] == formId) {
rowEdited = true;
} else {
rowNumber += 1;
}
}
My issue is with the next part:
//Append row if ID isn't duplicate or update row if duplicate found
if (rowEdited == false) {
for (var data in jsonData) {
rowList.push(jsonData[data])
}
setLog("***Row List = " + rowList + " ***");
setLog("***Current Row Number = " + rowNumber + " ***");
sheet.appendRow(rowList);
} else if(rowEdited == true){
var newRowValue = jsonData[data];
sheet.getRange(rowNumber, 1).setValues(newRowValue);
}
Everything works fine if the duplicate isn't found (the objects values are appended to the sheet). But if a duplicate is found I'm getting the error:
Cannot find method setValues(string)
This looks to me like i'm passing a string instead of an object, but as far as I'm aware I've already converted the JSON string into an object:
var jsonString = e.postData.getDataAsString();
var jsonData = JSON.parse(jsonString);
How can I modify my script to write the updated data to the matched row?
It's unclear based on your code whether or not you will actually write to the correct cell in the case of a duplicate. As presented, it looks as though you loop over the sheet data, incrementing a row number if the duplicate is not found. Then, after completing the loop, you write to the sheet, in the row described by rowNumber, even though your code as written changes rowNumber after finding a duplicate.
To address this, your loop needs to exit upon finding a duplicate:
var duplicateRow = null, checkedCol = /* your column to check */;
for(var r = 0, rows = allSheetData.length; r < rows; ++r) {
if(allSheetData[r][checkedCol] === formId) {
// Convert from 0-base Javascript index to 1-base Range index.
duplicateRow = ++r;
// Stop iterating through allSheetData, since we found the row.
break;
}
}
In both cases (append vs modify), you seem to want the same output. Rather than write the code to build the output twice, do it outside the loop. Note that the order of enumeration specified by the for ... in ... pattern is not dependable, so if you need the elements to appear in a certain order in the output, you should explicitly place them in their desired order.
If a duplicate ID situation is supposed to write different data in different cells, then the following two snippets will need to be adapted to suit. The general idea and instructions still apply.
var dataToWrite = [];
/* add items to `dataToWrite`, making an Object[] */
Then, to determine whether to append or modify, test if duplicateRow is null:
if(dataToWrite.length) {
if(duplicateRow === null) {
sheet.appendRow(dataToWrite);
} else {
// Overwriting a row. Select as many columns as we have data to write.
var toEdit = sheet.getRange(duplicateRow, 1, 1, dataToWrite.length);
// Because setValues requires an Object[][], wrap `dataToWrite` in an array.
// This creates a 1 row x N column array. If the range to overwrite was not a
// single row, a different approach would be needed.
toEdit.setValues( [dataToWrite] );
}
}
Below is the most basic solution. At the end of this post, I'll expand on how this can be improved. I don't know how your data is organized, how exactly you generate new unique ids for your records, etc., but let's assume it looks something like this.
Suppose we need to update the existing record with new data. I assume your JSON contains key-value pairs for each field:
var chris = {
id:2,
name: "Chris",
age: 29,
city: "Amsterdam"
};
Updating a record breaks down into several steps:
1) Creating a row array from your object. Note that the setValues() method accepts a 2D array as an argument, while the appendRow() method of the Sheet class accepts a single-dimension array.
2) Finding the matching id in your table if it exists. The 'for' loop is not very well-suited for this idea unless you put 'break' after the matching id value is found. Otherwise, it will loop over the entire array of values, which is redundant. Similarly, there's no need to retrieve the entire data range as the only thing you need is the "id" column.
IMPORTANT: to get the row number, you must increment the array index of the matching value by 1 as array indices start from 0. Also, if your spreadsheet contains 1 or more header rows (mine does), you must also factor in the offset and increment the value by the number of headers.
3) Based on the matching row number, build the range object for that row and update values. If no matching row is found, call appendRow() method of the Sheet class.
function updateRecord(query) {
rowData = [];
var keys = Object.keys(query);
keys.forEach(function(key){
rowData.push(query[key]);
})
var ss = SpreadsheetApp.getActive();
var sheet = ss.getSheets()[0];
var headers = sheet.getRange(1, 1, 1, sheet.getLastColumn()).getValues()[0];
var idColumn = 1;
var ids = sheet.getRange(2, idColumn, sheet.getLastRow() - 1, 1).getValues();
var i = 0;
var matchedRow;
do {
if (ids[i] == query.id) { matchedRow = i + 2; }
i++;
} while (!matchedRow && i < ids.length);
if (matchedRow) {
var row = sheet.getRange(matchedRow, idColumn, 1, rowData.length);
row.setValues([rowData]);
} else {
sheet.appendRow(rowData);
}
}
NOTE: if your query contains only some fields that need to be updated (say, the 'id' and the 'name' field), the corresponding columns for these fields will be
headers.indexOf(query[key]) + 1;
Possible improvements
If the goal is to use the spreadsheet as a database and define all CRUD (Create, Read, Write, Delete) operations. While the exact steps are beyond the scope of the answer, here's the gist of it.
1) Deploy and publish the spreadsheet-bound script as a web app, with the access set to "anyone, even anonymous".
function doGet(e) {
handleResponse(e);
}
function doPost(e) {
handleRespone(e);
}
function handleResponse(e) {
if (e.contentLength == -1) {
//handle GET request
} else {
//handle POST request
}
}
2) Define the structure of your queries. For example, getting the list of values and finding a value by id can be done via GET requests and passing parameters in the url. Queries that add, remove, or modify data can be sent as payload via POST request. GAS doesn't support other methods besides GET and POST, but you can simulate this by including relevant methods in the body of your query and then selecting corresponding actions inside handleResponse() function.
3) Make requests to the spreadsheet URL via UrlFetchApp. More details on web apps https://developers.google.com/apps-script/guides/web

SQL Azure Database / Can't Insert Record into a Table / ID not getting set during SubmitChanges

In the following instance, I have tried to simplify an issue to root components.
I've got a very simple SQL Azure database where I created a test table called Table1. Azure creates an ID field with Is Required, Is Primary Key checked. It will NOT allow to check the box Is Identity. There are a couple of other fields which are simply required.
In my VS2012 Project, I have created an LinqToSql Class which created a ProductionDataClasses1.dbml object.
I simply want to add a record to this table thru the method shown below. From what I am reading, ID would be set during the SubmitChanges() after InsertOnSubmit(NewRecord) is specified.
It does work the first time but value is set to zero. On subsequent save, I get an exception (basically it a duplicate record because ID=0 already exists).
To put this into context, I have included some sample code below. The idea is to first check if the record exists and update. If not, I want to add a record.
My question is... Do I need to manually set ID? If so, how do I set the value to an int and how to a retrieve the next value. I tried changing to a Guid but not allowed.
Here is my code sample:
public bool AddTestRecord(string someValue)
{
ProductionDataClasses1DataContext context = new ProductionDataClasses1DataContext();
try
{
var ExistingRecord = context.Table1s.SingleOrDefault(c => c.TextKey == someValue);
if (ExistingRecord == null)
{
var NewRecord = new Table1();
// NewRecord.ID = ???? ; How Do I Manually Set. It is getting set to 0 causing a duplicate value exception
NewRecord.TextKey = someValue;
NewRecord.AnotherValue = DateTime.Now.ToShortTimeString();
context.Table1s.InsertOnSubmit(NewRecord);
}
else
{
ExistingRecord.AnotherValue = DateTime.Now.TimeOfDay.ToString();
}
context.SubmitChanges();
return true;
}
catch (Exception e)
{
Console.WriteLine(e.Message);
return false;
context.SubmitChanges();
}
}
I would suggest manually running a SQL script to alter the table and make the column an identity. Look at this answer
Adding an identity to an existing column
Thanks for your reply.
I just was finally able to make this work on a new table and will try to follow along your instructions to make modifications to my real table. My code (as written above) was OK so the issue is in the SQL Azure table definition.
I found the issue is that when you create a new table in SQL Azure, it creates a table with three fields, ID, Column1, Column2. By default, ID is set as the Primary Key but none are checked as Is Identity.
To make this work, I made ID the Is Identity and unchecked PrimaryKey and Column1 the In Primary Key. Thus when a new record is saved, the ID is set and Column1 is checked to make sure it is not already in the system. I had to do this when the table was first created. Once saved, it would not allow me to change.
Afterwards, I updated my Linq To SQL class and dropped the new table in. I noted that now the AutoGenerated Value on ID and PrimaryKey on Column1 was set and my code worked.

Returning an Object[][] gives NullPointerException

I have an Access database which I need to retrieve all fields except the first and last and display it in a JTable. Everything works perfectly fine when I create my Object[][] but when i return it, i get a NullPointerException. I tried to find where there could be a null value in the database by printing the whole object out but that works fine and no values are null. Why would returning the Object[][] give me a NullPointerException and how can i fix it?
the stack trace is:
Exception in thread "AWT-EventQueue-0" java.lang.NullPointerException
public Object [] [] SetTrainingLogTable() throws SQLException
{
DatabaseConnection connection = new DatabaseConnection();
//Retrieves all the data from the TrainingLog table
ResultSet resultset = connection.SelectStatements("SELECT * FROM TrainingLog");
//Retrieves the number of entries
ResultSet numberofworkouts = connection.SelectStatements("SELECT COUNT(*) FROM TrainingLog");
int count = numberofworkouts.getInt(1);
number = count;
String[][] table = new String [count] [6];
//Number to incriment for while loops
int row = 0;
String date = "";
while(row<count)
{
date = resultset.getString(2);
table [row][0] = calculate.RefineDate(date);
table [row][1] = resultset.getString(3);
table [row][2] = resultset.getString(4);
table [row][3] = resultset.getString(5);
table [row][4] = resultset.getString(6);
table [row][5] = resultset.getString(7);
resultset.next();
row++;
}
Object[][] data = table;
connection.close();
return data;
}
I ran a debugger and it only gives the error when the return line is run.
It's best to post the stack trace and tell which line is raising the error. However, the typical way of writing such code is:
Connection con = ...;
Statement st = ...;
ResultSet rs = ...;
while (rs.next()) {
// ...
}
The result set starts out pointing before the first row. rs.next() returns whether there is a next row, and advances to it if it exists. Can you rewrite it in that style?
Other suggestions:
Can you create an actual object type instead of using Object[] to store the data from each row? Call it Workout.
Can you use a List<Workout> instead of your Object[][]?
Is the date stored in the database as a SQL DATE or TIMESTAMP? Then, don't convert it to a Java String: use java.sql.Date or java.util.Date. At work, I have a large old program that uses strings for dates, and it uses different formats to convert the values at different times. It's pretty miserable.
Don't use SELECT *. Give the names of the columns to return. Use the rs.getString("column_name") syntax.
There's no need to set one variable to the returned table and immediately set another variable to it.
Closing the connection or statement should be done in a finally block, or by try-with-resources.

How to set record id by myself in J2ME

I use RecordStore to store my data.
I know when we store data in RecordStore, it automatically generates a record id for each record.
But how can I set the record id by myself? Or how can I get the record id?
Because I want to use the recordstore.setRecord(..) method to update my recordstore.
But when I use RecordEnumeration to fetch RecordStore and use method nextRecordId(), it just shows odd or even ids. I mean when I have 8 records, it just prints out only odd or even records like
2 4 6 8
My code:
handleRecord.openRecordStore(handleRecord.getRecordName());
RecordEnumeration re;
try {
int rc = handleRecord.getRecordStore().getNumRecords();
re = hrs.getRcs().enumerateRecords(null, null, true);
while(re.hasNextElement()) {
int rid = re.nextRecordId();
System.out.println(rid);
}
} catch(Exception e) {
System.out.println(e.toString());
}
MIDP API doesn't have method to set record id by yourself.
See RecordStore API documentation for explanation how this is supposed to work.
"Records are uniquely identified within a given record store by their recordId, which is an integer value. This recordId is used as the primary key for the records. The first record created in a record store will have recordId equal to one (1). Each subsequent record added to a RecordStore will be assigned a recordId one greater than the record added before it. That is, if two records are added to a record store, and the first has a recordId of 'n', the next will have a recordId of 'n + 1'..."
The code that iterates the store appears OK:
re = hrs.getRcs().enumerateRecords(null, null, true);
while(re.hasNextElement()) {
int rid = re.nextRecordId();
System.out.println(rid);
}
if you're getting only odd or even record like 2-4-6... or 1-3-5... printed as a result, first thing to check is whether you somehow removed records that are "missing" - this could be done eg using RecordStore.getVersion method:
"Each time a record store is modified (by addRecord, setRecord, or deleteRecord methods) its version is incremented. This can be used by MIDlets to quickly tell if anything has been modified..."

Table does not refresh

I have a table that consists of rows of reports. A back bean method that is tied to a check box value change event is getting the row IDs and populating an array (rowsToBeRemoved) with this IDs. Another method gets this array and removes the object from reportlist class:
ObjectListDataProvider reportList = new ObjectListDataProvider();
List<RowKey> rowsToBeRemoved=new ArrayList();
Integer rowsToBeRemovedIndex = 0;
for(RowKey rowToBeRemoved:rowsToBeRemoved){
try {
System.out.println("rowToBeRemoved.toString()" + rowToBeRemoved.toString()); // outputs: rowToBeRemoved.toString()RowKey[0]
Report report = (Report) reportList.getObject(rowToBeRemoved);
System.out.println("report.getId()" + report.getId()); //outputs: report.getId()199|
Query resultQuery = queryGeneration(report.getId());
List<String> dropTableQueries = resultQuery.getResultList(); // generated the queries to drop r tables
for(int i=0; i<dropTableQueries.size(); i++){
String aDropTableQuery;
aDropTableQuery = dropTableQueries.get(i);
System.out.println("adroptableuery" + aDropTableQuery);// get single drop table query. outputs adroptableueryDROP TABLE r_199_0
entityManager.createNativeQuery(aDropTableQuery);
System.out.println("entitymanager dropTableQueries is invoked");//OK
reportList.removeObject(rowToBeRemoved);
System.out.println("removeObject");//OK
if (reportList.isRemoved(rowToBeRemoved)){
System.out.println("object removed");//OK
}
reportList.commitChanges();
System.out.println("commitchanges");//OK
}
reportJpaController.delete(report);
reportList.removeRow(rowToBeRemoved);
reportList.commitChanges();
analyzerResultService.drop(report.getId().longValue());
//rowsToBeRemoved.remove(rowsToBeRemovedIndex);
} catch (Exception e) {
error("Cannot delete report with row key " + rowToBeRemoved + e);
}
The code executes fine, removes the object from the reportlist but after that my table brings up a mixed reportlist. If I refresh the page it brings the reportlist in correct order, otherwise it doesn't. And if I try to delete a row in the mixed state it deletes the report as if it is in correct order, thus ending with deleting the wrong report. I hope I could explained.. What am I doing wrong?
I think this is similar to what you're looking for: How to refresh entire JSF page from the backing bean
This doesnt just refresh one part of the page though, but the full page.

Resources