Is this a valid way to code up an MVC Service Layer? - asp.net-mvc

In my MVC service layer I have code such as the following to validate:
protected bool ValidateAccount(Account account)
{
var accounts = _accountRepository.GetPk(account.PartitionKey);
if (accounts.Any(b => b.Title.Equals(account.Title) &&
!b.RowKey.Equals(account.RowKey)))
_validationDictionary.AddError("", "Duplicate title");
return _validationDictionary.IsValid;
}
However in the "action type" methods I absorb exceptions with code like this:
public bool Create(Account account)
{
if (!ValidateAccount(account))
return false;
try
{
_accountRepository.AddOrUpdate(account);
}
catch
{
return false;
}
return true;
}
My controller is coded like this:
public ActionResult Create(BaseViewModel vm)
{
_accountService = new AccountService(new ModelStateWrapper(this.ModelState), vm.Meta.DataSourceID);
if (ModelState.IsValid)
{
_accountService = new AccountService(new ModelStateWrapper(this.ModelState), vm.Meta.DataSourceID);
if (!_accountService.Create(vm.Account))
return View("CreateEdit", vm);
else
return RedirectToAction("Created");
}
return RedirectToAction("Home");
}
return View("CreateEdit", vm);
}
Is this a reasonable approach to take? My one concern is that I might be losing exception information in the service layer.

You should at the very least log your exception somewhere (event log, file system, using Elmah, etc).
The problem with your code is that you'll never know if something bad happened in production environment or you won't be able to discover what failed exactly (just that the code returned false).
Plus, you should never handle all exceptions like you do (unfiltered catch) but only the one you can revert back to a normal state. It's far better to let the application crash than to keep it online unstable.
Avoid handling errors by catching non-specific exceptions, such as System.Exception, System.SystemException, and so on, in application code. There are cases when handling errors in applications is acceptable, but such cases are rare.
See this MSDN article on best practices for handling exceptions and Design Guidelines for Exceptions

Since you are not doing anything with the exception in your Create method in the service layer, I would suggest removing the try/catch from it.
I would still return true or false to indicate whether the create operation was successful. I will however add a try/catch in the caller to make sure exceptions are handled. In your case that could be in your controller action (or the OnException in your base controller.)
Another approach is to leave the try/catch in your Create method but as #Stephane suggested do something with it (like log it) but you could also log it wherever you catch it.

Related

Q: Using Circuit Breaker in SAP Cloud SDK resilience

