ASP.NET Core Interception with Castle.DinamicProxy doesn't throw Exception with Async Methods !!! How can I solve this? - asp.net-core

I have been creating a project with Aspect Oriented Programming paradigm and
I have an "ExceptionLogAspect" class attribute which is used on business methods to log the errors throwing from them.
public class ExceptionLogAspect : MethodInterception
{
private readonly LoggerServiceBase _loggerServiceBase;
private static byte _risk;
public ExceptionLogAspect(Type loggerService, byte risk)
{
if (loggerService.BaseType != typeof(LoggerServiceBase))
{
throw new System.Exception(AspectMessages.WrongLoggerType);
}
_loggerServiceBase = (LoggerServiceBase)Activator.CreateInstance(loggerService);
_risk = risk;
}
protected override void OnException(IInvocation invocation, System.Exception e)
{
var logDetailWithException = GetLogDetail(invocation);
logDetailWithException.ExceptionMessage = e.Message;
_loggerServiceBase.Error(logDetailWithException);
}
}
This Aspect migrates MethodInterception class that I created with Castle.DinamicProxy package. And OnException method included by MethodInterception logs the exception data.
public abstract class MethodInterception:MethodInterceptionBaseAttribute
{
protected virtual void OnBefore(IInvocation invocation){}
protected virtual void OnAfter(IInvocation invocation){}
protected virtual void OnException(IInvocation invocation, System.Exception e){}
protected virtual void OnSuccess(IInvocation invocation){}
public override void Intercept(IInvocation invocation)
{
var isSuccess = true;
OnBefore(invocation);
try
{
invocation.Proceed();//Business Method works here.
}
catch (Exception e)
{
isSuccess = false;
OnException(invocation, e);
throw;
}
finally
{
if(isSuccess)
OnSuccess(invocation);
}
OnAfter(invocation);
}
}
When I run the code and try-catch block doesn't work for Exception. So catch block isn't called and no messages are logged.
If I turn the business method into a syncronous method, exception will be thrown and data will be logged.
How can I solve this asynchronous method problem?

I tried this solution, it works properly.
Intercept method has to be like this to make this process asynchronous.
Otherwise, this method doesn't work properly for async.
There are some other ways, for example Castle CoreAsync Interceptor, you can find it on Github or NuGet.
https://github.com/JSkimming/Castle.Core.AsyncInterceptor
public override void Intercept(IInvocation invocation)
{
var isSuccess = true;
OnBefore(invocation);
try
{
invocation.Proceed(); //Metodu çalıştır
if (invocation.ReturnValue is Task returnValueTask)
{
returnValueTask.GetAwaiter().GetResult();
}
if (invocation.ReturnValue is Task task && task.Exception != null)
{
throw task.Exception;
}
}
catch (Exception e)
{
isSuccess = false;
OnException(invocation, e);
throw;
}
finally
{
if (isSuccess)
OnSuccess(invocation);
}
OnAfter(invocation);
}

Related

Show custom message without breaking page using exception filter

I am using one MVC application where i have to handle all exception occurs in the code. I have found about exception filter and implemented there. Below is the created exception filter code:
public class HandleException : HandleErrorAttribute
{
#region Log Initialization
FileLogService logService = new
FileLogService(typeof(HandleException));
#endregion
public override void OnException(ExceptionContext filterContext)
{
filterContext.ExceptionHandled = true;
Log(filterContext.Exception);
base.OnException(filterContext);
}
private void Log(Exception exception)
{
logService.Error(exception.ToString());
}
}
Now i used this filter as attribute in my controller like below:
[AuthSession]
[HandleException]
public class OrganizationalController : BaseController
{
public ActionResult OrgSummary()
{
try
{
int a = 1, b = 0;
int result = a / b;
}
catch (Exception ex)
{
throw ex;
}
ViewData["ShowGrid"] = false;
return View();
}
}
As you can see in above code i am trying to generate exception in the code. In catch exception block when i used throw keyword then exception filter getting executed else not.
Now i need here when any exception occurs in the application i need to show a custom popup message for user. In popup message once user click on ok button then user should be available on the same page. The page should not break or get blank.
How could i implement this functionality?
Try this code. May be it helps
public class MyExceptionFilter: FilterAttribute, IExceptionFilter
{
public void OnException(ExceptionContext filterContext)
{
// below code will redirect to the error view
filterContext.Result = new RedirectResult("ErrorPage.html");
filterContext.ExceptionHandled = true;
}
}
and then you need to apply the above as an attribute to your action methods like:
[MyExceptionFilter]
public ActionResult XYZ()
{
}

