I am trying to implement Entity Framework in my application and i should be able to commit and rollback the changes manually.
First time when i execute the update statement it updates the table successfully and i am able to rollback the changes. This is correct
But second time when i execute the update statement, it updates the table successfully and also commits the changes. So I am unable to rollback manually. This is wrong
Please let me know why it is happening and how to solve this issue.
The below code is just sample to reproduce my problem.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data.Common;
using System.Data;
namespace EFTest
{
public class DBOperations
{
NorthwindEntities NorthwindContext;
DbTransaction transObject;
public DBOperations()
{
}
public void ConnectDB()
{
try
{
if (NorthwindContext == null)
{
NorthwindContext = new NorthwindEntities();
if (NorthwindContext != null && NorthwindContext.Connection.State != ConnectionState.Open)
{
NorthwindContext.Connection.Open();
transObject = NorthwindContext.Connection.BeginTransaction(IsolationLevel.ReadUncommitted);
}
}
}
catch (Exception ex)
{
throw new Exception("Database Error " + ex.Message);
}
}
public int disconnect()
{
if (NorthwindContext != null && transObject != null)
{
try
{
transObject.Rollback();
}
catch (Exception)
{
}
transObject.Dispose();
NorthwindContext.Connection.Close();
NorthwindContext.Dispose();
}
return 0;
}
public void CommitTransaction()
{
if (NorthwindContext != null && transObject != null)
{
try
{
transObject.Commit();
}
catch (Exception)
{
}
}
}
public void RollbackTransaction()
{
if (NorthwindContext != null && transObject != null)
{
try
{
transObject.Rollback();
}
catch (Exception)
{
}
}
}
public int UpdateDB()
{
int _returnVal = 0;
try
{
NorthwindContext.ExecuteStoreCommand("UPDATE Orders SET OrderDate = GETDATE() WHERE OrderID = '10248'");
}
catch (Exception ex)
{
throw new Exception("Database Error " + ex.Message);
}
return _returnVal;
}
}
public class program
{
public program()
{
//Establishing the connection.
DBOperations _DBOperations = new DBOperations();
_DBOperations.ConnectDB();
//Update the datebase
_DBOperations.UpdateDB(); //Update the database but it doesn't commit the changes.
//Issue Rollback to rollback the transaction.
_DBOperations.RollbackTransaction(); //Successfully Rollbacks the database changes.
//Again Update the datebase
_DBOperations.UpdateDB(); //Update the database it commits the changes.
//Issue Rollback to rollback the transaction.
_DBOperations.RollbackTransaction(); //Rollback fails.
}
}
}
With TransactionScope Your DbOperations could look like this:
public class DBOperations : IDisposable
{
NorthwindEntities _context;
private TransactionScope _transactionScope;
public DBOperations()
{
this.Initialize();
}
private void Initialize()
{
try
{
this.Dispose();
this._transactionScope = new TransactionScope();
this._context = new NorthwindEntities();
// no need to open connection. Let EF manage that.
}
catch (Exception ex)
{
throw new Exception("Database Error " + ex.Message);
}
}
public void RollbackTransaction()
{
try
{
this._transactionScope.Dispose();
this._transactionScope = null;
this.Dispose();
this.Initialize();
}
catch (Exception)
{
// TODO
}
}
public int UpdateDB()
{
int _returnVal = 0;
try
{
this._context.ExecuteStoreCommand("UPDATE Orders SET OrderDate = GETDATE() WHERE OrderID = '10248'");
}
catch (Exception ex)
{
throw new Exception("Database Error " + ex.Message);
}
return _returnVal;
}
public void Dispose()
{
if (this._transactionScope != null)
{
this._transactionScope.Complete();
this._transactionScope.Dispose();
}
if (this._context != null) this._context.Dispose();
}
}
And the program:
public class program
{
public program()
{
using (DBOperations dbOperations = new DBOperations())
{
dbOperations.UpdateDB(); // Update the database no commit.
dbOperations.RollbackTransaction(); // Rollback.
dbOperations.UpdateDB(); // Update the database no commit.
dbOperations.RollbackTransaction(); // Rollback.
} // Commit on Dispose.
}
}
A connection that is opened within the scope of a TransactionScope automatically enlists in the transaction. The transaction is only committed by calling Commplete(). Disposing or unhandled exceptions will cause a rollback.
If you do more than just a store command, as in changing objects and relying on the context's change tracking, you may to implement a retry mechanism in stead of just discarding the context and the changes.
You need to assign new transaction after commit or rollback of transaction .
public program()
{
//Establishing the connection.
DBOperations _DBOperations = new DBOperations();
_DBOperations.ConnectDB();
//Update the datebase
_DBOperations.UpdateDB(); //Update the database but it doesn't commit the changes.
//Issue Rollback to rollback the transaction.
_DBOperations.RollbackTransaction(); //Successfully Rollbacks the database changes.
_DBOperations.ConnectDB(); //you need to assign new transaction because your last
//transaction is over when you commit or roll back
_DBOperations.UpdateDB(); //Update the database it commits the changes.
//Issue Rollback to rollback the transaction.
_DBOperations.RollbackTransaction(); //Rollback fails.
}
Related
I am developing a C# application which connects to SQL Server. If the network connection breaks, the application should be able to go into a "read-only mode" (offline mode) and only read data from a local database. Right now, I am trying to figure out how to detect the disconnect:
public int executeNonQuery(string query, List<SqlParameter> parameters)
{
int result;
using (SqlConnection sqlConnection = new SqlConnection(ConnectionString))
{
tryOpenSqlConnection(sqlConnection);
using (SqlCommand cmd = new SqlCommand(query, sqlConnection))
{
if (parameters != null)
{
cmd.Parameters.AddRange(parameters.ToArray());
}
result = cmd.ExecuteNonQuery();
}
sqlConnection.Close();
}
return result;
}
private void tryOpenSqlConnection(SqlConnection sqlConnection)
{
try
{
sqlConnection.Open();
}
catch (SqlException se)
{
if (se.Number == 26)
{
catchOfflineMode(se);
}
throw se;
}
}
//...
private void catchOfflineMode(SqlException se)
{
Console.WriteLine("SqlException: " + se.Message);
Console.WriteLine("Setting offline mode...");
//...
}
I thought about using the SQL error codes to detect the loss of connection. But the problem is that sometimes I get exceptions only after the SqlConnection already established, e.g. during execution of the command. The last exception I got was
Error Code 121 - The semaphore timeout period has expired
So, I would have to check every single error code that could have to do with losing network connection.
EDIT: I also thought about catching every SqlException and then checking the ethernet connection (e.g. pinging the server) to check whether the exception comes from a lost connection or not.
Are there better ways to do it?
I came up with my own solution by creating a simple helper class called
ExternalServiceHandler.cs
which is used as a proxy for external service calls to detect the online and offline status of the application after an operation failed.
using System;
using System.Threading;
using System.Threading.Tasks;
using Application.Utilities;
namespace Application.ExternalServices
{
class ExternalServiceHandler: IExternalServiceHandler
{
public event EventHandler OnlineModeDetected;
public event EventHandler OfflineModeDetected;
private static readonly int RUN_ONLINE_DETECTION_SEC = 10;
private static ExternalServiceHandler instance;
private Task checkOnlineStatusTask;
private CancellationTokenSource cancelSource;
private Exception errorNoConnection;
public static ExternalServiceHandler Instance
{
get
{
if (instance == null)
{
instance = new ExternalServiceHandler();
}
return instance;
}
}
private ExternalServiceHandler()
{
errorNoConnection = new Exception("Could not connect to the server.");
}
public virtual void Execute(Action func)
{
if (func == null) throw new ArgumentNullException("func");
try
{
func();
}
catch
{
if(offlineModeDetected())
{
throw errorNoConnection;
}
else
{
throw;
}
}
}
public virtual T Execute<T>(Func<T> func)
{
if (func == null) throw new ArgumentNullException("func");
try
{
return func();
}
catch
{
if (offlineModeDetected())
{
throw errorNoConnection;
}
else
{
throw;
}
}
}
public virtual async Task ExecuteAsync(Func<Task> func)
{
if (func == null) throw new ArgumentNullException("func");
try
{
await func();
}
catch
{
if (offlineModeDetected())
{
throw errorNoConnection;
}
else
{
throw;
}
}
}
public virtual async Task<T> ExecuteAsync<T>(Func<Task<T>> func)
{
if (func == null) throw new ArgumentNullException("func");
try
{
return await func();
}
catch
{
if (offlineModeDetected())
{
throw errorNoConnection;
}
else
{
throw;
}
}
}
private bool offlineModeDetected()
{
bool isOffline = false;
if (!LocalMachine.isOnline())
{
isOffline = true;
Console.WriteLine("-- Offline mode detected (readonly). --");
// notify all modues that we're in offline mode
OnOfflineModeDetected(new EventArgs());
// start online detection task
cancelSource = new CancellationTokenSource();
checkOnlineStatusTask = Run(detectOnlineMode,
new TimeSpan(0,0, RUN_ONLINE_DETECTION_SEC),
cancelSource.Token);
}
return isOffline;
}
private void detectOnlineMode()
{
if(LocalMachine.isOnline())
{
Console.WriteLine("-- Online mode detected (read and write). --");
// notify all modules that we're online
OnOnlineModeDetected(new EventArgs());
// stop online detection task
cancelSource.Cancel();
}
}
public static async Task Run(Action action, TimeSpan period, CancellationToken cancellationToken)
{
while (!cancellationToken.IsCancellationRequested)
{
await Task.Delay(period, cancellationToken);
if (!cancellationToken.IsCancellationRequested)
{
action();
}
}
}
protected virtual void OnOfflineModeDetected(EventArgs e)
{
OfflineModeDetected?.Invoke(this, e);
}
protected virtual void OnOnlineModeDetected(EventArgs e)
{
OnlineModeDetected?.Invoke(this, e);
}
}
}
The LocalMachine.isOnline() method looks like this:
namespace Application.Utilities
{
public class LocalMachine
{
// ... //
public static bool isOnline()
{
try
{
using (var client = new WebClient())
{
string serveraddress = AppSettings.GetServerHttpAddress();
using (var stream = client.OpenRead(serveraddress))
{
return true;
}
}
}
catch
{
return false;
}
}
// ... //
}
The helper class can be used every time an external service call is made. In the following example, a SQL non query is executed by the ExternalServiceHandler:
public async Task<int> executeNonQueryAsync(string query)
{
return await ExternalServiceHandler.Instance.ExecuteAsync(async () =>
{
return await DBManager.executeNonQueryAsync(query);
});
}
The solution works fine for me. If you have any better ideas, please let me know.
I have a lot of classes with this structure as you can see:
public class OrganizationUserRepository : IOrganizationUserRepository
{
private DataContext _ctx;
public OrganizationUserRepository(DataContext ctx)
{
_ctx = ctx;
}
public bool Add(OrganizationUser entity)
{
try
{
_ctx.OrganizationUsers.Add(entity);
_ctx.SaveChanges();
return true;
}
catch (Exception ex)
{
// TODO log this error
return false;
}
}
public bool Edit(OrganizationUser entity)
{
try
{
OrganizationUser Edited = _ctx.OrganizationUsers.Where(i => i.Id == entity.Id).First();
_ctx.Entry(Edited).CurrentValues.SetValues(entity);
_ctx.SaveChanges();
return true;
}
catch (Exception ex)
{
// TODO log this error
return false;
}
}
public bool Remove(string id)
{
try
{
Int64 Id = Int64.Parse(id);
OrganizationUser obj = _ctx.OrganizationUsers.Where(i => i.Id == Id).First();
_ctx.OrganizationUsers.Remove(obj);
_ctx.SaveChanges();
return true;
}
catch (Exception ex)
{
// TODO log this error
return false;
}
}
}
The db context in constructor is injected by ninject .as you can see it just one of my classes .and i have multi classes like this in another services that use a single DB .(WCF Service).But i get this error in my wcf tracelog :
ExecuteReader requires an open and available Connection. The connection's current state is open.
I am using EF code first .
I found this Wrap DbContext db = new DbContext() inusing statement. And i want to know should i use this ,if Yes how can i change my class structure to use using in my code ?
I used public Readonly DbContext .i just remove readonly and everything work fine.
I'm facing following problem:
In my project, I have done error logging into the same DB, as the application is using. That means, that if error occurs, then in each catch, there is the error stored into DB.
The problem is however, when using transactions. When error occurs, the tran rollbacks, but it also rollbacks the error logged, like in this scenario:
this is public service, used to save client changes.
public UpdateClient(client)
{
try
{
TransactionScope scope0 = new TransactionScope();
// some code
scope0.Complete();
}
catch(Exception ex)
{
Logger.LogException(ex); //log the exception into DB
}
}
this is public service, used to multiple clients changes:
public void UpdateClients(clients)
{
try
{
TransactionScope scope1 = new TransactionScope();
foreach (client c in clients)
{
UpdateClient(c);
}
scope1.Complete();
}
catch (Exception ex)
{
Logger.LogException(ex);
}
}
What happend here is, that if using UpdateClients(), and if error occurs in UpdateClient(), then it is logged into DB in UpdateClient() catch block. The scope0 is rollbacked, and doesnt affect the logged exception. But the scope1 is also rollbacked, and will rollback also the exception stored in DB in the UpdateClient() catch block.
I know, that there are options like store the errors in different DB and so on, but this is unfortunately not acceptible in current state of development.
Is there any way, how to solve this problem without major changes?
You can collect the exceptions while updating and insert them into the db outside the transaction scope:
public UpdateClient(client, bool logErrors = true)
{
try
{
TransactionScope scope0 = new TransactionScope();
// some code
scope0.Complete();
}
catch(Exception ex)
{
Logger.EnlistException(ex); // collect the exception
}
if (logErrors) Logger.WriteEnlistedExceptions();
}
public void UpdateClients(clients)
{
try
{
TransactionScope scope1 = new TransactionScope();
foreach (client c in clients)
{
UpdateClient(c, false);
}
scope1.Complete();
}
catch (Exception ex)
{
Logger.EnlistException(ex);
}
Logger.WriteEnlistedExceptions();
}
public partial class Logger
{
private static List<Exception> _exceptionList = new List<Exception>();
public static void EnlistException(Exception ex)
{
_exceptionList.Add(ex);
}
public static void WriteEnlistedExceptions()
{
foreach(Exception ex in _exceptionList)
LogException(ex);
_exceptionList.Clear();
}
}
This is of course not yet thread safe. The distinction between a call from UpdateClient or UpdateClients can be made by the stack trace of the exception.
As the answers to the question linked by Micky show, there is no way to prevent the rollback by the outer scope.
[EDIT] added logErrors flag
So I found an acceptable solution. The code will be implemented in internal method without transaction and will enlist exception in catch. Then usage is as follows:
internal UpdateClient(client)
{
try
{
// some code
}
catch(Exception ex)
{
Logger.EnlistException(ex); // collect the exception
}
}
public UpdateClient(client)
{
try
{
TransactionScope scope0 = new TransactionScope();
//call internal UpdateClient
/*internal*/ UpdateClient(client);
scope0.Complete();
}
catch(Exception ex)
{
Logger.WriteEnlistedExceptions(ex); // collect and write the exception
}
}
public void UpdateClients(clients)
{
try
{
TransactionScope scope1 = new TransactionScope();
foreach (client c in clients)
{
/*interanl*/ UpdateClient(c);
}
scope1.Complete();
}
catch (Exception ex)
{
Logger.WriteEnlistedExceptions(ex);
}
}
public partial class Logger
{
private static List<Exception> _exceptionList = new List<Exception>();
public static void EnlistException(Exception ex)
{
_exceptionList.Add(ex);
}
public static void WriteEnlistedExceptions()
{
foreach(Exception ex in _exceptionList)
LogException(ex);
_exceptionList.Clear();
}
}
I am using Entity Framework 6.1 version.
I am performing a bulk insert operation with Entity Framework. I am getting an intermittent error with DbContext:
object reference not set to an instance of object" for db context.
Here is my code:
public class ApiTimingDataProvider : IApiTimingDataProvider
{
private ApiTimingDbContext _apiDbContext;
public ApiTimingDataProvider(ApiTimingDbContext apiDbContext)
{
if (apiDbContext == null)
throw new ArgumentNullException("ApiDbContext is Null");
_apiDbContext = apiDbContext;
}
public void InsertApiData(List<ApiTimingModel> apiModelList)
{
try
{
using (var ctx = _apiDbContext)
{
using (var transactionScope = new TransactionScope())
{
// some stuff in dbcontext
ctx.Database.CommandTimeout = 600;
ctx.BulkInsert(apiModelList);
ctx.SaveChanges();
transactionScope.Complete();
}
}
}
catch (Exception ex)
{
throw ex;
}
}
}
Can anyone suggest me the reason for getting the exception?
I have the following code to test DB connection, it runs periodically to test for DB availability:
private bool CheckDbConn()
{
SqlConnection conn = null;
bool result = true;
try
{
conn = DBConnection.getNewCon();
ConnectionState conState = conn.State;
if (conState == ConnectionState.Closed || conState == ConnectionState.Broken)
{
logger.Warn(LogTopicEnum.Agent, "Connection failed in DB connection test on CheckDBConnection");
return false;
}
}
catch (Exception ex)
{
logger.Warn(LogTopicEnum.Agent, "Error in DB connection test on CheckDBConnection", ex);
return false; // any error is considered as db connection error for now
}
finally
{
try
{
if (conn != null)
{
conn.Close();
}
}
catch (Exception ex)
{
logger.Warn(LogTopicEnum.Agent, "Error closing connection on CheckDBConnection", ex);
result = false;
}
}
return result;
}
And:
static public SqlConnection getNewCon()
{
SqlConnection newCon = new SqlConnection();
newCon.ConnectionString = DBConnection.ConnectionString; // m_con.ConnectionString;
newCon.Open();
return newCon;
}
My question is: will this work as expected?
Specifically, I'm concerned about the test of the ConnectionState. Is it possible that the state will be: connecting (since Open() is synchronous)?
What should I do in that case?
You can try like this.
public bool IsServerConnected()
{
using (var l_oConnection = new SqlConnection(DBConnection.ConnectionString))
{
try
{
l_oConnection.Open();
return true;
}
catch (SqlException)
{
return false;
}
}
}
SqlConnection will throw a SqlException when it cannot connect to the server.
public static class SqlExtensions
{
public static bool IsAvailable(this SqlConnection connection)
{
try
{
connection.Open();
connection.Close();
}
catch(SqlException)
{
return false;
}
return true;
}
}
Usage:
using(SqlConnection connection = GetConnection())
{
if(connection.IsAvailable())
{
// Success
}
}
Your code seems fine, but you really need to use the IDisposable pattern, and some naming convention too:
private bool CheckDbConnection(string connectionString)
{
try
{
using(var connection = new SqlConnection(connectionString))
{
connection.Open();
return true;
}
}
catch (Exception ex)
{
logger.Warn(LogTopicEnum.Agent, "Error in DB connection test on CheckDBConnection", ex);
return false; // any error is considered as db connection error for now
}
}
And connection.Close() is not supposed to throw. Just use the using block and your are fine.
No need to test the Close state, since you have just opened it.
More about the Broken state:
Broken The connection to the data source is broken. This can occur
only after the connection has been opened. A connection in this state
may be closed and then re-opened. (This value is reserved for future
versions of the product.)
So really, no need to test that.
The Connecting state could be catch if you are in a multithread context and your instance of connection is shared. But it is not your case here.
This code does not block a UI if called.
public static class DatabaseExtensions
{
public static async Task<bool> IsConnectionViable(this string connectionStr)
{
await using var sqlConn = new SqlConnection(connectionStr);
return await sqlConn.IsConnectionViable();
}
public static async Task<bool> IsConnectionViable(this SqlConnection connection)
{
var isConnected = false;
try
{
await connection.OpenAsync();
isConnected = (connection.State == ConnectionState.Open);
}
catch (Exception)
{
// ignored
}
return isConnected;
}
}
actually, in visual studio, connection class has sonnectionstate property.
when connection state changes, connections statechange event is been trigerred.
you might want to check this article.
https://msdn.microsoft.com/en-us/library/aa326268(v=vs.71).aspx
I was using #Ramesh Durai's solution but found that on my setup at least (the app calling/testing periodically after the app had started; using .Net 3.5 with Sql Server 2012 database) that the first call to IsConnected() after taking the database offline was returning true. However, it was throwing the expected exception on the ExecuteScalar() line below:
public bool IsConnected() {
using (var conn = new SqlConnection(DBConnection.ConnectionString)) {
using (var cmd = New SqlCommand("SELECT 1", conn)) {
try {
conn.Open();
cmd.ExecuteScalar();
return true;
} catch (SqlException) {
return false;
}
}
}
}
This code is for Mysql.
public class Program
{
string connection = "SERVER=localhost; user id=root; password=; database=dbname";
private void Form1_Load(object sender, System.EventArgs e)
{
checkifconnected();
}
private void checkifconnected()
{
MySqlConnection connect = new MySqlConnection(connection);
try{
connect.Open();
MessageBox.Show("Database connected");
}
catch
{
MessageBox.Show("you are not connected to database");
}
}
public static void Main()
{
}
}