When I tried to use ResilienceDecorator.executeCallable() to enable circuit breaker, I have to throw out ResilienceRuntimeException in my callable to make the circuit break work. Sample code as below. Without it, circuit breaker is always closed. is this the right way to do it?
response = ResilienceDecorator.executeCallable(() -> {
HttpResponse response1 = tryHttpClient.get().execute(request);
if (response1.getStatusLine().getStatusCode() == 404){
throw new ResilienceRuntimeException("404 error is raised when calling SB api");
}
return response1;
},
ResilienceConfiguration.of(SubscriptionBillingAdapter.class).isolationMode(ResilienceIsolationMode.TENANT_OPTIONAL).timeLimiterConfiguration(ResilienceConfiguration.TimeLimiterConfiguration.of().timeoutDuration(Duration.ofSeconds(6L))).circuitBreakerConfiguration(ResilienceConfiguration.CircuitBreakerConfiguration.of().waitDuration(Duration.ofSeconds(600000L)).failureRateThreshold(1).closedBufferSize(1).halfOpenBufferSize(1)),
e -> {LOG.warn("resiliience fallback call: " + e); return response1;});
I am asking since I don't see any document of it. Also when I check how destination configuration in SCP is retrieved, I saw the following code in com.sap.cloud.sdk.cloudplatform.connectivity.DestinationService . It doesn't throw out ResilienceRuntimeException, when using ResilienceDecorator.executeCallable(). so my question is do I need to throw out ResilienceRuntimeException or not to make circuit breaker work? if I don't need, anything wrong in my code?
return (String)ResilienceDecorator.executeCallable(() -> {
XsuaaCredentials xsuaaCredentials = (new ServiceCredentialsRetriever()).getClientCredentials("destination");
AccessToken accessToken;
if (propagateUser) {
accessToken = xsuaaService.retrieveAccessTokenViaUserTokenExchange(xsuaaCredentials.getXsuaaUri(), xsuaaCredentials.getCredentials(), useProviderTenant);
} else {
accessToken = xsuaaService.retrieveAccessTokenViaClientCredentialsGrant(xsuaaCredentials.getXsuaaUri(), xsuaaCredentials.getCredentials(), useProviderTenant);
}
return this.fetchDestinationsJson(servicePath, accessToken);
}, ResilienceConfiguration.of(DestinationService.class).isolationMode(ResilienceIsolationMode.TENANT_OPTIONAL).timeLimiterConfiguration(TimeLimiterConfiguration.of().timeoutDuration(Duration.ofSeconds(6L))).circuitBreakerConfiguration(CircuitBreakerConfiguration.of().waitDuration(Duration.ofSeconds(6L))));
Steven
I'm not the most experienced in this specific part, but looking at your code it seems fine to me. When a server returns 404 Not found it's not an indication of a service failure or error, but that resource is simply not found. If in your case 404 means that an error took place and the request has to be retried with a resilient approach, you have to throw that exception to inform Resilience4J that smth went wrong.
While we're working on improving our documentation, I recommend you take a look at the existing tutorial explaining resilience within the SAP Cloud SDK context. There we also throw ResilienceRuntimeException for clarity:
public List<BusinessPartner> execute() {
return ResilienceDecorator.executeSupplier(this::run, myResilienceConfig, e -> {
logger.warn("Fallback called because of exception.", e);
return Collections.emptyList();
});
}
private List<BusinessPartner> run() {
try {
return businessPartnerService
.getAllBusinessPartner()
.select(BusinessPartner.BUSINESS_PARTNER,
BusinessPartner.LAST_NAME,
BusinessPartner.FIRST_NAME,
BusinessPartner.IS_MALE,
BusinessPartner.IS_FEMALE,
BusinessPartner.CREATION_DATE,
BusinessPartner.TO_BUSINESS_PARTNER_ADDRESS
.select(BusinessPartnerAddress.CITY_NAME,
BusinessPartnerAddress.COUNTRY,
BusinessPartnerAddress.TO_EMAIL_ADDRESS
.select(AddressEmailAddress.EMAIL_ADDRESS)
)
)
.filter(BusinessPartner.BUSINESS_PARTNER_CATEGORY.eq(CATEGORY_PERSON))
.orderBy(BusinessPartner.LAST_NAME, Order.ASC)
.top(200)
.execute(destination);
} catch (ODataException e) {
throw new ResilienceRuntimeException(e);
}
}
Regarding the code snippet from the DestinationService, I believe that fetchDestinationsJson() method throws an implicit exception thus letting Resilience4J know that smth went wrong. While in your case HttpClient won't throw anything when receiving 404 as it's a correct response code as any other.
I also think that checking CircuitsBreaker examples from Resilience4J library might be helpful.
I hope it helps:)
No you do not have to throw a ResilienceRuntimeException. In fact the SDK only uses that to wrap checked and unchecked exceptions into an unchecked exception which wraps all kinds of failures that occur within a resilient call.
Please expand your question with more details, I'll then expand this answer:
Which version of the SDK are you using?
How (and how often) do you invoke the decorated callable? Please expand the code block.
Please specify the exact behaviour you observe. What exception is thrown when you invoke the decorated callable? If the circuit breaker opens this should be a CallNotPermittedException wrapped inside a ResilienceRuntimeException
Reduce the callable to simply throw an exception in order to simplify the code
Reduce the resilience configuration to only use a circuit breaker (leverage ResilienceConfiguration.empty()). If that works add stuff back in again until it doesn't.
For reference also please find the documentation of resilience4j which the SDK uses under the hood to perform resilient operations.

How to know if data was persisted during database transaction after method returning?

