Created
May 10, 2014 03:15
-
-
Save mgroves/148bf35335e5633c2884 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| [Serializable] | |
| [MulticastAttributeUsage(MulticastTargets.Method, TargetMemberAttributes = MulticastAttributes.Public)] | |
| public class ServiceErrorInterceptor : MethodInterceptionAspect | |
| { | |
| [NonSerialized] | |
| ServiceErrorConcern _concern; | |
| public static bool On = true; | |
| public override bool CompileTimeValidate(MethodBase method) | |
| { | |
| if (!typeof (ServiceBase).IsAssignableFrom(method.DeclaringType)) | |
| { | |
| var declaringType = method.DeclaringType; | |
| var declaringTypeName = "Unknown"; | |
| if (declaringType != null) | |
| declaringTypeName = declaringType.FullName; | |
| Message.Write(method, SeverityType.Error, "SEI01", "The target type (" + declaringTypeName + ") does not implement ServiceBase."); | |
| return false; | |
| } | |
| return base.CompileTimeValidate(method); | |
| } | |
| public override void RuntimeInitialize(MethodBase method) | |
| { | |
| if (!On) return; | |
| // I'm newing up directly instead of using StructureMap/IoC | |
| // because my project structure would require some extra hoops | |
| // but I plan to do that work when it makes sense | |
| // so think of this as a technical debt decision | |
| _concern = new ServiceErrorConcern(new ExceptionlessLogger()); | |
| } | |
| public override void OnInvoke(MethodInterceptionArgs args) | |
| { | |
| if (!On) | |
| { | |
| args.Proceed(); | |
| return; | |
| } | |
| _concern.OnInvoke(new MethodConcernArgs(args)); | |
| } | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| public interface IMethodConcernArgs | |
| { | |
| void Proceed(); | |
| object Instance { get; } | |
| MethodBase Method { get; } | |
| object ReturnValue { get; set; } | |
| } | |
| public class MethodConcernArgs : IMethodConcernArgs | |
| { | |
| readonly MethodInterceptionArgs _args; | |
| public MethodConcernArgs(MethodInterceptionArgs args) | |
| { | |
| _args = args; | |
| } | |
| public void Proceed() | |
| { | |
| _args.Proceed(); | |
| } | |
| public object Instance | |
| { | |
| get { return _args.Instance; } | |
| } | |
| public MethodBase Method | |
| { | |
| get { return _args.Method; } | |
| } | |
| public object ReturnValue | |
| { | |
| get { return _args.ReturnValue; } | |
| set { _args.ReturnValue = value; } | |
| } | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| public class ServiceErrorConcern | |
| { | |
| readonly IExceptionLogger _exceptionLogger; | |
| public ServiceErrorConcern(IExceptionLogger exceptionLogger) | |
| { | |
| _exceptionLogger = exceptionLogger; | |
| } | |
| public void OnInvoke(IMethodConcernArgs args) | |
| { | |
| try | |
| { | |
| args.Proceed(); | |
| } | |
| catch (SqlException ex) | |
| { | |
| var obj = (ServiceBase)args.Instance; | |
| if (ex.Message.Contains("The DELETE statement conflicted with the REFERENCE constraint")) | |
| obj.AddValidationError("This item can't be deleted because it is still used by other items."); | |
| else | |
| { | |
| obj.AddValidationError(GetErrorMessage(ex)); | |
| _exceptionLogger.Log(ex); | |
| } | |
| ErrorReturn(args); | |
| } | |
| catch (NotImplementedException) | |
| { | |
| // if something's not implemented, just throw it up | |
| throw; | |
| } | |
| catch (Exception ex) | |
| { | |
| _exceptionLogger.Log(ex); | |
| var obj = (ServiceBase)args.Instance; | |
| obj.AddValidationError(GetErrorMessage(ex)); | |
| ErrorReturn(args); | |
| } | |
| } | |
| string GetErrorMessage(Exception ex) | |
| { | |
| if (ex is SqlException) | |
| { | |
| if (IsThisSite.RunningLocally) | |
| return "There was a database error. Please try again later. (" + ex.Message + "), (" + ex.GetType().FullName + ")"; | |
| return "There was a database error. Please try again later."; | |
| } | |
| if (IsThisSite.RunningLocally) | |
| return "There was an error. Please try again later. (" + ex.Message + "), (" + ex.GetType().FullName + ")"; | |
| return "There was an error. Please try again later."; | |
| } | |
| // if the return type is ienumerable, then I want the return type to be an empty collection | |
| // if the return type is value, then I want the return to be default value | |
| // if the return type is void, don't touch the return value | |
| // if the return type is reference (should cover everything else), then return should be null | |
| void ErrorReturn(IMethodConcernArgs args) | |
| { | |
| try | |
| { | |
| var methodInfo = args.Method as MethodInfo; | |
| if (methodInfo == null) | |
| return; | |
| if (methodInfo.ReturnType == typeof(string)) | |
| args.ReturnValue = null; | |
| else if (typeof(IEnumerable).IsAssignableFrom(methodInfo.ReturnType)) | |
| args.ReturnValue = Activator.CreateInstance(methodInfo.ReturnType); | |
| else if (methodInfo.ReturnType.IsValueType) | |
| args.ReturnValue = Activator.CreateInstance(methodInfo.ReturnType); | |
| else if (methodInfo.ReturnType != typeof (void)) | |
| args.ReturnValue = null; | |
| } | |
| catch | |
| { | |
| // don't want any errors to take place trying to build the correct return value | |
| // so just swallow them, and let args.ReturnValue stay whatever it is | |
| } | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment