I'm developing using Asp.net MVC 4, NHibernate and Session-per-request.
I have a service method which updates multiple databases so the work is wrapped in a TransactionScope. I have discovered that the NHibernate Session is not usable outside the TransactionScope due to it not being thread safe.
The code is similar to this:
public void ProcessItems()
{
var items = itemService.GetAll();
var mailMessages = new List<MailMessage>();
using(var scope = new TransactionScope())
{
foreach(var item in items)
{
itemService.UpdateOne(item);
itemService.UpdateTwo(item);
try
{
mailMessages.Add(itemService.GenerateMailMessage(item));
}
catch(Exception ex)
{
// we don't want exceptions caused be generating email to prevent DB work
if (ex is InvalidOperationException
|| ex is NullReferenceException
|| ex is FormatException
|| ex is ArgumentException
|| ex is ItemNotFoundException)
{
LogError(String.Format("Unable to generate email alert for item.Id:{0} - {1}", item.Id, ex.Message), log);
}
else
{
// For exception types we don't know we can ignore rethrow
throw;
}
}
scope.Complete()
}
mailService.SendMail(mailMessages);
}
The database updates are critical to the success of the method. The email alerts are not. I don't want problems with the generation of the email alerts to prevent the database updates taking place.
My questions are:
Given the constraints does this look like a reasonable approach?
I'm worried that an exception I haven't handled may be thrown when
generating the email message. This will cause the entire TransactionScope to
be rolled back. It feels like I want any exception to be ignored
if it happens in that try block of code. However I appreciate a
catch-all is a no-no so any other suggestions for making this more
robust are welcome.
EDIT
Just to clarify my question:
I know it would be better to generate and send the email after the TransactionScope. However I am unable to do this as GenerateMailMessage() makes use of the NHibernate Session which is not safe to use outside of the TransactionScope block.
I guess what I was really asking is would it be defensible to change the catch statement above to a geniune catch-all (still with logging taking place) in order to provide as much protection to the critical UpdateOne() and UpdateTwo() calls as possible?
Update
My advice would be to try to prevent the exception from occurring. Failing that, a catch-all is likely the only option you have remaining. Logging all exceptions is going to be critical here.
1st question: Your case isn't really a catch-all, you are catching all exceptions to query the type. My only advice is to log details for the exceptions you choose to consume.
2nd question: I would completely remove the generation of email from the scope if it is liable to fail. Once the transaction rolls back, all items will be rolled back too. Create and send all the emails on successful commit.
public void ProcessItems()
{
var items = itemService.GetAll();
var mailMessages = new List<MailMessage>();
bool committed = false;
using(var scope = new TransactionScope())
{
foreach(var item in items)
{
itemService.UpdateOne(item);
itemService.UpdateTwo(item);
}
scope.Complete()
committed = true;
}
if (committed)
{
// Embed creation code and exception handling here.
mailService.SendMail(mailMessages);
}
}
I'd suggest changing this around. Instead of generating the email there and then... keep a list of the successfully processed items in a local List and then do all the mail sends at the end after you've committed.
public void ProcessItems()
{
var items = itemService.GetAll();
var successItems = new List<Item>();
var mailMessages = new List<MailMessage>();
using(var scope = new TransactionScope())
{
foreach(var item in items)
{
itemService.UpdateOne(item);
itemService.UpdateTwo(item);
successItems.Add(item);
// you still need try/catch handling for DB updates that fail... or maybe you want it all to fail.
}
scope.Complete()
}
mailMessages = successItems.Select(i => itemService.GenerateMailMessage).ToList();
//Do stuff with mail messages
}
Related
I'm trying to import a bunch of data from an Excel to a DB in my Web API, this data is being validate using FluentValidation, the problem is, every time I hit a line with bad information from the Excel table my code stops running and the API returns the exception.
I wish I could store all these exceptions, keep my code running until the end of the Excel table, and then after that return all the exceptions as my API response. I also think it would be a good idea to return in which line of the table the exception occurred.
My code is running inside a for each statement so for storing the line in which errors occurred I can simply start a counter inside of it.
As for keeping my code running I can run it inside of a Try-Catch (or would there be a better way?), but inside of it how can I store all the exceptions together to then return them later?
Most .NET parts that can return multiple exceptions use AggregateException to achieve that. A generic template for that would be:
var exceptions = new List<Exception>();
foreach (var w in work)
{
try
{
DoWork(w);
}
catch (Exception e)
{
exceptions.Add(e);
}
}
if (exceptions.Any())
{
throw new AggregateException(exceptions);
}
Here's the docs for AggregateException.
the problem is, every time I hit a line with bad information from the excel table my code stops running and the API returns the exception.
Going on with a process after an exception has never been a good idea. At best the thing you are working with is now in a invalid state and will throw more exceptions "InvalidState" every further access attempt. At worst, it is utterly broken and will cause utterly unpredictbale behavior because it asumes you would not do something so bad. Such behavior can go all the way into your process being kicked out by windows, due to Memory Access Violations.
There is basic classification I use for Exceptions. While this exception is at worst a Exogenous Exception for your code, it should be considered a fatal one for the parser and this Documet. Going on after it, is not going to add any value. At best, it adds more useless error messages wich hide the real error.
90% of the times you get a compiler error, you get a message because the Syntax is wrong. And often it is so wrong, the Source Code parser can not make heads or tails of what it is seeing anymore. It stop being able to tell you where the issue even is.
You should expose/log each exception, stop the parsing after the first one and have the user deal with the issue (wich is clearly outside your job).
You can try to add an extra column Status in your excel table , for example you can adapt your logic and store in the excel table which stores into DB.
class Program
{
static void Main(string[] args)
{
Customer Cust = new Customer();
List<Customer> Customers = Cust.GetCustomers();
foreach(Customer c in Customers)
{
Console.WriteLine(c.Name);
}
Console.Read();
}
}
public class Customer
{
public int Id { get; set; }
public string Name { get; set; }
public string Status { get; set; }
List<Customer> Customers = new List<Customer>();
public List<Customer> GetCustomers()
{
//Your Foreach Logic will be replaced
for(int i=0;i<3;i++)
{
if (i % 2 == 0)
{
Customers.Add(new Customer { Id = i, Name = "Name" + i, Status = "Success Message" });
}
else
{
Customers.Add(new Customer { Id = i, Name = "Name" + i, Status = "Error Message" });
}
}
return Customers;
}
}
I'm getting strange things since updated to EF6,no sure this is related or not, but used to be good
I'm doing a set of work, then save it to DB , then do another , save another.
after a while,i check SQL server by sp_who2 , i found many dead connections from my computer.
Job is huge then there goes to 700 connections,
I have to kill them all manually in cycle.
program like:
while (jobDone == false)
{
var returnData=doOneSetJob();
myEntity dbconn= new myEntity;
foreach( var one in retrunData)
{
dbconn.targetTable.add(one );
try
{
dbconn.savechange();
/// even i put a dispose() here , still lots of dead connections
}
catch
{
console.writeline("DB Insertion Fail.");
dbconn.dispose();
dbconn= new myEntity();
}
}
dbconn.dispose()
}
You should consider refactoring your code so that your connection is cleaned up after your job is complete. For example:
using (var context = new DbContext())
{
while (!jobDone)
{
// Execute job and get data
var returnData = doOneSetJob();
// Process job results
foreach (var one in returnData)
{
try
{
context.TargetTable.Add(one);
context.SaveChanges();
}
catch (Exception ex)
{
// Log the error
}
}
}
}
The using statement will guarantee that your context is cleaned up properly, even if an error occurs while you are looping through the results.
In this case you should use a using statement. Taken from MSDN:
The using statement ensures that Dispose is called even if an exception occurs while you are calling methods on the object. You can achieve the same result by putting the object inside a try block and then calling Dispose in a finally block; in fact, this is how the using statement is translated by the compiler.
So, your code would look better like this:
using(var dbconn = new DbContext())
{
while (!jobDone)
{
foreach(var one in retrunData)
{
try
{
targetTable row = new TargetTable();
dbconn.TargetTable.add(row);
dbconn.SaveChanges();
}
catch (Exception ex)
{
Console.WriteLine("DB Insertion Fail.");
}
}
}
}
This way, even if your code fails at some point, the Context, resources and connections will be properly disposed.
I have the following code courtesy of an answer posted by Jean-Michel Bezeau
bool isAlive = false;
string fixedAddress = "http://localhost:8732/Design_Time_Addresses/WCFService/mex";
System.ServiceModel.Description.ServiceEndpointCollection availableBindings = System.ServiceModel.Description.MetadataResolver.Resolve(typeof(WCFService.IAlive), new EndpointAddress(fixedAddress));
ChannelFactory<WCFService.IAlive> factoryService = new ChannelFactory<WCFService.IAlive>(availableBindings[0]);
WCFService.IAlive accesService = factoryService.CreateChannel();
isAlive = accesService.IsAlive();
I would like my program to continue even if the WCF Service can't be reached so that I can notify someone via email and add it to a log. I thought of doing it like this:
bool isAlive = false;
try
{
string fixedAddress = "http://localhost:8732/Design_Time_Addresses/WCFService/mex";
System.ServiceModel.Description.ServiceEndpointCollection availableBindings = System.ServiceModel.Description.MetadataResolver.Resolve(typeof(WCFService.IAlive), new EndpointAddress(fixedAddress));
ChannelFactory<WCFService.IAlive> factoryService = new ChannelFactory<WCFService.IAlive>(availableBindings[0]);
WCFService.IAlive accesService = factoryService.CreateChannel();
isAlive = accesService.IsAlive();
}
catch {}
finally
{
if (isAlive)
{
//add success message to log
}
else
{
//add warning message to log
//send email notification
}
}
However, I don't like catching all exceptions like that (I know it's bad practice). What's the best way to go about this?
Are there particular exceptions I should be catching? Or, is this a good time to implement a using statement (if so can I have some help with how)?
The exception could be lots of things - it might be just a timeout, or a 404 error, a 500 error, a connection reset error... so there's probably a bunch of exceptions that can be thrown. In this particular case I wouldn't have a problem with a global catch.
You might want to consider retries as well, if it fails the first time try again, in case it was just a timeout.
Alternatively if you already have global error handling on your app, you might not want to swallow the exception, so you could just use the finally block without the catch:
try
{
....
}
finally
{
....
}
But you'd only want to do this if it was a genuine error that the app couldn't handle or resolve by itself.
When I iterate through a foreach with the following code it successfully catches the first exception that occurs and adds the id to my error list. On all the subsequent iterations of the loop, it will continue to catch the previous exception.
How can I appropriately catch the exception and undo or clear the failed DeleteObject request so that subsequent deletes can be performed.
public ActionResult Delete(int[] ListData)
{
List<int> removed = new List<int>();
List<int> error = new List<int>();
Item deleteMe;
foreach (var id in ListData)
{
deleteMe = this.getValidObject(id);
if (deleteMe == null)
{
error.Add(id);
continue;
}
try
{
this.DB.Items.DeleteObject(deleteMe);
this.DB.SaveChanges();
removed.Add(id);
}
catch (DataException ex)
{
// revert change to this.DB.Items?
error.Add(id);
}
}
if (error.Count > 0)
{
return Json(new { Success = false, Removed = removed, Error = error });
}
return Json(new { Success = true, Removed = removed });
}
I have searched SO and google and most people will process all the delete objects first and then save changes so that it is one transaction. But I need it to process each transaction individually so a single failure does not stop the rest of the transactions.
I am using Entity Framework 4.
The exception I get for this specific example caused by foreign keys being associated to the item that is being removed. While in production I will be handling this scenario, it should be able to continue on no matter what the exception is.
I assume that the the same context, this.DB, is being used in this.getValidObject(id) to retrieve an entity. If that is the case, in the exception block call: this.DB.detach(deleteme). That should prevent the SaveChanges() to try to delete the problematic entity on the next iteration.
The code you present looks good. What is the error you see? As you've noted, maybe you need to un-tag something in this.DB.Items, though I don't think so. You could also try creating a new DataContext for each loop such that the old, failed DataContext's state on the world is irrelevant.
If I understood correctly, you cannot remove the entity(Item) because it has a foreign key association(child) to it.
You will first have to update all child(related) entities using the Parent(Item) you want to delete, by removing the relationship, updating the entity to relate too an alternative parent(Item) or deleting the child entity(entities) and then finally removing the Parent(Item) entity.
Just wondering if this is considered a clear use of goto in C#:
IDatabase database = null;
LoadDatabase:
try
{
database = databaseLoader.LoadDatabase();
}
catch(DatabaseLoaderException e)
{
var connector = _userInteractor.GetDatabaseConnector();
if(connector == null)
throw new ConfigException("Could not load the database specified in your config file.");
databaseLoader = DatabaseLoaderFacade.GetDatabaseLoader(connector);
goto LoadDatabase;
}
I feel like this is ok, because the snippet is small and should make sense. Is there another way people usually recover from errors like this when you want to retry the operation after handling the exception?
Edit: That was fast. To answer a few questions and clarify things a bit - this is part of a process which is essentially converting from a different kind of project. The _userInteractor.GetDatabaseConnector() call is the part which will determine if the user wants to retry (possibly with a different database than the one in the config they are loading from). If it returns null, then no new database connection was specified and the operation should fail completely.
I have no idea why I didn't think of using a while loop. It must be getting too close to 5pm.
Edit 2: I had a look at the LoadDatabase() method, and it will throw a DatabaseLoaderException if it fails. I've updated the code above to catch that exception instead of Exception.
Edit 3: The general consensus seems to be that
Using goto here is not necessary - a while loop will do just fine.
Using exceptions like this is not a good idea - I'm not sure what to replace it with though.
Is there another way people usually
recover from errors like this when you
want to retry the operation after
handling the exception?
Yes, in the calling code. Let the caller of this method decide if they need to retry the logic or not.
UPDATE:
To clarify, you should only catch exceptions if you can actually handle them. Your code basically says:
"I have no idea what happened, but whatever I did caused everything to
blow up... so lets do it again."
Catch specific errors that you can recover from, and let the rest bubble up to the next layer to be handled. Any exceptions that make it all the way to the top represent true bugs at that point.
UPDATE 2:
Ok, so rather than continue a rather lengthy discussion via the comments I will elaborate with a semi-pseudo code example.
The general idea is that you just need to restructure the code in order to perform tests, and handle the user experience a little better.
//The main thread might look something like this
try{
var database = LoadDatabaseFromUserInput();
//Do other stuff with database
}
catch(Exception ex){
//Since this is probably the highest layer,
// then we have no clue what just happened
Logger.Critical(ex);
DisplayTheIHaveNoIdeaWhatJustHappenedAndAmGoingToCrashNowMessageToTheUser(ex);
}
//And here is the implementation
public IDatabase LoadDatabaseFromUserInput(){
IDatabase database = null;
userHasGivenUpAndQuit = false;
//Do looping close to the control (in this case the user)
do{
try{
//Wait for user input
GetUserInput();
//Check user input for validity
CheckConfigFile();
CheckDatabaseConnection();
//This line shouldn't fail, but if it does we are
// going to let it bubble up to the next layer because
// we don't know what just happened
database = LoadDatabaseFromSettings();
}
catch(ConfigFileException ex){
Logger.Warning(ex);
DisplayUserFriendlyMessage(ex);
}
catch(CouldNotConnectToDatabaseException ex){
Logger.Warning(ex);
DisplayUserFriendlyMessage(ex);
}
finally{
//Clean up any resources here
}
}while(database != null);
}
Now obviously I have no idea what your application is trying to do, and this is most certainly not a production example. Hopefully you get the general idea. Restructure the program so you can avoid any unnecessary breaks in application flow.
Cheers,
Josh
maybe im missing something but why cant you just use a while loop? this will give you the same loop forever if you have an exception (which is bad code) functionality that your code gives.
IDatabase database = null;
while(database == null){
try
{
database = databaseLoader.LoadDatabase();
}
catch(Exception e)
{
var connector = _userInteractor.GetDatabaseConnector();
if(connector == null)
throw new ConfigException("Could not load the database specified in your config file.");
databaseLoader = DatabaseLoaderFacade.GetDatabaseLoader(connector);
//just in case??
database = null;
}
}
if you have to use goto in your normal code, you're missing logical flow. which you can get using standard constructs, if, while, for etc..
Personally, I would have this in a separate method that returns a status code of success or failure. Then, in the code that would call this method, I can have some magic number of times that I would keep trying this until the status code is "Success". I just don't like using try/catch for control flow.
Is it clear? Not really. What you actually want to do, I think, is first try to load the database and then, if that didn't work, try to load it a different way. Is that right? Let's write the code that way.
IDatabase loadedDatabase = null;
// first try
try
{
loadedDatabase = databaseLoader.LoadDatabase();
}
catch(Exception e) { } // THIS IS BAD DON'T DO THIS
// second try
if(loadedDatabase == null)
{
var connector = _userInteractor.GetDatabaseConnector();
if(connector == null)
throw new ConfigException("Could not load the database specified in your config file.");
databaseLoader = DatabaseLoaderFacade.GetDatabaseLoader(connector);
loadedDatabase = databaseLoader.LoadDatabase()
}
This more clearly illustrates what you're actually doing. As an added bonus, other programmers won't gouge out your eyes. :)
NOTE: you almost certainly don't want to catch Exception. There's likely a more specific exception that you would rather be catching. This would also catch TheComputerIsOnFireException, after which it isn't really worth retrying.
No, it's not okay: http://xkcd.com/292/
On a side note, I think there is potential for an endless loop if you always get an exception.
Technically there is nothing wrong with your goto structure, but for me, I would opt for using a while loop instead. Something like:
IDatabase database = null;
bool bSuccess = false;
int iTries = 0
while (!bSuccess) // or while (database == null)
{
try
{
iTries++;
database = databaseLoader.LoadDatabase();
bSuccess = true;
}
catch(DatabaseLoaderException e)
{
//Avoid an endless loop
if (iTries > 10)
throw e;
var connector = _userInteractor.GetDatabaseConnector();
if(connector == null)
throw new ConfigException("Could not load the database specified in your config file.");
databaseLoader = DatabaseLoaderFacade.GetDatabaseLoader(connector);
}
}