I have a method written in a Grails service, which processes a lot of data.
I noticed that, sometimes, the method returns success but the data is not persisted to the database.
I debugged it, following all the data till the end of the method and everything is fine, however data is not persisted.
The following image demonstrates the what I just explained. You can see the end of the method, in which a Map object is filled with persistent object metadata. Even you can see the console which contains the printend Hibertate SQL
How can I detect whether a rollback mechanism is thrown after successful method returning?
This is my connection properties for Oracle 12c database. Others configurations are Grails defaults
dataSource.pooled=true
hibernate.jdbc.use_get_generated_keys=true
hibernate.cache.use_second_level_cache=true
hibernate.cache.use_query_cache=false
hibernate.cache.region.factory_class=org.hibernate.cache.ehcache.EhCacheRegionFactory
dataSource.driverClassName=oracle.jdbc.driver.OracleDriver
dataSource.dialect=org.hibernate.dialect.OracleDialect
dataSource.url=jdbc:oracle:thin:#172.16.1.20:1521:db
dataSource.username=<USER>
dataSource.password=<PASS>
hibernate.default_schema=<SCHEMA>
The service is anotated as #Transactional
#Transactional
class SincronizacionService {
}
Any Idea?
When using GORM's save method, also use failOnError:true. By default, save method silently fails. However, if you use failOnError:true, it will throw an exception if the data is not persisted.
If you do not want to stop the program when the data fails to save, you can use the try-catch block to log data that failed to save and let the algorithm continue to do it work.
Hope that helps.
I found the problem. In this method actaDenunciaService.generarActaDenuncia(denuncia), there is a peculiarity. In a part of the method is located the following snippet:
try {
DNomenclador nomenclador = nomencladorService.obtenerNomencladorDNomenclador(meta.valor.toLong())
if (!nomenclador) {
return toReturn(limpiarTexto(meta.valor))
} else {
return toReturn(nomenclador.valor)
}
} catch (Exception e) {
return toReturn(limpiarTexto(meta.valor))
}
A team member changed this line nomencladorService.obtenerNomencladorDNomenclador(meta.valor.toLong()). The change represented a huge improvement of memory saving. However, the team member did not take into account a business process, which does not take into account the method he used.
Yes, a runtime exception is being thrown.
And the treatment, depending on the objective of the method, is correct
For the future, this is how the method will be from now on:
try {
DNomenclador nomenclador = nomencladorService.obtenerNomencladorDNomencladorLibre(meta.valor.toLong())
if (!nomenclador) {
return toReturn(limpiarTexto(meta.valor))
} else {
return toReturn(nomenclador.valor)
}
} catch (Exception e) {
e.printStackTrace()
return toReturn(limpiarTexto(meta.valor))
}
nomencladorService.obtenerNomencladorDNomencladorLibre(meta.valor.toLong()) for the business process
e.printStackTrace() for tracing any other peculiarity
Thanks a lot to everybody who had collaborated on finding this error
I found the error!
An error thrown inside a method for generating a PDF document with data, appearsto be failing. The second line shows this
try {
denuncia.xmlFirmadoServ = dfileManagerService.guardarDFile(signatureResponse.resultado, "xmlfirmadoservidor.xml", usuario)
denuncia = actaDenunciaService.generarActaDenuncia(denuncia).denuncia
} catch (Throwable t) {
denunciaUtilService.incrementarNumeroDenuncia(true)
throw t
}
Now, the new question is: If the method is encapsulated inside a try/catchblock, why the catch block is not excecuting?
When I comment the 2nd line inside try/catch block, data is persisted on database
With no comments, generation PDF method is executed till the end, doing all what it must do

Grails (2.3) #Transactional does not rollback

