I had some confusion which I want to clear it - I am inserting values into database using ADO.NET. Let say I want to insert 10 item if I encounter error while inserting data of 5th item it should roll back whatever I had inserted into the database.
I just read the concept of Transaction and Rollback method and also tried to implement it in the program but still it insert 4 item and give me error message of 5th item. It doesn't roll back insert query.
Does transaction and roll back method solved my issue or I need to used other alternative.
here is my code,
for (int i = 0; i < itemLength - 1; i++)
{
//--- Start local transaction ---
myTrans = Class1.conn.BeginTransaction();
//--- Assign transaction object and connection to command object for a pending local transaction ---
_insertQry = Class1.conn.CreateCommand();
_insertQry.Connection = Class1.conn;
_insertQry.Transaction = myTrans;
_insertQry.CommandText = "INSERT INTO Product_PropertyValue(ItemNo, PropertyNo, ValueNo) VALUES (#ItemNo, #PropertyNo, #ValueNo)";
//_insertQry = new SqlCommand("INSERT INTO Product_PropertyValue(ItemNo, PropertyNo, ValueNo) VALUES (#ItemNo, #PropertyNo, #ValueNo)", Class1.conn);
_insertQry.Parameters.AddWithValue("#ItemNo", _itemNo[i]);
_insertQry.Parameters.AddWithValue("#PropertyNo", _propNo);
_insertQry.Parameters.AddWithValue("#ValueNo", _propValue);
_insertQry.ExecuteNonQuery();
myTrans.Commit();
}
Can anyone help me?
It sounds like you are trying to achieve an atomic commit. It either inserts completely or doesn't insert at all.
Try something like the following
SqlTransaction objTrans = null;
using (SqlConnection objConn = new SqlConnection(strConnString))
{
objConn.Open();
objTrans = objConn.BeginTransaction();
SqlCommand objCmd1 = new SqlCommand("insert into tbExample values(1)", objConn);
SqlCommand objCmd2 = new SqlCommand("insert into tbExample values(2)", objConn);
try
{
objCmd1.ExecuteNonQuery();
objCmd2.ExecuteNonQuery();
objTrans.Commit();
}
catch (Exception)
{
objTrans.Rollback();
}
finally
{
objConn.Close();
}
Also take a look at
http://www.codeproject.com/Articles/10223/Using-Transactions-in-ADO-NET
I did 2 modification to your code
1) Move the BeginTransaction() outside the for loop, So that all your 10 INSERt statements are in a single transaction, that is what you want if you want them to be atomic
2) added a TRY/CATCH block, so that you can roll back in case of errors.
//--- Start local transaction ---
myTrans = Class1.conn.BeginTransaction();
bool success = true;
try
{
for (int i = 0; i < itemLength - 1; i++)
{
//--- Assign transaction object and connection to command object for a pending local transaction ---
_insertQry = Class1.conn.CreateCommand();
_insertQry.Connection = Class1.conn;
_insertQry.Transaction = myTrans;
_insertQry.CommandText = "INSERT INTO Product_PropertyValue(ItemNo, PropertyNo, ValueNo) VALUES (#ItemNo, #PropertyNo, #ValueNo)";
//_insertQry = new SqlCommand("INSERT INTO Product_PropertyValue(ItemNo, PropertyNo, ValueNo) VALUES (#ItemNo, #PropertyNo, #ValueNo)", Class1.conn);
_insertQry.Parameters.AddWithValue("#ItemNo", _itemNo[i]);
_insertQry.Parameters.AddWithValue("#PropertyNo", _propNo);
_insertQry.Parameters.AddWithValue("#ValueNo", _propValue);
_insertQry.ExecuteNonQuery();
}
}
catch (Exception ex)
{
success = false;
myTrans.Rollback();
}
if (success)
{
myTrans.Commit();
}
let me know if this doesn't works.
You are on the right path, ADO.NET supports transactions so you will be able to rollback on errors.
Posting your your code here would get you more specific guidance; However since your question is very generic, I will encourage you to follow the template provided by MSDN
using (SqlConnection connection = new SqlConnection(connectionString))
{
connection.Open();
// Start a local transaction.
SqlTransaction sqlTran = connection.BeginTransaction();
// Enlist a command in the current transaction.
SqlCommand command = connection.CreateCommand();
command.Transaction = sqlTran;
try
{
// Execute two separate commands.
command.CommandText =
"INSERT INTO Production.ScrapReason(Name) VALUES('Wrong size')";
command.ExecuteNonQuery();
command.CommandText =
"INSERT INTO Production.ScrapReason(Name) VALUES('Wrong color')";
command.ExecuteNonQuery();
// Commit the transaction.
sqlTran.Commit();
Console.WriteLine("Both records were written to database.");
}
catch (Exception ex)
{
// Handle the exception if the transaction fails to commit.
Console.WriteLine(ex.Message);
try
{
// Attempt to roll back the transaction.
sqlTran.Rollback();
}
catch (Exception exRollback)
{
// Throws an InvalidOperationException if the connection
// is closed or the transaction has already been rolled
// back on the server.
Console.WriteLine(exRollback.Message);
}
}
}
Related
I am using Netsuite API (version v2016_2) to search data. With below code, it seems that Netsuite taking much time to give response for the query. I am searching GL transaction of periticular period that has 149 MainLine record and 3941 LineItem (Journal Entries) record and Netsuite takes almost 22 minutes to give this data in response. Below is code snippet that I am using to search transaction.
public void GetTransactionData()
{
DataTable dtData = new DataTable();
string errorMsg = "";
LoginToService(ref errorMsg);
TransactionSearch objTransSearch = new TransactionSearch();
TransactionSearchBasic objTransSearchBasic = new TransactionSearchBasic();
SearchEnumMultiSelectField semsf = new SearchEnumMultiSelectField();
semsf.#operator = SearchEnumMultiSelectFieldOperator.anyOf;
semsf.operatorSpecified = true;
semsf.searchValue = new string[] { "Journal" };
objTransSearchBasic.type = semsf;
objTransSearchBasic.postingPeriod = new RecordRef() { internalId = "43" };
objTransSearch.basic = objTransSearchBasic;
//Set Search Preferences
SearchPreferences _searchPreferences = new SearchPreferences();
Preferences _prefs = new Preferences();
_serviceInstance.preferences = _prefs;
_serviceInstance.searchPreferences = _searchPreferences;
_searchPreferences.pageSize = 1000;
_searchPreferences.pageSizeSpecified = true;
_searchPreferences.bodyFieldsOnly = false;
//Set Search Preferences
try
{
SearchResult result = _serviceInstance.search(objTransSearch);
/*
Above line taking almost 22 minutes for below record count
result.recordList.Length = 149
Total JournalEntryLine = 3941
*/
List<JournalEntry> lstJEntry = new List<JournalEntry>();
List<JournalEntryLine> lstLineItems = new List<JournalEntryLine>();
if (result.status.isSuccess)
{
for (int i = 0; i <= result.recordList.Length - 1; i += 1)
{
JournalEntry JEntry = (JournalEntry)result.recordList[i];
lstJEntry.Add((JournalEntry)result.recordList[i]);
if (JEntry.lineList != null)
{
foreach (JournalEntryLine line in JEntry.lineList.line)
{
lstLineItems.Add(line);
}
}
}
}
try
{
_serviceInstance.logout();
}
catch (Exception ex)
{
}
}
catch (Exception ex)
{
throw ex;
}
}
I am unable to know that If I am missing something in my code or this is something about the data. Please suggest me some sort of solution for this.
Thanks.
You should set _searchPreferences.bodyFieldsOnly = true. It will improve the performance with searching because it doesn't return the related or sublist data
I think you are doing this search from the outside of the Netsuite to get journal entries data or lines. Instead of doing a direct search outside, do maintain RESTLET in NETSUITE and call that RESTLET. In the RESTLET DO that search you wanted and return results. Within the NETSUITE, search performance gives fast results.
I am working on a stand-alone WinForm program in C# that uses the Solidworks EPDM api. The program takes a top level assembly and finds all the referenced and referencing files in the assembly. e.g. all sub-assemblies, part files, and drawings. The program then checks out all the files from EPDM, updates the data cards, and checks in all the files back to the EPDM.
I have successfully implemented the portion of the code that finds all the referenced and referencing files and updates the data card information using a background worker. This portion of the code does not require access the the UI thread. I would like to be able to add the code that checks out the files and checks them back in within a background worker. The problem is that the methods used to do the checkout and check-in take this.Handle as an argument. I know that accessing the UI thread from within a background worker will throw a cross thread exception. The code does not access any of the UI controls. It only need access to this.Handle. Is it possible to pass this.Handle to a background worker in a thread safe way that will not throw a cross thread exception?
This is my first use of background workers so my knowledge is limited. Below is the code that I would like to run in a background worker.
private void BatchCheckout(Dictionary<string, string> SelectedFiles)
{
try
{
IEdmBatchGet batchGetter = (IEdmBatchGet)vault.CreateUtility(EdmUtility.EdmUtil_BatchGet);
EdmSelItem[] ppoSelection = new EdmSelItem[SelectedFiles.Count];
IEdmFile5 aFile;
IEdmFolder5 aFolder;
IEdmFolder5 ppoRetParentFolder;
IEdmPos5 aPos;
int i = 0;
foreach (KeyValuePair<string, string> kvp in SelectedFiles)
{
aFile = vault1.GetFileFromPath(kvp.Key, out ppoRetParentFolder);
aPos = aFile.GetFirstFolderPosition();
aFolder = aFile.GetNextFolder(aPos);
ppoSelection[i] = new EdmSelItem();
ppoSelection[i].mlDocID = aFile.ID;
ppoSelection[i].mlProjID = aFolder.ID;
i = i + 1;
}
batchGetter.AddSelection((EdmVault5)vault1, ref ppoSelection);
batchGetter.CreateTree(this.Handle.ToInt32(), (int)EdmGetCmdFlags.Egcf_Lock);
batchGetter.GetFiles(this.Handle.ToInt32(), null);
}
catch (System.Runtime.InteropServices.COMException ex)
{
MessageBox.Show("HRESULT = 0x" + ex.ErrorCode.ToString("X") + " " + ex.Message);
}
catch (Exception ex)
{
MessageBox.Show(ex.Message + "\n" + GetStackTrace(ex));
}
}
I have been a reader of StackOverflow for many years and have found answers to just about every question I have ever had. This is my first ever question on StackOverflow. I am really hoping that someone will have an answer to this problem.
EDIT:
I have successfully test AndrewK's suggestion and am happy to report that it did work for my batch checkout method. When I run my batch check-in method in a background worker I'm getting the following COM exception:
Unable to cast COM object of type 'System.__ComObject' to interface type 'EPDM.Interop.epdm.IEdmBatchUnlock2'. This operation failed because the QueryInterface call on the COM component for the interface with IID '{F0970446-4CBB-4F0F-BAF5-F9CD2E09A5B3}' failed due to the following error: No such interface supported (Exception from HRESULT: 0x80004002 (E_NOINTERFACE)).
I only get this exception if I run the code from a background worker.
Here is the code from my BatchCheckin method:
private void BatchCheckin(Dictionary<string, string> SelectedFiles)
{
try
{
int i = 0;
IEdmFolder5 ppoRetParentFolder;
IEdmFile5 aFile;
IEdmFolder5 aFolder;
IEdmPos5 aPos;
EdmSelItem[] ppoSelection = new EdmSelItem[SelectedFiles.Count];
IEdmBatchUnlock2 batchUnlock;
foreach (KeyValuePair<string, string> kvp in SelectedFiles)
{
aFile = vault5.GetFileFromPath(kvp.Key, out ppoRetParentFolder);
aPos = aFile.GetFirstFolderPosition();
aFolder = aFile.GetNextFolder(aPos);
ppoSelection[i] = new EdmSelItem();
ppoSelection[i].mlDocID = aFile.ID;
ppoSelection[i].mlProjID = aFolder.ID;
i = i + 1;
}
batchUnlock = (IEdmBatchUnlock2)vault7.CreateUtility(EdmUtility.EdmUtil_BatchUnlock);
batchUnlock.AddSelection((EdmVault5)vault5, ref ppoSelection);
batchUnlock.CreateTree(0, (int)EdmUnlockBuildTreeFlags.Eubtf_ShowCloseAfterCheckinOption + (int)EdmUnlockBuildTreeFlags.Eubtf_MayUnlock);
batchUnlock.Comment = "Updates";
batchUnlock.UnlockFiles(0, null);
}
catch (System.Runtime.InteropServices.COMException ex)
{
MessageBox.Show("HRESULT = 0x" + ex.ErrorCode.ToString("X") + " " + ex.Message);
}
catch (Exception ex)
{
MessageBox.Show(ex.Message + "\n" + GetStackTrace(ex));
}
}
I am getting the exception when I make the call to vault7.CreateUtility. The BatchCheckin code is nearly identical to the BatchCheckout. I'm making the same call to vault7.CreateUtility in both methods. The only difference is the EdmUtility flag is set to EdmUtil_BatchUnlock in the BatchCheckin method. Any clue on this one AndrewK?
UPDATE:
I was able to resolve the COM exception by changing batchUpdate from the IEdmBatchUnlock2 interface to the IEdmBatchUnlock interface. Here is the code change:
private void BatchCheckin(Dictionary<string, string> SelectedFiles)
{
int i = 0;
IEdmFolder5 ppoRetParentFolder;
IEdmFile5 aFile;
IEdmFolder5 aFolder;
IEdmPos5 aPos;
EdmSelItem[] ppoSelection = new EdmSelItem[SelectedFiles.Count];
IEdmBatchUnlock batchUnlock = (IEdmBatchUnlock)vault7.CreateUtility(EdmUtility.EdmUtil_BatchUnlock);
try
{
foreach (KeyValuePair<string, string> kvp in SelectedFiles)
{
aFile = vault5.GetFileFromPath(kvp.Key, out ppoRetParentFolder);
aPos = aFile.GetFirstFolderPosition();
aFolder = aFile.GetNextFolder(aPos);
ppoSelection[i] = new EdmSelItem();
ppoSelection[i].mlDocID = aFile.ID;
ppoSelection[i].mlProjID = aFolder.ID;
i = i + 1;
}
batchUnlock.AddSelection((EdmVault5)vault5, ref ppoSelection);
batchUnlock.CreateTree(0, (int)EdmUnlockBuildTreeFlags.Eubtf_ShowCloseAfterCheckinOption + (int)EdmUnlockBuildTreeFlags.Eubtf_MayUnlock);
batchUnlock.Comment = "Release to Production ECO";
batchUnlock.UnlockFiles(0, null);
}
catch (System.Runtime.InteropServices.COMException ex)
{
MessageBox.Show("HRESULT = 0x" + ex.ErrorCode.ToString("X") + " " + ex.Message);
}
catch (Exception ex)
{
MessageBox.Show(ex.Message + "\n" + GetStackTrace(ex));
}
}
I am guessing that this is a bug in the IEdmBatchUnlock2 interface. The IEdmBatchUnlock2 will cause a COM exception if called from a background worker but will not cause a COM exception if called from the UI thread. The IEdmBatchUnlock interface will not cause a COM exception when called from a background worker.
Just put a 0 in there for the handle. As long as your code will not require user input, it will work. I do it often.
batchGetter.AddSelection((EdmVault5)vault1, ref ppoSelection);
batchGetter.CreateTree(0, (int)EdmGetCmdFlags.Egcf_Lock);
batchGetter.GetFiles(0, null);
I am getting below error while trying to insert multiple entities in Azure Table storage:
com.microsoft.azure.storage.table.TableServiceException: Bad Request
at com.microsoft.azure.storage.table.TableBatchOperation$1.postProcessResponse(TableBatchOperation.java:525)
at com.microsoft.azure.storage.table.TableBatchOperation$1.postProcessResponse(TableBatchOperation.java:433)
at com.microsoft.azure.storage.core.ExecutionEngine.executeWithRetry(ExecutionEngine.java:146)
Below is the Java code for batch insert:
public BatchInsertResponse batchInsert(BatchInsertRequest request){
BatchInsertResponse response = new BatchInsertResponse();
String erpName = request.getErpName();
HashMap<String,List<TableEntity>> tableNameToEntityMap = request.getTableNameToEntityMap();
HashMap<String,List<TableEntity>> errorMap = new HashMap<String,List<TableEntity>>();
HashMap<String,List<TableEntity>> successMap = new HashMap<String,List<TableEntity>>();;
CloudTable cloudTable=null;
for (Map.Entry<String, List<TableEntity>> entry : tableNameToEntityMap.entrySet()){
try {
cloudTable = azureStorage.getTable(entry.getKey());
} catch (Exception e) {
e.printStackTrace();
}
// Define a batch operation.
TableBatchOperation batchOperation = new TableBatchOperation();
List<TableEntity> value = entry.getValue();
for (int i = 0; i < value.size(); i++) {
TableEntity entity = value.get(i) ;
batchOperation.insertOrReplace(entity);
if (i!=0 && i % batchSize == 0) {
try {
cloudTable.execute(batchOperation);
batchOperation.clear();
} catch (Exception e) {
e.printStackTrace();
}
}
}
try {
cloudTable.execute(batchOperation);
} catch (Exception e) {
e.printStackTrace();
}
}
}
Above code is working fine if I will assign batchSize value to 10 but if I will assign to 1000 or 100 it will throw Bad request error.
Please help me to resolve this error. I am using Spring boot and Azure-storage Java SDK version 4.3.0.
As Aravind mentioned, 400 error usually means there's something wrong with your data. From this link, an entity batch transaction will fail if one or more of the following conditions are not met:
All entities subject to operations as part of the transaction must have the same PartitionKey value.
An entity can appear only once in the transaction, and only one operation may be performed against it.
The transaction can include at most 100 entities, and its total payload may be no more than 4 MB in size.
All entities are subject to the limitations described in Understanding the Table Service Data Model.
Please check your entities against these four rules and ensure that you're not violating one of the rules.
Here I coding for get each and every StudyUID(as string) from database to SqlDataReader,but i need to know how the reader value call to forloop execution.
Get to read each and every StudyUID for execution.Here is the code :.
public void automaticreport()
{
//string autsdyid="";
SqlConnection con = new SqlConnection(constr);
con.Open();
string autoquery = "Select StudyUID From StudyTable Where status='2'";
SqlCommand cmd = new SqlCommand(autoquery, con);
SqlDataReader rdr = cmd.ExecuteReader();
for()
{
//how to call each StudyUId from database through for loop
if (!this.reportchk)
{
Reportnew cf = new Reportnew();
ThreadPool.QueueUserWorkItem((WaitCallback)(o => cf.ReportRetrive(this, autsdyid, true)));
}
else
{
int num = (int)System.Windows.Forms.MessageBox.Show("Reports checking in progress, Please wait sometime and try again later", "OPTICS", MessageBoxButtons.OK, MessageBoxIcon.Asterisk);
}
con.Close();
}
Like #R.T and others have mentioned you can use the Read method on the data reader. Looking at your sample code you might want to refactor it slightly to meet more of the SOLID principles and make sure you're not leaking database connections
Here's an example of code that has been refactored a bit.
public void automaticreport()
{
foreach (var autsdyid in LoadStudyIdentifiers())
{
if (!this.reportchk)
{
Reportnew cf = new Reportnew();
ThreadPool.QueueUserWorkItem((WaitCallback)(o => cf.ReportRetrive(this, autsdyid, true)));
}
else
{
int num = (int)System.Windows.Forms.MessageBox.Show("Reports checking in progress, Please wait sometime and try again later", "OPTICS", MessageBoxButtons.OK, MessageBoxIcon.Asterisk);
}
}
}
private string[] LoadStudyIdentifiers()
{
var results = new List<string>();
// adding a using statement will close the database connection if there are any errors
// avoiding consuming the database connection pool
using (var con = new SqlConnection(constr))
{
conn.Open();
var autoquery = "Select StudyUID From StudyTable Where status='2'";
using (var cmd = new SqlCommand(autoquery, con))
{
SqlDataReader rdr = cmd.ExecuteReader();
while(rdr.Read())
{
results.Add(rdr.GetString(rdr.GetOrdinal("StudyUID")));
}
}
}
return results.ToArray();
}
Note: I wrote this in notepad so there is no guarantee it will compile but should give an indication as to how you could refactor your code.
if (rdr.HasRows)
{
while (rdr.Read())
{
Console.WriteLine(rdr.getString("columnName"));
}
}
You can use something like:
while (reader.Read())
{
string value = reader.getString("columnName");
}
You may use the while loop like this:
while (rdr.Read())
{
string s = rdr.GetString(rdr.GetOrdinal("Column"));
//Apply logic to retrieve here
}
I am writing a database application using Visual Studio 2012 with Entity Framework 5 and SQL Server 2008. I would like Entity Framework to impersonate a SQL Server user (i.e. user without a login). I have created a new constructor for the DB context MyDatabaseEntities which includes an argument for the name of the user to impersonate. Here is the code that I've written:
public partial class MyDatabaseEntities
{
private String _impersonateUser = null;
public MyDatabaseEntities(String impersonateUser)
: base("MyConnectionString")
{
_impersonateUser = impersonateUser;
this.Database.Connection.StateChange += Connection_StateChange;
}
void Connection_StateChange(object sender, StateChangeEventArgs e)
{
if (e.CurrentState == ConnectionState.Open && e.OriginalState != ConnectionState.Open)
{
using (var cmd = this.Database.Connection.CreateCommand())
{
cmd.CommandType = CommandType.Text;
cmd.Parameters.Add(new SqlParameter("user", _impersonateUser));
cmd.CommandText = "EXECUTE AS USER = #user";
cmd.ExecuteNonQuery();
}
}
}
I had to add the check...
if (e.CurrentState == ConnectionState.Open && e.OriginalState != ConnectionState.Open)
...because the method Connection_StateChange method seems to execute even when the state hasn't changed. Then problem is that when I run the code twice,
public void RunSimpleQuery()
{
using (MyDatabaseEntities context = new MyDatabaseEntities("UserName"))
{
var result = context.TableName.ToList();
}
}
...Entity Framework throws a SqlException:
A severe error occurred on the current command. The results, if
any, should be discarded.\r\nA severe error occurred on the current
command. The results, if any, should be discarded.
Any ideas?
Update 1
I in my code above, I changed...
cmd.CommandText = "EXECUTE AS USER = #user;";
...to...
cmd.CommandText = "REVERT; EXECUTE AS USER = #user;";
...and I still get the same SqlException error.
The problem is that EF closes connection when it doesn't need it and returns it back to the pool. So when it executes some SQL again it request new connection from the pool where your event may not be initialized. But again I believe that you should try to solve this with manually controlling connection lifetime to have both benefit of connection pooling and be able to meet your requirements.
I know is an old question, but maybe will be useful for someone.
I did in a different way, using your code...
Instead of
Connection_StateChanged event
I create two methods in the same class:
public void ChangeUser(String sUser)
{
if(Database.Connection.State != ConnectionState.Open)
Database.Connection.Open();
using (var cmd = Database.Connection.CreateCommand())
{
cmd.CommandType = CommandType.Text;
cmd.Parameters.Add(new SqlParameter("user", sUser));
cmd.CommandText = "EXECUTE AS USER = #user;";
cmd.ExecuteNonQuery();
}
}
public void Revert()
{
if (Database.Connection.State != ConnectionState.Open)
Database.Connection.Open();
using (var cmd = Database.Connection.CreateCommand())
{
cmd.CommandType = CommandType.Text;
cmd.CommandText = "REVERT;";
cmd.ExecuteNonQuery();
}
}
I use it before and after execute stored procedure,
using (var db = new MyDatabaseEntities())
{
db.ChangeUser(model.Username);
var result = db.Something();
db.Revert();
return result;
}
It works fine with SPs and it doesn't throw an exception even after many executions. If I could catch an event after command execute, maybe all be encapsulated on MyDatabaseEntities.