Although I read a lot about Exception Handling, I am still not sure when to throw one and when not.
For example I have an API as three tier architecture and in the DB layer events can occur.
Try to receive a customer from database, but customer with the given id was not found.
Try to delete a customer by id, but the id was not found in database.
Try to update a customer by id, but the id was not found in database.
In the first case I am not throwing an exception, because nothing really "goes wrong". My repository function just returns "null" to tell upper layers that nothing was found.
But the other two cases are already tricky.
It does not make sense to me if a deleteById-function returns "null" if the id was not found. I could return "false" if the deletion was not successful and "true" if it was successful. But then I have to transport this from database layer, through domain layer to the presentation layer. Throwing an Exception would just be easy to me.
But in this case I also did not produce "unexpected behaviour". Like in the first case, nothing "goest wrong". Isnt there any kind of "best pracice"?
What would you do?
I argue that you shouldn't throw an exception in any of these cases - here's why:
Exceptions should be used for exceptional things - mainly things you can't control in code - such as a network connection error and stuff like that.
Throwing exceptions for non-exceptional situations is just, well, vexing, as Eric Lipprt explains so much better than I can.
Try to receive a customer from database, but customer with the given id was not found.
Well, that's a no brainer - You didn't find a customer in the database - return null. No reason to throw an exception because there's nothing exceptional in this situation.
Try to delete a customer by id, but the id was not found in database.
If the customer was found in the database, then this operation would result with the removal of this customer.
If it wasn't found in the database - the end result is still the same as if it was found - so why should you care it wasn't there in the first place? Again, no reason to throw an exception.
Try to update a customer by id, but the id was not found in database.
This one is the most tricky to explain, but there are basically two legitimate ways to handle this situation:
One way is to do what any database does when an update statement has a where clause that doesn't fit any row in the table - and that's simply do nothing.
As far as letting the client know if there was an actual update or it was a no-op, you can check the number of rows effected and return true/false or customer/null to the client.
The other way, is to convert the update to an "upserts" - so if the customer is not found on the database, simply create a new customer.
This can also be indicated back to the client using a simple true/false return value. In that case, you should name the method properly - AddOrUpdateCustomer, for instance.
You should not use exceptions as a way to signal caller for expected execution flows. This should be done returning a meaningful value as the function returns. If your function return is more complex and couldn't be expressed by simple true/false returns, you can declare an enum to use as return value or even a Tuple in more complex scenarios.
Try to receive a customer from database, but customer with the given id was not found.
Try to delete a customer by id, but the id was not found in database.
Try to update a customer by id, but the id was not found in database.
All this 3 possible situations should be well handled by DB and when such a path happens, just return a value to the caller with enough information so it can handle the result and act properly.
you have to throw an exception when something unexpected goes wrong. with your example =>
a getById call return item | null if not found => there is no error if does not exist
the repo.Delete function MUST have a valid id => db function throw an exception(better if ArgumentException )... BUT the controller/manager need to catch(ArgumentException) and then 2 options:
2a. 'delete not exist item' => success -> after method call, item
does not exist
2b. es: controller send error message to client 'Item you're want to delete does not exist'
repo.Update function must have valid id => like above without 2a option
if function NEED this param => throw
if function manage 'not found=null' case => not throw
Related
I am trying to work out a solution to check if any fields that should not be duplicated are having a row added that would break this rule. I am using the respository pattern so I wanted to create a method within the respository to do this.
In this example I am trying to add a company, in the controller before it's added it calls this method in the CompanyRespository:
public bool Exists(Company company, bool ignoreId)
{
if (!ignoreId)
{
if (context.Companies.Any(c => c.Id == company.Id)) return true;
}
if (context.Companies.Any(c => c.TextId == company.TextId)) return true;
if (context.Companies.Any(c => c.Email == company.Email)) return true;
if (context.Companies.Any(c => c.PhoneNumber == company.PhoneNumber)) return true;
return false;
}
The issue I am facing is trying to return the error so the controller can send the error to the client. The obvious solution would be to just send Exceptions instead of returning true. However, if I was to call this just to say if there is a company with these parameters then I dont want exceptions sent, I just want a boolean in both cases.
The cut down question: Is it bad practice to in this situation create another method CheckDuplication() which would return exceptions instead of a boolean? If yes then what is the correct way for checking duplicated fields before saving to the database in Entity Framework Core?
Having a separate Validation check would be fine, and allows you to write a more user friendly application. However don't make the mistake of relying on this check to ensure data integrity. Multi threading would be the main cause of concern. Sql DB can guard data integrity way better, so I would suggest defining unique constraints on each of these fields (for instance check out Setting unique Constraint with fluent API?). Of course that will raise EF exceptions but those can be caught and inspected to determine if they arer violations of the constraints or not. That way you could still return a boolean if required.
As a side note the Exits method can be written more efficiently by using an OR(||), that way you don't have to fire off so many sql queries.
Entity Framework is giving a pretty cryptic error message when I try to do a simple Add operation.
_context.Users.Add(new User
{
DateJoined = DateTime.UtcNow
});
_context.SaveChanges();
FormatException: Index (zero based) must be greater than or equal to zero and less than the size of the argument list.
User inherits from IdentityUser from Microsoft.AspNet.Identity
I crossed check the database and model and I cant seem to identify what property is causing the problem. The models contains:
ints, bools, and strings - get default values
one non-nullable DateTime property (DateJoined) that I provide
one nullable DateTime property
one enum that correctly defaults to 0
virtual ICollections
I don't undestand what property is causing the issue, how can I know more specifically? Could it be something else? The exception dialog doesnt contain the familiar link to view the inner exception.
To know more about what is going on, try to do the following:
1) Add an Interceptor to see the underlying database operation. From the logs, you can see the INSERT command and probably see what is going on by comparing it to the table constraints.
2) Put a breakpoint on the SaveChanges and see the tracked entities from the Context object. Make sure that this user is the only added entity and marked as dirty. From there you can also see its properties
3) In you model, mark all non-mandatory properties as [NonMapped] one by one. Spot the problematic property this way. If those properties where inherited from the base class, you can mark them as NonMapped programmatically in your database configuration class.
4) If non of the above works, try using LinqPad to do your insert (from 1) )and see if you get a better error message
Hope this helps
Try to give the user a username, e.g.
_context.Users.Add(new User
{
UserName = "Bob",
DateJoined = DateTime.UtcNow
});
_context.SaveChanges();
I have a entity object, that has via FK referened other entity object. In my example batch.Equipment references Equipment entity. If I try to insert the object using this code:
var batch = new Batch();
batch.Equipment = session.Load<Equipment>(someEquipmentId);
session.Save(batch);
Everything is working fine, but I would expect that, if it happens that Equipment with the someEquipmentId doesn't exist, nhibernate would throw me ObjectNotFoundException, instead it throws GenericAdoException saying that there was violation of foreign key, which is obvious because the someEquipmentId doesn't exist in database, so the batch cannot be inserted with that equipment id, but I thought nhibernate would handle that for me.
So the question is, is there a way (some mapping attributes or something) that would make nhibernate throw ObjectNotFoundException in such cases, or do I have to do session.Get(someEquipmentId) and check it for null? I mean I can do that, but it gives me in my opinion unecessary db roundtrips, feels like repetitive code and I don't like checking for every reference like that as there are many and the cases where this actually happens are rare and really exception, I prefer putting it whole in try catch and processing the ObjectNotFoundException in one place. I need it to report back to user, why the insert failed, specifying which entity doesn't exist (requirement).
The answer here is pretty straightforward: Load(id) is a contract, representing the existing value. So, if the passed id value could be wrong (not existing), you do not trust it: you must use Get(id) and check the null.
Please, do read this article: Ayende - NHibernate – The difference between Get, Load and querying by id, some extract:
Get() and Load() are here for a reason, they provide a way to get an entity by primary key. That is important for several aspects, most importantly, it means that NHibernate can apply quite a few optimizations for this process.
But there is another side to that, there is a significant (and subtle) difference between Get and Load.
Load will never return null. It will always return an entity or throw an exception. Because that is the contract that we have we it, it is permissible for Load to not hit the database when you call it, it is free to return a proxy instead.
...
Get, however, is different. Get will return null if the object does not exist. Since this is its contract, it must return either the entity or null, so it cannot give you a proxy if the entity is not known to exist. Get will usually result in a select against the database, but it will check the session cache and the 2nd level cache first to get the values first.
session.Load(id) will never return null. It will return a proxy instead in your case, because the id doesn't exist. Load is purposed to NOT hit the database, but to load the object from cache.
"Load will never return null. It will always return an entity or throw an exception. Because that is the contract that we have we it, it is permissible for Load to not hit the database when you call it, it is free to return a proxy instead.
Why is this useful? Well, if you know that the value exist in the database, and you don’t want to pay the extra select to have that, but you want to get that value so we can add that reference to an object, you can use Load to do so:
s.Save(
new Order
{
Amount = amount,
customer = s.Load<Customer>(1)
}
);
The code above will not result in a select to the database, but when we commit the transaction, we will set the CustomerID column to 1. This is how NHibernate maintain the OO facade when giving you the same optimization benefits of working directly with the low level API." - Ayende Rahien
http://ayende.com/blog/3988/nhibernate-the-difference-between-get-load-and-querying-by-id
I've created a REST API in MVC3 and one the things I need to do is return a descriptive message when an update or create operation fails.
Messages like "The update operation failed while creating the customer object." isn't good enough.
My next thought was to get the message out of the exception which returns something like this:
The UPDATE statement conflicted with the FOREIGN KEY constraint "FK_Business_Category". The conflict occurred in database "MyDb_Dev", table "dbo.Category", column 'CategoryID'.
The statement has been terminated.
Which seems to be too much information. I could parse out column "CategoryID"out of the exception message... Not sure if that is reliable.
Is it possible to set up attributes for the fields so when they fail I can get that fields error description? I'm open to suggestions.
Sounds like you need to use Exception.Data. More info can be found here:
http://msdn.microsoft.com/en-us/library/system.exception.data.aspx
Place the offending code in a try catch block. Set a breakpoint somewhere in the catch section. Then add a watch item called ModelState.
ModelState.IsValid will tell you if the model is valid (or errored).
ModelState.Keys will show you the list of fields the model contains.
ModelState.Values will show you the data values for each of those respective fields. Drill down into each and you will see an Errors node with a count of how many errors are in that data item. Look for the one with more than 0 errors. Make a note of its index, and then go back to the ModelState.Keys[Index noted] to see which field is is causing the error.
To be honest, once you got this far, you'll have all the information you need to sort out any errors of this kind. I'll leave it up to you know.
Currently, I'm using the following code to perform an 'upsert' in CRM
try
{
crm.Create(c);
}
catch (SoapException)
{
crm.Update(c);
}
Am I right in assuming that this type of updating will wipe all existing information from my business entity? And if so, how can I get the entity to update from the repository? Do I need fetch, retrieve, or something like that?
Thanks.
Using exception handling for flow control is bad 99.99% of the time; in this case among other things because you never know what the actual reason for your SoapException is.
A much cleaner way would be to check whether your record's ID field has a value; if so, do an Update, if not, do a Create (and maybe add the resulting ID to your object if you use it further). (We've seen a Create on a record with an ID actually do an update years ago, but we've never been able to reproduce it.)
Other than that, #ckeller is perfectly right; an attribute that is null in your object (because it wasn't in the ColumnSet when retrieving from the database or hasn't been set otherwise) will not be touched in an Update.