Rollback is done here as expected:
#Transactional(propagation = Propagation.REQUIRES_NEW)
def test1() {
def dummy = new Dummy(name: "test1")
dummy.save()
throw new RuntimeException("test1!")
}
But here not - which is probably wrong - try/catch should not affect the behavior:
#Transactional(propagation = Propagation.REQUIRES_NEW)
def test2() {
def dummy = new Dummy(name: "test2")
dummy.save()
try {
throw new RuntimeException("test2!")
} catch (all) {
println all.message
}
}
By default, #Transactional wraps the method such that any non-checked exception (viz., a RuntimeException) will cause the transaction to be rolled back.
If you catch/handle the exception within the method, of course, the exception doesn't propagate up to the transactional wrapper and the transaction won't be marked as rollback-only. This appears to be what you're doing.
It's worth pointing out that you can indicate that the transactional wrapper should rollback transactions if other Exceptions are thrown (and propagate to the wrapper). You can do this with the rollbackFor annotation parameter.
For example,
#Transactional(rollbackFor=Throwable.class)
void doTransactionalWork() throws MyException { ... }
will cause the transaction to be rolled back if any Throwable propagates up to the wrapper, even those that are checked (viz., MyException)
This should be the behavior of any #Transactional method, regardless of whether you're creating a new transaction or inheriting an existing transactional context.
maybe you have misunderstood the purpose of try catch or maybe you are just having a wobbly moment:
#Transactional(propagation = Propagation.REQUIRES_NEW)
def test2() {
//you may be doing other stuff here
//but now about to do some transaction work
//so lets wrap this method around a try catch
try {
//this is happening
def dummy = new Dummy(name: "test2")
dummy.save()
} catch (Exception all) { // or catch (Throwable all) {
// if something went wrong in above save method
//should be caught and runtime exception means roll back
throw new RuntimeException("test2!" +all?.toString())
}
}
I hope it explains where you went wrong but really you wish to do all of this in a service and do the try catch part in the controller -
so you do you transaction work and if things go wrong you may wish to throw additional exceptions from the service that the try catch in the controller would capture and set it to roll back.
I did a sample project years back here hope it helps
eitherway those are someone's experiments and aren't really the way you would go about doing proper coding, I mean it is a rather odd unusual way of doing things and in short he is just trying to make it throw a runtime exception therefore triggering roll back. I stick with my suggestion in the answer that you want to do a one off try catch in the controller. That attempts to capture both validation errors of the object at hand as well as failures within the failure of any given service transactional work. Something like this but probably a lot more work to capture all specific issues and return back to originating page with the underlying issues - having also now rolled back transaction.

NHibernate - How to handle critical transaction in ASP.NET MVC action

I have an action that handle a critical transaction and I am not sure what would be the best way to handle the transaction.
Here is a simplified example of what I would need to do:
[HttpPost]
public ActionResult BeginOrderProcess(Guid orderKey)
{
// Not sure what isolation level I sould use here to start with...
IsolationLevel isolationLevel = IsolationLevel.ReadCommitted;
using(new TransactionScope(isolationLevel)){
// Retreive the order
var order = GetExistingOrder(orderKey);
// Validate that the order can be processed
var validationResult = ValidateOrder(order);
if (!validationResult.Successful)
{
// Order cannot be processed, returning
return View("ErrorOpeningOrder");
}
// Important stuff going on here, but I must be sure it
// will never be called twice for the same order
BeginOrderProcess(order);
return View("OrderedProcessedSuccessfully");
}
}
First thing I would ask is: in this kind of operation, where we can have multiple requests at the same time for the same order (i.e.:quick requests from browser for same order), should I use pessimistic locking to really ensure one transaction at the time or there is a way to make sure BeginOrderProcess would never be called twice with two concurrent requests for the same order almost at the same time with optimistic locking (considering that it would probably be faster)?
Second thing: Am I doing it completely the wrong way and there is a better way to handle cases like this? In other words, how should I handle this? :)
Ok, after some research, I think I've found what I wanted.
For a case like this, it would be overkill to use pessimistic lock with nhibernate (by using session.Lock(order))
I've opted for optimistic lock simply because I didn't know how to use it before.
Here's what the code should look like:
[HttpPost]
public ActionResult BeginOrderProcess(Guid orderKey)
{
// I confirm, here I really want ReadCommit since I need optimistic lock
IsolationLevel isolationLevel = IsolationLevel.ReadCommitted;
using(var tx = new TransactionScope(isolationLevel)){
// Retreive the order
var order = GetExistingOrder(orderKey);
// Validate that the order can be processed
var validationResult = ValidateOrder(order);
if (!validationResult.Successful)
{
// Order cannot be processed, returning
return View("ErrorOpeningOrder");
}
// Important stuff going on here, but I must be sure it
// will never be called twice for the same order
BeginOrderProcess(order);
// The main difference is here
// I need to do an explicit commit here to catch the stale object exception
// and handle it properly. Before that,
// I was handling the commit in the dispose of my TransactionScope
// Since my transaction scope is in ReadCommit, no one but this request
// should be able to read the modified data whatever the changes are
try{
try{
tx.Commit();
}catch(Exception){
tx.RollBack();
throw;
}
}catch(StaleObjectStateException){
return View("OrderIsCurrentlyBeeingProcessedBySomeoneElse");
}
return View("OrderedProcessedSuccessfully");
}
}
As my comment shows, the main difference is that I handle my commit manually and then handle the exception as it would. With this implementation, I wont worry about blocking other users requests and I can handle the exception as I need.
I am using fluent nhibernate and I've configured my entities to use version in my mappings:
OptimisticLock.Version();
Version(x => x.Version)
.Column("EntityVersion")
.Generated.Never()
.Default(0)
.UnsavedValue("null");
With this, when I am doing my commit, NHibernate will look at the version and throw a StaleObjectStateException if the commit doesn't match the right version.
Happy NHibernating :)