Retrofit Rxjava error handler

I have a service class which is used to call api requset
here is an example method:
public Observable<List<Category>> test(Location location, int radius) {
Observable<CategoryListResponse> observable = api.test();
return observable
.doOnNext(new Action1<CategoryListResponse>() {
#Override
public void call(CategoryListResponse categoryListResponse) {
//handle error
}
})
.flatMap(new Func1<CategoryListResponse, Observable<Category>>() {
#Override
public Observable<Category> call(CategoryListResponse categoryListResponse) {
return Observable.from(categoryListResponse.getCategories());
}
})
.map(new Func1<Category, Category>() {
#Override
public Category call(Category category) {
//do something...
return category;
}
})
.toList();
}
And subscribe() will be called in another class.
observable.subscribe(new Action1<List<Category>>() {
#Override
public void call(List<Category> categories) {
//on success
}
}, new Action1<Throwable>() {
#Override
public void call(Throwable throwable) {
//on error
}
});
I was thinking to do error handling in the doOnNext() before it is returned back. but how can I trigger onError()?
You should throw a runtime exception and control the exception in onError operator in case that happens
Observable<CategoryListResponse> observable = api.test();
return observable
.doOnNext(list -> {
try{
request(list);
catch(Exception e){
throw new RuntimeException(e);
}
}).onError(t->//Here you control your errors otherwise it will be passed to OnError callback in your subscriber)
.flatMap(item -> Observable.from(item.getCategories()))
.map(category-> category)
.toList();
}
Try to use lambdas, make your code much more clear and readable
You can see some RxJava examples here https://github.com/politrons/reactive

Exception handling on Tasks without Wait()

What is the best approach to handle exception of a Task that does not Wait()? I read a couple of blogs which spoke about using ContinueWith because regular try/catch cannot handle Task exception. Below code does not validate that.
Method 1:
public class Service1 : IService1
{
public string GetData(int value)
{
var a = Task.Factory.StartNew(ThrowException);
return string.Format("You entered: {0}", value);
}
private void ThrowException()
{
try
{
Thread.Sleep(6000);
throw new ArgumentException("Hello from exception");
}
catch (Exception)
{
Trace.WriteLine("Log it");
}
}
}
Method 2:
public class Service1 : IService1
{
public string GetData(int value)
{
var a = Task.Factory.StartNew(ThrowException);
a.ContinueWith(c => { Trace.WriteLine("Log it"); },
TaskContinuationOptions.OnlyOnFaulted);
return string.Format("You entered: {0}", value);
}
private void ThrowException()
{
Thread.Sleep(6000);
throw new ArgumentException("Hello from exception");
}
}
Are Method 1 and Method 2 doing the same thing? Is there a better way to implement this.
Edit: Added code snippet for continuewith.
Both methods work and they are equivalent. Choose what you like most. The continuation based one has the advantage that you can make the error handling into an extension method (or some other central helper).
Are you aware that IIS worker processes can suddenly disappear for many reasons? In that case background work is lost. Or, the work faults but the error handler disappears.
It looks like it will work if all you need is to call methods on the Trace class. However, if you need custom exception handling, I would recommend injecting an exception handler:
private void ThrowException(Action<Exception> handleExceptionDelegate)
{
try
{
// do stuff that may throw an exception
}
catch (Exception ex)
{
if (handler != null)
handleExceptionDelegate(ex);
}
}
Then you could do
Task.Factory.StartNew(() =>
{
ThrowException((ex) =>
{
// Handle Exception
});
});

Retrying Event For SqlAzureExecutionStrategy

When working with SQL Azure, if I create my own RetryPolicy, e.g.:
var retryStrategy = new FixedInterval(3, TimeSpan.FromSeconds(10));
var retryPolicySQL = new RetryPolicy<SqlAzureTransientErrorDetectionStrategy>(retryStrategy);
retryPolicySQL.Retrying += ....;
I am able to get notified when a retry is happening which is useful to log.
However, if I use what seems to be the new recommended strategy with EF6 and Azure - i.e. a custom DbConfiguration class something like this:
public class MyConfiguration : DbConfiguration
{
public MyConfiguration()
{
this.SetExecutionStrategy("System.Data.SqlClient", () =>
{
var strat = new SqlAzureExecutionStrategy();
// strat. No events
return strat;
});
}
}
I can't seem to find a way to hook into the retrying process. Is there a way to do this?
Implement the protected method called ShouldRetryOn by subclassing from SqlAzureExecutionStrategy. In that method you could put your logic to log or hook it into a handler as I show below.
public delegate void ChangedEventHandler(object sender, EventArgs e);
public class MyStrategy : SqlAzureExecutionStrategy
{
public event ChangedEventHandler Changed;
protected override bool ShouldRetryOn(Exception exception)
{
OnChanged(EventArgs.Empty);
return base.ShouldRetryOn(exception);
}
protected virtual void OnChanged(EventArgs e)
{
if (Changed != null)
Changed(this, e);
}
}
If you would perfer to just log the exception or the retry, you can do as follows:
public class LoggedSqlAzureExecutionStrategy : SqlAzureExecutionStrategy
{
protected override bool ShouldRetryOn(Exception exception)
{
var shouldRetry = base.ShouldRetryOn(exception);
if (shouldRetry)
{
// log retry
}
return shouldRetry;
}
}

NServiceBus retrying message even though no exception

I got a wierd problem with NServiceBus retying the message X number of times although no exception is thrown in the handler. There is some information out there dealing with the NHibernate session and the NSB ambiant transaction. Since no error is thrown I'm not 100% sure of the problem and thus can't really decide on what to do.
I got NSB configured with Castle Windsor like so:
IWindsorContainer container = new WindsorContainer(new XmlInterpreter());
container.Install(new ContainerInstaller());
container.Install(new UnitOfWorkInstaller(AppDomain.CurrentDomain.BaseDirectory, Castle.Core.LifestyleType.Scoped));
container.Install(new FactoryInstaller(AppDomain.CurrentDomain.BaseDirectory));
container.Install(new RepositoryInstaller(AppDomain.CurrentDomain.BaseDirectory));
Configure.With()
.CastleWindsorBuilder(container)
.FileShareDataBus(Properties.Settings.Default.DataBusFileSharePath)
.MsmqTransport()
.IsTransactional(true)
.PurgeOnStartup(false)
.UnicastBus()
.LoadMessageHandlers()
.ImpersonateSender(false)
.JsonSerializer();
The UnitOfWorkInstaller registers the unit of work (the NHibernate session) like so:
public void Install(IWindsorContainer container, IConfigurationStore store)
{
var fromAssemblyDescriptor = AllTypes.FromAssemblyInDirectory(new AssemblyFilter(_installationPath));
container.Register(fromAssemblyDescriptor
.IncludeNonPublicTypes()
.Pick()
.If(t => t.GetInterfaces().Any(i => i == typeof(IUnitOfWork)) && t.Namespace.StartsWith("Magma"))
.WithService.AllInterfaces()
.Configure(con => con.LifeStyle.Is(_lifeStyleType).UsingFactoryMethod(k => k.Resolve<IUnitOfWorkFactory>().Create())));
}
So each time a message arrives the same unit of work is used for all the repositories. I read that manually rolling back the current transaction results in an error (I don't really know why) and I also know that NSB creates a child container for every transport message and that this child container is disposed after the processing of the message. The problem is that when the child container is disposed the unit of work is disposed this way:
public void Dispose()
{
if (!_isDisposed)
{
DiscardSession();
_isDisposed = true;
}
}
private void DiscardSession()
{
if (_transaction != null && _transaction.IsActive)
{
_transaction.Dispose();
}
if (Session != null)
{
Session.Dispose();
}
}
My handlers are structured like this: (the _unitOfWork is passed as a constructor dependency)
public void Handle(<MessageType> message)
{
using (_unitOfWork)
{
try
{
// do stuff
_unitOfWork.Commit();
}
catch (Exception ex)
{
_unitOfWork.Rollback();
// rethrow so the message stays in the queue
throw;
}
}
}
I found out that if I don't commit the unit of work (which flushes the session and commit the transaction) I got an error saying that the message has retried beyond the max retry count bla bla bla...
So it seems to be linked with the NHibernate session and the way it's created and disposed of but since it's created within the unit of work I can't really use the session factory. I read that I could use the IMessageModule to create and dispose the session but again I don't know if this is the right way to go since I don't understand what's causing the error in the first place.
So to recap:
I'm using a scoped unit of work so that all the handler dependencies using it will share the same instance (thx to the child container, BTW: I've setup the unit of work as transient thinking that the child container will treat all transient object as singleton within that container but I saw that the unit of work wasn't shared so this is why it's setup as scoped)
I'm wrapping my handler in a using(_unitOfWork) { } statement to dispose the unit of work after each processing.
When the unit of work get's disposed, the NHibernate session is also disposed
If I don't explicitly call Commit on the _unitOfWork, the message retries beyond the max retry count and then an error is thrown.
What is causing this behavior? and is the IMessageModule is the answer for this?
I think I narrowed it down a bit... I removed all of the using(_unitOfWork), the _unitOfWork.Commit() and the _unitOfWork.Rollback() and let the NSB TransactionScope do the work of commit or rollback the transaction since the NHibernate's Session was enlisting in the NSB transaction scope.
I also started to use the NHibernate session's transaction (Session.Transaction) instead of grabbing a reference to it through Session.BeginTransaction() and using this one. I've copy/pasted my UoW implementation so you can see the differences (the old code is in comments).
I don't know if my changes account for anything but using the Session's transaction and removing the flushes since it's taking care of within the transaction commit seems to have solved the problem... I don't have to explicitly call the Commit method in order for the message to be successfully processed anymore. Here is my UoW implementation:
public class NHibernateUnitOfWork : INHibernateUnitOfWork
{
//private ITransaction _transaction;
private bool _isDisposed;
private bool _isInError;
public ISession Session { get; protected set; }
public NHibernateUnitOfWork(ISession session)
{
Contract.Requires(session != null, "session");
Session = session;
//_transaction = Session.BeginTransaction();
// create a new transaction as soon as the session is available
Session.BeginTransaction();
_isDisposed = false;
_isInError = false;
}
public void MarkCreated(Object entity)
{
// assert stuff
try
{
Session.SaveOrUpdate(entity);
//Session.Flush();
}
catch (HibernateException ex)
{
HandleError();
throw;
}
}
public void MarkUpdated(Object entity)
{
// assert stuff
try
{
Session.Update(entity);
//Session.Flush();
}
catch (HibernateException ex)
{
HandleError();
throw;
}
}
public void MarkSavedOrUpdated(Object entity)
{
// assert stuff
try
{
Session.SaveOrUpdate(entity);
//Session.Flush();
}
catch (HibernateException)
{
HandleError();
throw;
}
}
public void MarkDeleted(Object entity)
{
// assert stuff
try
{
Session.Delete(entity);
//Session.Flush();
}
catch (HibernateException ex)
{
HandleError();
throw;
}
}
public void Commit()
{
// assert stuff
try
{
//Session.Flush();
//_transaction.Commit();
Session.Transaction.Commit();
}
catch (HibernateException ex)
{
HandleError();
throw;
}
}
public void Rollback()
{
// assert stuff
try
{
//if (!_transaction.WasRolledBack)
//{
// _transaction.Rollback();
//}
Session.Transaction.Rollback();
}
catch (HibernateException ex)
{
HandleError();
throw;
}
}
public void Dispose()
{
if (!_isDisposed)
{
DiscardSession();
_isDisposed = true;
}
}
private void DiscardSession()
{
//if (_transaction != null && _transaction.IsActive)
//{
// _transaction.Dispose();
//}
if (Session != null)
{
try
{
// rollback all uncommitted changes
if (Session.Transaction != null && Session.Transaction.IsActive)
{
Session.Transaction.Rollback();
}
//Session.Clear();
Session.Close();
}
catch (Exception)
{ }
finally
{
Session.Dispose();
}
}
}
private void HandleError()
{
_isInError = true;
//if (_transaction != null && _transaction.IsActive)
//{
// _transaction.Rollback();
//}
if (Session.Transaction != null && Session.Transaction.IsActive)
{
Session.Transaction.Rollback();
}
}
// assert methods
}
Does any of this makes sense? I still don't know what caused the error in the first place but it seems to have to do with disposing the NHibernate Session before the transaction scope finishes.