diff --git a/.github/workflows/publish-package-prod.yml b/.github/workflows/publish-package-prod.yml
new file mode 100644
index 0000000..4e2caf4
--- /dev/null
+++ b/.github/workflows/publish-package-prod.yml
@@ -0,0 +1,60 @@
+on:
+ push:
+ tags:
+ - 'v*'
+
+jobs:
+ publish:
+ runs-on: ubuntu-latest
+ env:
+ WORKING_DIRECTORY: AElf.ExceptionHandler/
+ environment: prod
+ steps:
+ - name: Checkout repository
+ uses: actions/checkout@v2
+ with:
+ fetch-depth: 0 # Ensure the full history is fetched so we can check the commit history
+
+ - name: Verify tag is on master branch
+ id: verify_tag
+ run: |
+ # Get the commit SHA of the tag
+ TAG_COMMIT=$(git rev-list -n 1 $GITHUB_REF)
+ # Check if the commit exists on the master branch
+ if git merge-base --is-ancestor $TAG_COMMIT origin/master; then
+ echo "Tag commit is on master branch."
+ echo "IS_ON_MASTER=true" >> $GITHUB_ENV
+ else
+ echo "Tag commit is not on master branch."
+ echo "IS_ON_MASTER=false" >> $GITHUB_ENV
+ fi
+
+ - name: Stop if not on master
+ if: env.IS_ON_MASTER != 'true'
+ run: |
+ echo "This tag was not created from the master branch. Exiting."
+ exit 1
+
+ - name: Setup .NET
+ if: env.IS_ON_MASTER == 'true'
+ uses: actions/setup-dotnet@v1
+ with:
+ dotnet-version: '8.0.*' # Change this to the .NET version you're using
+
+ - name: Extract version from tag
+ if: env.IS_ON_MASTER == 'true'
+ run: |
+ TAG_NAME=$(echo $GITHUB_REF | sed 's/refs\/tags\///')
+ VERSION=${TAG_NAME#v}
+ echo "VERSION=$VERSION" >> $GITHUB_ENV
+
+ - name: Pack
+ if: env.IS_ON_MASTER == 'true'
+ working-directory: ${{ env.WORKING_DIRECTORY }}
+ run: dotnet pack --configuration Release --output nupkgs /p:Version=$VERSION
+
+ - name: Publish NuGet packages
+ if: env.IS_ON_MASTER == 'true'
+ working-directory: ${{ env.WORKING_DIRECTORY }}
+ run: |
+ dotnet nuget push "nupkgs/*.nupkg" --api-key ${{ secrets.TEMPLATES_NUGET_API_KEY }} --source ${{ vars.TEMPLATES_NUGET_SOURCE_URL }}
\ No newline at end of file
diff --git a/.idea/.idea.AOPExceptionModule/.idea/.gitignore b/.idea/.idea.AOPExceptionModule/.idea/.gitignore
deleted file mode 100644
index 041581c..0000000
--- a/.idea/.idea.AOPExceptionModule/.idea/.gitignore
+++ /dev/null
@@ -1,13 +0,0 @@
-# Default ignored files
-/shelf/
-/workspace.xml
-# Rider ignored files
-/.idea.AOPExceptionModule.iml
-/contentModel.xml
-/modules.xml
-/projectSettingsUpdater.xml
-# Editor-based HTTP Client requests
-/httpRequests/
-# Datasource local storage ignored files
-/dataSources/
-/dataSources.local.xml
diff --git a/AOPExceptionModule.sln b/AElf.ExceptionHandler.sln
similarity index 78%
rename from AOPExceptionModule.sln
rename to AElf.ExceptionHandler.sln
index 6b33a69..04f804b 100644
--- a/AOPExceptionModule.sln
+++ b/AElf.ExceptionHandler.sln
@@ -1,6 +1,6 @@
Microsoft Visual Studio Solution File, Format Version 12.00
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AOPExceptionModule", "AOPExceptionModule\AOPExceptionModule.csproj", "{0EE0548B-D1FF-4209-97EB-DAC11D0E210A}"
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AElf.ExceptionHandler", "AElf.ExceptionHandler\AElf.ExceptionHandler.csproj", "{0EE0548B-D1FF-4209-97EB-DAC11D0E210A}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
diff --git a/AElf.ExceptionHandler/AElf.ExceptionHandler.csproj b/AElf.ExceptionHandler/AElf.ExceptionHandler.csproj
new file mode 100644
index 0000000..b8b28c5
--- /dev/null
+++ b/AElf.ExceptionHandler/AElf.ExceptionHandler.csproj
@@ -0,0 +1,33 @@
+
+
+
+ Contract
+ AElf.ExceptionHandler
+ AElf ExceptionHandler
+ AElf
+ An ExceptionHandler module for use in ABP and Orleans framework.
+ abp;module;grains;orleans
+ net8.0;net7.0
+ enable
+ enable
+ https://github.com/AElfProject/AOPExceptionModule
+ git
+ https://github.com/AElfProject/AOPExceptionModule
+ AElf.ExceptionHandler
+
+
+
+
+
+
+
+
+ README.md
+ LICENSE
+
+
+
+
+
+
+
diff --git a/AElf.ExceptionHandler/AOPExceptionModule.cs b/AElf.ExceptionHandler/AOPExceptionModule.cs
new file mode 100644
index 0000000..2ce47f6
--- /dev/null
+++ b/AElf.ExceptionHandler/AOPExceptionModule.cs
@@ -0,0 +1,34 @@
+using System.Collections.Concurrent;
+using Microsoft.Extensions.DependencyInjection;
+using Volo.Abp;
+using Volo.Abp.Modularity;
+
+namespace AElf.ExceptionHandler;
+
+public class AOPExceptionModule : AbpModule
+{
+ public override void ConfigureServices(ServiceConfigurationContext context)
+ {
+ context.Services.AddTransient()
+ .AddTransient()
+ .AddSingleton>()
+ .AddSingleton>>();
+
+ context.Services.OnRegistered(options =>
+ {
+ var methodInfos = options.ImplementationType.GetMethods();
+ // Check if any of the class methods is decorated with the ExceptionHandlerAttribute
+ foreach (var methodInfo in methodInfos)
+ {
+ if (methodInfo.IsDefined(typeof(ExceptionHandlerAttribute), true))
+ {
+ var result = options.Interceptors.TryAdd();
+ if (!result)
+ {
+ throw new AbpException("ExceptionHandlingInterceptor is already registered.");
+ }
+ }
+ }
+ });
+ }
+}
diff --git a/AElf.ExceptionHandler/ExceptionHandler.cs b/AElf.ExceptionHandler/ExceptionHandler.cs
new file mode 100644
index 0000000..e46160c
--- /dev/null
+++ b/AElf.ExceptionHandler/ExceptionHandler.cs
@@ -0,0 +1,288 @@
+using System.Collections.Concurrent;
+using System.Linq.Expressions;
+using System.Reflection;
+
+namespace AElf.ExceptionHandler;
+
+public class ExceptionHandler : IInterceptor
+{
+ private readonly ConcurrentDictionary MethodCache;
+ private readonly ConcurrentDictionary> FinallyCache;
+ private List? Attributes { get; set; } = null;
+
+ public ExceptionHandler(ConcurrentDictionary methodCache, ConcurrentDictionary> finallyCache)
+ {
+ MethodCache = methodCache;
+ FinallyCache = finallyCache;
+ }
+
+ public async Task InterceptAsync(MethodExecutionArgs args)
+ {
+ try
+ {
+ await args.Invocation();
+ }
+ catch (Exception e)
+ {
+ OnException(e, args);
+ }
+ finally
+ {
+ OnFinally(args);
+ }
+ }
+
+ private void OnFinally(MethodExecutionArgs args)
+ {
+ var attributes = GetAttributes(args);
+
+ if(attributes == null)
+ {
+ return;
+ }
+
+ foreach (var attribute in attributes)
+ {
+ if (HandleFinally(args, attribute))
+ {
+ break;
+ }
+ }
+ }
+
+ private void OnException(Exception ex, MethodExecutionArgs args)
+ {
+ var attributes = GetAttributes(args);
+
+ if(attributes == null)
+ {
+ return;
+ }
+
+ var handled = false;
+
+ foreach (var attribute in attributes)
+ {
+ // If it's an AggregateException, iterate through inner exceptions
+ if (ex is AggregateException aggEx)
+ {
+ foreach (var innerEx in aggEx.InnerExceptions)
+ {
+ handled = HandleInnerException(innerEx, args, attribute);
+ }
+ if (handled)
+ {
+ break;
+ }
+ }
+ else
+ {
+ handled = HandleInnerException(ex, args, attribute);
+ if (handled)
+ {
+ break;
+ }
+ }
+ }
+
+ if (!handled)
+ {
+ throw ex;
+ }
+ }
+
+ private List? GetAttributes(MethodExecutionArgs args)
+ {
+ if (Attributes != null)
+ {
+ return Attributes;
+ }
+
+ var attributes = args.MethodInfo.GetCustomAttributes()
+ .OrderBy(attr => attr.GetType().MetadataToken);
+
+ if (attributes == null)
+ {
+ return null;
+ }
+
+ var exceptionHandlerAttributes = attributes.ToList();
+
+ if(exceptionHandlerAttributes.Count == 0)
+ {
+ return null;
+ }
+
+ Attributes = exceptionHandlerAttributes;
+
+ return Attributes;
+ }
+
+ private bool HandleFinally(MethodExecutionArgs args, ExceptionHandlerAttribute attribute)
+ {
+ if(attribute.FinallyMethodName == null || attribute.FinallyTargetType == null)
+ {
+ return false;
+ }
+
+ var finallyMethod = GetFinallyMethod(attribute.FinallyTargetType, attribute.FinallyMethodName);
+
+ var parameters = args.Arguments.ToArray();
+
+ var task = Task.Run(() => finallyMethod(args.TargetObject, parameters));
+ task.Wait();
+
+ return true;
+ }
+
+ private bool HandleInnerException(Exception exception, MethodExecutionArgs args, ExceptionHandlerAttribute attribute)
+ {
+ // If the exception is not of the specified type, return early
+ if (!attribute.Exceptions.Any(e => e.IsInstanceOfType(exception)))
+ {
+ return false;
+ }
+
+ var exceptionHandlerInfo = GetExceptionHandlerInfo(attribute.TargetType, attribute.MethodName);
+
+ var parameters = new object[] { exception };
+
+ if (!exceptionHandlerInfo.ContainsExceptionParamOnly)
+ {
+ parameters = parameters.Concat(args.Arguments.ToArray()).ToArray();
+ }
+
+ var task = Task.Run(() => exceptionHandlerInfo.Method(args.TargetObject, parameters));
+ var flowBehavior = task.Result;
+
+ if(task.Exception != null)
+ {
+ throw task.Exception;
+ }
+
+ if(flowBehavior.ExceptionHandlingStrategy == ExceptionHandlingStrategy.Rethrow)
+ {
+ throw exception;
+ }
+
+ if (flowBehavior.ReturnValue == null)
+ {
+ return true;
+ }
+
+ if(flowBehavior.ExceptionHandlingStrategy == ExceptionHandlingStrategy.Throw)
+ {
+ throw (Exception)flowBehavior.ReturnValue!;
+ }
+
+ args.ReturnValue = flowBehavior.ReturnValue;
+ return true;
+ }
+
+ private ExceptionHandlerInfo GetExceptionHandlerInfo(Type targetType, string methodName)
+ {
+ var cacheKey = CacheKey(targetType, methodName);
+
+ // Try to get the method delegate from the cache
+ if (MethodCache.TryGetValue(cacheKey, out var exceptionHandlerInfo))
+ {
+ // If the delegate exists in the cache, return early
+ return exceptionHandlerInfo;
+ }
+
+ exceptionHandlerInfo = CreateExceptionHandlerInfo(targetType, methodName);
+
+ // Cache the compiled delegate
+ MethodCache.TryAdd(cacheKey, exceptionHandlerInfo);
+
+ return exceptionHandlerInfo;
+ }
+
+ private Func