May a scenario not have a When in BDD?

I'm currently learning/testing BDD using SpecFlow, and it works great!
Before I choose to ask my question, I have read this one, and I felt like I had to ask my question despite the fact that the same problem is addressed, because of the Exception scenario which is not mentioned.
I'm actually testing this scenario:
Scenario: I should get an error whenever I try to remove an item from an empty stack
Given I have an empty stack
When I pop from it
Then I should get an error
public class StackBehaviour {
public void GivenIHaveAnEmptyStack() { stack = new CustomStack<string>(); }
// This will throw whenever called!
// So the Then method will never be run!
// I feel like I should just put a comment which says why it's empty,
// allowing a fellow programmer to understand the exact intention.
public void WhenIPopFromIt() { stack.Pop(); }
// It is here that it verifies whether the CustomStack meets the expected behaviour.
public void ThenIShouldGetAnError() {
Assert.Throws<IndexOutOfRangeException>(delegate {
stack.Pop();
});
}
private CustomStack<string> stack;
}
public class CustomStack<T> {
public T Pop() {
if (stack.Count == 0)
throw new IndexOutOfRangeException("Cannot pop from an empty stack!");
T item = stack[stack.Count-1];
stack.RemoveAt(stack.Count-1);
return item;
}
private ArrayList stack = new ArrayList();
}
I think that leaving a comment in the When method is correct, so that the business requirement doesn't lack any information, and on the code behind, I put it clear what my intention is exactly by commenting.
What do you think? Any other ideas why I shouldn't make it?
There is another trick that you can use where the bindings help make the feature's meaning a little clearer. In this case, let's start at the end.
Then I should get an error
Your problem is a basically here, you know want an error, but you don't know how to get it. In fact you've already missed it, the error has already occurred in the When step, and in your code example, you moved the action into the then step just so you could get the error.
But what if keep the when performing the action amd re-express our then to reflect what really happens
Then I should have had an error
Seems a trivial change but now our feature reflects that the error should have been associated with the When step and we are simply evaluating it later, and that is something we can code. We just need something to remember the error in the when and deliver it to the then.
private Exception errorFromWhen = null;
public void WhenIPopFromIt()
{
try
{
stack.Pop();
}
catch(Exception ex)
{
errorFromWhen = ex;
}
}
public void ThenIShouldGetAnError()
{
errorFromWhen.ShouldNotBeNull();
errorFromWhen.ShouldBe<IndexOutOfRangeException>();
}
SpecFlow has absolutely no problems with this, in fact due to its mini Dependency injection system, you can even pass this state between binding classes if necessary.
May a scenario not have a When in BDD?
In Specflow, neither given, when or then are mandatory.
However in your example, I don't believe this is a good use of Specflow and BDD. In this answer here Marcus states:
"BDD is about ensuring that we're building the right thing, TDD is about ensuring that we're building it right."
In your example the scope of what is being tested i.e. the CustomStack, should be tested via TDD. It is the end solution that makes use of the CustomStack should be tested via BDD (and hence SpecFlow) e.g. if this CustomStack was being exercised via a certain action on a website.
This answer is also pertinent to your question.

Resources