I use EnterpriseLibrary to access database with async method.but it is error this:"Invalid operation. The connection is closed."
my code:
public Database CreateDatabase()
{
if (null == this._createDatabase)
{
this._createDatabase = new DatabaseProviderFactory().Create("ConnectionString");
}
return this._createDatabase;
}
public async Task<bool> IsExistCode(string code)
{
Database db = base.CreateDatabase();
using (DbCommand dbCommand = db.GetSqlStringCommand("select top 1 1 from Ads WITH(NOLOCK) where Code=#Code"))
{
db.AddInParameter(dbCommand, "Code", DbType.AnsiString, code);
var result = await dbCommand.ExecuteScalarAsync();//error occurrence
return null != result;
}
}
ps:sync method(dbCommand.ExecuteScalar()) is ok.
Note:my way to use is wrong,see:https://msdn.microsoft.com/zh-cn/library/hh211418.aspx
Related
I have this issue in production with MySQL connections where the connection is either being used or it's not opened error comes up frequently. I think when multiple requests hit the method then this issue is happening. I got stats from production were 80 different instances hit the Graphql method. I am using Sqlkata to make DB calls. When there are no parallel calls then it works fine.
ConnectionManager.cs
public IDbConnection GetConnection
{
get
{
if (_iDbConnection != null)
{
if (_iDbConnection.State != ConnectionState.Open)
{
_iDbConnection.Open();
return _iDbConnection;
}
}
if (_iDbConnection != null && _iDbConnection.State == ConnectionState.Open)
{
// close the previous connection
_iDbConnection.Close();
// open new connection
_iDbConnection.Open();
return _iDbConnection;
}
try
{
string connectionString = Configuration.GetConnectionString("seasonal");
// Register the factory
DbProviderFactories.RegisterFactory("MySqlConnector", MySqlConnectorFactory.Instance);
// Get the provider invariant names
IEnumerable<string> invariants = DbProviderFactories.GetProviderInvariantNames();
var factory = DbProviderFactories.GetFactory(invariants.FirstOrDefault());
var conn = factory.CreateConnection();
if (conn != null)
{
conn.ConnectionString = connectionString;
conn.Open();
_iDbConnection = conn;
}
}
catch (Exception e)
{
throw;
}
return _iDbConnection;
}
}
Service (constructor)
public OutboundRecordService(IConnectionManager connectionManager) : base(connectionManager)
{
IDbConnection connection = connectionManager.GetConnection;
if (connection.State != ConnectionState.Open)
{
_logger.Error($"Connection is not open, current state: {connection.State}");
}
_queryFactory = new QueryFactory(connection, new MySqlCompiler());
_logger = LogManager.GetLogger(typeof(OutboundRecordService<T>));
}
public async Task<CareMetx.Service.Models.PaginationResult<OutboundRecord>> GetAllByCriteria(OutboundRecordSearchCriteria searchCriteria)
{
try
{
// START - Test code
var arrayTask = new List<Task>();
int i = 10;
for (int c = 0; c < i; c++)
{
arrayTask.Add(Task.Factory.StartNew(() => GetDataFromDB(searchCriteria)));
}
Task.WaitAll(arrayTask.ToArray());
Console.WriteLine("All threads complete");
return await Task.FromResult<CareMetx.Service.Models.PaginationResult<OutboundRecord>>(null);
// END - Test code
}
catch (Exception ex)
{
var message = ex.Message;
}
return null;
}
private async Task<List<OutboundRecord>> GetDataFromDB(OutboundRecordSearchCriteria searchCriteria)
{
Query dbQuery = _queryFactory.Query("SELECT * FROM OutboundRecords Where BatchId = 1");
// Clone the query and get the total count
var totalCountQuery = dbQuery.Clone();
totalCountQuery = totalCountQuery.AsCount();
// Log Record Count SQL
LogSqlQuery(totalCountQuery);
// Get the total record count
var totalRecords = await totalCountQuery.FirstOrDefaultAsync<int>();
}
Exception
I'm having trouble with some code, and I just can't seem to figure this out. I am attempting to send some data to a backend API that connects to our SQL Server and executes a query that I don't expect any kind of results from. The problem I'm having is that the SQL command isn't being sent to the server, and I'm getting a "404 - This file doesn't exist".
Here is the front part of the request:
public async Task ExportNewLists (string pid, string list)
{
var endpointUrl = string.Concat(baseEndpoint, "ExportLists", "/", pid, "/", list);
AddAuthorization();
using (HttpResponseMessage response = await client.GetAsync(endpointUrl))
{
if (!response.IsSuccessStatusCode)
{
Response.StatusCode = (int)response.StatusCode;
var result = response.Content.ReadAsStringAsync().Result;
var message = JsonConvert.DeserializeObject<ResponseError>(result);
}
}
}
And here is the API function I'm trying to call:
[Route("api/Lists/ExportLists/{pid}/{list}")]
[HttpGet]
[ResponseType(typeof(void))]
private async Task<IHttpActionResult> ExportList(string pid, string list)
{
using (var connection = db.Database.Connection)
{
try
{
connection.Open();
var command = connection.CreateCommand();
command.Connection = connection;
command.CommandText = "EXEC LIST_EXPORT_SINGLE";
command.CommandType = CommandType.StoredProcedure;
command.Parameters.Add("#PID");
command.Parameters["#PID"].Value = pid;
command.Parameters.Add("#LIST");
command.Parameters["#LIST"].Value = list;
await command.ExecuteNonQueryAsync();
connection.Close();
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}
return Ok();
}
You have marked ExportScrubList as private. You cannot call an action marked as private via http.
I migrated an Access database to SQL using Microsoft SQL Server Migration Assistant.
Now, I am having trouble reading data.
return reader.GetInt32(0); Throws Invalid attempt to read when no data is present exception after 32 rows retrieved. If I add CommandBehavior.SequentialAccess to my command, I am able to read 265 rows, every time.
The data
The query (in SQL Management Studio):
SELECT *
FROM Products2
WHERE Products2.PgId = 337;
There is nothing special about row 32, and if I reverse the order, it is still row 32 that kills it.
Row 265, just for good measure.
The code
The query:
SELECT *
FROM Products2
WHERE Products2.PgId = #productGroupId;
The parameter:
Name = "productGroupId"
Value = 337
The execution:
public async Task ExecuteAsync(string query, Action<IDataReader> onExecute, params DataParameter[] parameters)
{
using(var command = builder.BuildCommand(query))
{
foreach(var parameter in parameters)
command.AddParameter(parameter);
if(!connection.IsOpen)
connection.Open();
await Task.Run(async () =>
{
using(var reader = await command.ExecuteAsync())
if(await reader.ReadAsync())
onExecute(reader);
});
}
}
And reading:
await executor.ExecuteAsync(query, async reader =>
{
do
{
products.Add(GetProduct(reader));
columnValues.Enqueue(GetColumnValues(reader).ToList());
} while(await reader.ReadAsync());
}, parameters.ToArray());
await reader.ReadAsync() returns true, but when GetProduct(reader) calls reader.GetInt32(0); for the 32nd time, it throws the exception.
It works fine if the data is less than 32 rows, or 265 in case of SequentialAccess.
I tried increasing the CommandTimeout, but it didn't help. When I swap the connection to OleDb again, it works just fine.
Thanks in advance.
EDIT
If I replace * in the query with just a few specific columns, it works. When I read ~12 columns, it fails, but later than row 32.
As per request, GetProduct:
private Product GetProduct(IDataReader productReader)
{
return new Product
{
Id = productReader.ReadLong(0),
Number = productReader.ReadString(2),
EanNo = productReader.ReadString(3),
Frequency = productReader.ReadInt(4),
Status = productReader.ReadInt(5),
NameId = productReader.ReadLong(6),
KMPI = productReader.ReadByte(7),
OEM = productReader.ReadString(8),
CurvesetId = productReader.ReadInt(9),
HasServiceInfo = Convert.ToBoolean(productReader.ReadByte(10)),
ColumnData = new List<ColumnData>()
};
}
GetColumnData:
private IEnumerable<long> GetColumnValues(IDataReader productReader)
{
var columnValues = new List<long>();
int columnIndex = 11;
while(!productReader.IsNull(columnIndex))
columnValues.Add(productReader.ReadLong(columnIndex++));
return columnValues;
}
And the adapter:
public long ReadLong(int columnIndex)
{
return reader.GetInt32(columnIndex);
}
Alright, it is getting long. :)
Thanks to #Igor, I tried creating a small working example. This seem to work fine:
private static async Task Run()
{
var result = new List<Product>();
string conString = #" ... ";
var con = new SqlConnection(conString);
con.Open();
using(var command = new SqlCommand("SELECT * FROM Products2 WHERE Products2.PgId = #p;", con))
{
command.Parameters.Add(new SqlParameter("p", 337));
await Task.Run(async () =>
{
using(var productReader = await command.ExecuteReaderAsync())
while(await productReader.ReadAsync())
{
result.Add(new Product
{
Id = productReader.GetInt32(0),
Number = productReader.GetString(2),
EanNo = productReader.GetString(3),
Frequency = productReader.GetInt16(4),
Status = productReader.GetInt16(5),
NameId = productReader.GetInt32(6),
KMPI = productReader.GetByte(7),
OEM = productReader.GetString(8),
CurvesetId = productReader.GetInt16(9),
HasServiceInfo = Convert.ToBoolean(productReader.GetByte(10))
});
GetColumnValues(productReader);
}
});
}
Console.WriteLine("End");
}
private static IEnumerable<long> GetColumnValues(SqlDataReader productReader)
{
var columnValues = new List<long>();
int columnIndex = 11;
while(!productReader.IsDBNull(columnIndex))
columnValues.Add(productReader.GetInt32(columnIndex++));
return columnValues;
}
}
Here's the data in Access, just in case:
I managed to fix the problem.
I still have questions though, since I don't understand why this didn't affect Access.
I changed:
public async Task ExecuteAsync(string query, Action<IDataReader> onExecute, params DataParameter[] parameters)
{
using(var command = builder.BuildCommand(query))
{
foreach(var parameter in parameters)
command.AddParameter(parameter);
if(!connection.IsOpen)
connection.Open();
await Task.Run(async () =>
{
using(var reader = await command.ExecuteAsync())
if(await reader.ReadAsync())
onExecute(reader);
});
}
}
To:
public async Task ExecuteAsync(string query, Func<IDataReader, Task> onExecute, params DataParameter[] parameters)
{
using(var command = builder.BuildCommand(query))
{
foreach(var parameter in parameters)
command.AddParameter(parameter);
if(!connection.IsOpen)
connection.Open();
using(var reader = await command.ExecuteAsync())
await onExecute(reader);
}
}
If someone can explain why this helped, I would really appreciate it.
I have a very strange problem that only occurs when the code in question is in a high load situations. My ASP.NET web client C# code calls a T-SQL stored procedure that has an OUTPUT parameter.
Under high loads, the data being returned sometimes does not make it back to the calling C# code. I have extracted all the relevant code into the example below;
Stored procedure:
CREATE PROCEDURE GetLoginName
#LoginId BIGINT,
#LoginName VARCHAR(50) OUTPUT
AS
SET NOCOUNT ON
SELECT #LoginName = LoginName
FROM Logins
WHERE Id = #LoginId
SET NOCOUNT OFF
GO
Database base class:
public class DatabaseContextBase : IDisposable
{
private SqlConnection _connection;
private string _connectionString;
private SqlInt32 _returnValue;
private int _commandTimeout;
private static int _maxDatabaseExecuteAttempts = 3;
private static int s_commandTimeout = 30;
protected DBContextBase()
{
// try and get the connection string off the identity first...
string connectionString = GetConnectionStringFromIdentity();
if(connectionString != null)
{
ConstructionHelper(connectionString, s_commandTimeout);
}
else
{
// use the initialised static connection string, and call the other overload
// of the constructor
ConstructionHelper(s_connectionString, s_commandTimeout);
}
}
private void ConstructionHelper( string connectionString, int commandTimeout )
{
// store the connection string in a member var.
_connectionString = connectionString;
// store the timeout in a member var.
_commandTimeout = commandTimeout;
}
public static string GetConnectionStringFromIdentity()
{
IIdentity identity = Thread.CurrentPrincipal.Identity as wxyz.Security.wxyzIdentityBase;
string connectionString = null;
if(identity != null)
{
connectionString = ((wxyz.Security.wxyzIdentityBase) identity ).ConnectionString;
}
return connectionString;
}
public void Dispose()
{
if (_connection.State != ConnectionState.Closed)
{
_connection.Close();
}
_connection.Dispose();
_connection = null;
}
protected void ExecuteNonQuery(SqlCommand command)
{
SqlConnection con = this.Connection;
lock (con)
{
if (con.State != ConnectionState.Open)
{
con.Open();
}
// don't need a try catch as this is only ever called from another method in this
// class which will wrap it.
command.Connection = con;
command.Transaction = _transaction;
command.CommandTimeout = _commandTimeout;
for (int currentAttempt = 1; currentAttempt == _maxDatabaseExecuteAttempts; currentAttempt++)
{
try
{
// do it
command.ExecuteNonQuery();
// done, exit loop
break;
}
catch (SqlException sex)
{
HandleDatabaseExceptions(currentAttempt, sex, command.CommandText);
}
}
}
}
protected void HandleDatabaseExceptions(int currentAttempt, SqlException sqlException, string sqlCommandName)
{
if (DataExceptionUtilities.IsDeadlockError(sqlException))
{
if (!this.IsInTransaction)
{
// Not in a transaction and a deadlock detected.
// If we have not exceeded our maximum number of attemps, then try to execute the SQL query again.
string deadlockMessage = string.Format("Deadlock occured in attempt {0} for '{1}':", currentAttempt, sqlCommandName);
Logging.Write(new ErrorLogEntry(deadlockMessage, sqlException));
if (currentAttempt == DBContextBase.MaxDatabaseExecuteAttempts)
{
// This was the last attempt so throw the exception
throw sqlException;
}
// Wait for a short time before trying again
WaitShortAmountOfTime();
}
else
{
// We're in a transaction, then the calling code needs to handle the deadlock
string message = string.Format("Deadlock occured in transaction for '{0}':", sqlCommandName);
throw new DataDeadlockException(message, sqlException);
}
}
else if (this.IsInTransaction && DataExceptionUtilities.IsTimeoutError(sqlException))
{
// We're in a transaction and the calling code needs to handle the timeout
string message = string.Format("Timeout occured in transaction for '{0}':", sqlCommandName);
// Raise a Deadlock exception and the calling code will rollback the transaction
throw new DataDeadlockException(message, sqlException);
}
else
{
// Something else has gone wrong
throw sqlException;
}
}
/// <summary>
/// get the SqlConnection object owned by this database (already connected to db)
/// </summary>
public SqlConnection Connection
{
get {
// check whether we've got a connection string (from either identity or static initialise)
if ( _connectionString == null )
{
throw new ArgumentNullException( "connectionString", "Connection string not set" );
}
if ( _connection != null )
{
return _connection;
}
else
{
_connection = new SqlConnection( _connectionString );
return _connection;
}
}
}
/// <summary>
/// Return value from executed stored procedure
/// </summary>
public SqlInt32 ReturnValue
{
get { return _returnValue; }
set { _returnValue = value; }
}
}
Database access class:
public class AuthenticationDBCommands
{
public static SqlCommand GetLoginName()
{
System.Data.SqlClient.SqlCommand cmd = new System.Data.SqlClient.SqlCommand("GetLoginName");
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.Add(new SqlParameter("#RETURN_VALUE", System.Data.SqlDbType.Int, 0, System.Data.ParameterDirection.ReturnValue, false, 0, 0, "RETURN_VALUE", System.Data.DataRowVersion.Current, SqlInt32.Null));
cmd.Parameters.Add(new SqlParameter("#LoginId", SqlDbType.BigInt, 8, ParameterDirection.Input, false, 0, 0, "LoginId", DataRowVersion.Current, SqlInt64.Null));
cmd.Parameters.Add(new SqlParameter("#LoginName", SqlDbType.VarChar, 50, ParameterDirection.InputOutput,false, 0, 0, "LoginName", DataRowVersion.Current, SqlString.Null));
return cmd;
}
}
public class AuthenticationDBContext : DatabaseContextBase
{
public AuthenticationDBContext() : base()
{
}
public void GetLoginName(SqlInt64 LoginId, ref SqlString LoginName)
{
SqlCommand cmd = AuthenticationDBCommands.GetLoginName();
cmd.Parameters[1].Value = LoginId;
cmd.Parameters[2].Value = LoginName;
base.ExecuteNonQuery(cmd);
base.ReturnValue = (SqlInt32) cmd.Parameters[0].Value;
LoginName = (SqlString)(cmd.Parameters[2].Value);
}
}
So when it's used inside the ASP.NET web client it look like this:
protected string GetLoginName(long loginId)
{
SqlString loginName = SqlString.Null;
using (AuthenticationDBContext dbc = new AuthenticationDBContext())
{
dbc.GetLoginName(loginId, ref loginName);
}
return loginName.Value;
}
As you can see this is fairly standard stuff. But when the AuthenticationDBContext.GetLoginName() method is called by many different users in quick succession the loginName object is sometimes null.
When the SqlCommand.ExecuteNonQuery() fails to return any data it does not throw an exception.
I have tested the SQL and it always finds a value (I've inserted #LoginName into a table and it's never null). So the problem is happening after or in SqlCommand.ExecuteNonQuery();
As I said, this is an example of what it happening. In reality, data is not being returned for lots of different stored procedures.
It's also worth stating that other web clients that share the app pool in IIS are not affected when the web client in question is under a heavy load.
I'm using .NET 4.5 and my database is on SQL Server 2008.
Has anyone seen anything like this before?
Can anyone recommend any changes?
Thanks in advance,
Matt
UPDATE. Thanks for all your comments. I have made the following change to the DatabaseContextBase class.
private void ExecuteNonQueryImpl(SqlCommand command)
{
object _lockObject = new object();
lock (_lockObject)
{
SqlConnection con = this.GetConnection();
if (con.State != ConnectionState.Open)
{
con.Open();
}
// don't need a try catch as this is only ever called from another method in this
// class which will wrap it.
command.Connection = con;
command.Transaction = _transaction;
command.CommandTimeout = _commandTimeout;
for (int currentAttempt = 1; currentAttempt <= _maxDatabaseExecuteAttempts; currentAttempt++)
{
try
{
// do it
command.ExecuteNonQuery();
// done, exit loop
break;
}
catch (SqlException sex)
{
HandleDatabaseExceptions(currentAttempt, sex, command.CommandText);
}
}
if (!this.IsInTransaction)
{
con.Close();
}
}
}
public SqlConnection GetConnection()
{
if (this.IsInTransaction)
{
return this.Connection;
}
else
{
// check whether we've got a connection string (from either identity or static initialise)
if ( _connectionString == null )
{
string exceptionMessage = Language.Translate("DbContextNotInitialized");
throw new ArgumentNullException( "connectionString", exceptionMessage );
}
return new SqlConnection(_connectionString);
}
}
However, in a load test the data still sometimes comes back as null. The web client is not working in a transaction so a new SqlConnection object is created, opened and closed every time a call is made. (there are other areas of code which share the DatabaseContextBase class that do work in a transaction so the original connection property is needed)
I would like to mention that again that I'm confident that the store procedure is working correctly as I have inserted the #LoginName value into a table and it's never null.
Thanks,
Matt
Your "for loop" definition is not correct.
for (int currentAttempt = 1; currentAttempt == _maxDatabaseExecuteAttempts; currentAttempt++)
This will initialize currentAttempt to 1, run the loop, increment currentAttempt, and then check to see if currentAttempt is equal to 3, which it isn't, and exit the loop. I think what you want is
for (int currentAttempt = 1; currentAttempt <= _maxDatabaseExecuteAttempts; currentAttempt++)
I created a method while back that:
Locked a table
Read value from it
Wrote updated value back
Unlocked the table
The code worked for Oracle. Now I can't get it work for SQL Server 2008. The method is below and executing my unlocking command results in a SqlException with text:
"NOLOC" is not a recognized table hints option. If it is intended as a
parameter to a table-valued function or to the CHANGETABLE function,
ensure that your database compatibility mode is set to 90.
Code:
public static int GetAndSetMaxIdTable(DbProviderFactory factory, DbConnection cnctn, DbTransaction txn, int tableId, string userName, int numberOfIds)
{
bool isLocked = false;
string sql = string.Empty;
string maxIdTableName;
if (tableId == 0)
maxIdTableName = "IdMax";
else
maxIdTableName = "IdMaxTable";
try
{
bool noPrevRow = false;
int realMaxId;
if (factory is OracleClientFactory)
sql = string.Format("lock table {0} in exclusive mode", maxIdTableName);
else if (factory is SqlClientFactory)
sql = string.Format("select * from {0} with (TABLOCKX)", maxIdTableName);
else
throw new Exception(string.Format("Unsupported DbProviderFactory -type: {0}", factory.GetType().ToString()));
using (DbCommand lockCmd = cnctn.CreateCommand())
{
lockCmd.CommandText = sql;
lockCmd.Transaction = txn;
lockCmd.ExecuteNonQuery();
isLocked = true;
}
using (DbCommand getCmd = cnctn.CreateCommand())
{
getCmd.CommandText = CreateSelectCommand(factory, tableId, userName, getCmd, txn);
object o = getCmd.ExecuteScalar();
if (o == null)
{
noPrevRow = true;
realMaxId = 0;
}
else
{
realMaxId = Convert.ToInt32(o);
}
}
using (DbCommand setCmd = cnctn.CreateCommand())
{
if (noPrevRow)
setCmd.CommandText = CreateInsertCommand(factory, tableId, userName, numberOfIds, realMaxId, setCmd, txn);
else
setCmd.CommandText = CreateUpdateCommand(factory, tableId, userName, numberOfIds, realMaxId, setCmd, txn);
setCmd.ExecuteNonQuery();
}
if (factory is OracleClientFactory)
sql = string.Format("lock table {0} in share mode", maxIdTableName);
else if (factory is SqlClientFactory)
sql = string.Format("select * from {0} with (NOLOC)", maxIdTableName);
using (DbCommand lockCmd = cnctn.CreateCommand())
{
lockCmd.CommandText = sql;
lockCmd.Transaction = txn;
lockCmd.ExecuteNonQuery();
isLocked = false;
}
return realMaxId;
}
catch (Exception e)
{
...
}
}
So what goes wrong here? Where does this error come from? Server or client? I copied the statement from C code and it's supposed to work there. Unfortunately I can't debug and check if it works for me.
Edit: Just trying to lock and unlock (without reading or updating) results in same exception.
Thanks & BR -Matti
The TABLOCKX hint locks the table as you intend, but you can't unlock it manually. How long the lock stays on depends on your transaction level. If you don't have an active transaction on your connection, the lock is held while the SELECT executes and is discarded thereafter.
If you want to realize the sequence "lock the table -> do something with the table -> release the lock" you would need to implement the ADO.NET equivalent of this T-SQL script:
BEGIN TRAN
SELECT TOP (1) 1 FROM myTable (TABLOCKX, KEEPLOCK)
-- do something with the table
COMMIT -- This will release the lock, if there is no outer transaction present
you can either execute the "BEGIN TRAN"/"COMMIT" through DbCommand objects or you can use the System.Data.SqlClient.SqlTransaction class to start a transaction and commit it.
Attention: This approach only works if your connection is not enlisted in a transaction already! SQL Server doesn't support nested transaction, so the COMMIT wouldn't do anything and the lock would be held. If you have a transaction already running, you cannot release the lock until the transaction finishes. In this case maybe a synchronisation through sp_getapplock/sp_releaseapplock might help.
Edit: If you want to educate yourself about transactions, locking and blocking, I recommend these two videos: http://technet.microsoft.com/en-us/sqlserver/gg545007.aspx and http://technet.microsoft.com/en-us/sqlserver/gg508892.aspx
Here is answer for one table for SqlClient with code I made based on TToni's answer:
public static int GetAndSetMaxIdTable(DbProviderFactory factory, DbConnection cnctn, DbTransaction txn, int numberOfIds)
{
bool noPrevRow = false;
int realMaxId;
using (DbCommand getCmd = cnctn.CreateCommand())
{
getCmd.CommandText = "SELECT MaxId FROM IdMax WITH (TABLOCKX)"
getCmd.Transaction = txn;
object o = getCmd.ExecuteScalar();
if (o == null)
{
noPrevRow = true;
realMaxId = 0;
}
else
{
realMaxId = Convert.ToInt32(o);
}
}
using (DbCommand setCmd = cnctn.CreateCommand())
{
if (noPrevRow)
setCmd.CommandText = CreateInsertCommand(factory, tableId, userName, numberOfIds, realMaxId, setCmd, txn);
else
setCmd.CommandText = CreateUpdateCommand(factory, tableId, userName, numberOfIds, realMaxId, setCmd, txn);
setCmd.ExecuteNonQuery();
}
return realMaxId;
}
and it i's like this:
...
try
{
using (txn = cnctn.BeginTransaction())
{
oldMaxId = GetAndSetMaxIdTable(factory, cnctn, txn, 5);
for (i = 0; i < 5; i++)
{
UseNewIdToInsertStuff(factory, cnctn, txn, oldMaxId + i + 1)
}
txn.Commit();
return true;
}
}
catch (Exception e)
{
// don't know if this is needed
if (txn != null && cnctn.State == ConnectionState.Open)
txn.Rollback();
throw e;
}
...
For oracle client it seems to be desirable to have:
SELECT MaxId from IdMax WHERE ... FOR UPDATE OF MaxId
-m