设计模式/ c#重复代码的技巧
本文关键字:代码 设计模式 | 更新日期: 2023-09-27 18:02:01
我有一个WCF服务,它记录任何异常,然后将它们作为faultexception抛出。
我做了很多重复,例如在每个服务方法。
try {
// do some work
}
catch(Exception ex)
{
Logger.log(ex);
// actually will be Fault Exception but you get the idea.
throw ex;
}
我正在寻找一个更优雅的方式来做到这一点,因为我在每个服务中剪切和粘贴try/catch。
有没有一种设计模式/c#技巧可以让它更优雅?你说的是AOP - Aspect Oriented Programming
我是这样做的,通过将"work"作为lambda传递:
public partial static class Aspect
{
public static T HandleFaultException<T>( Func<T> fn )
{
try
{
return fn();
}
catch( FaultException ex )
{
Logger.log(ex);
throw;
}
}
}
然后使用:
return Aspect.HandleFaultException( () =>
{
// call WCF
}
);
还有其他方法可以达到同样的目标,甚至一些商业产品,但我发现这种方法是最明确和灵活的。
例如,您可以编写一个方面来为您创建和处置客户端:
public partial static class Aspect
{
public static T CallClient<T>( Func<Client, T> fn )
{
using ( var client = ... create client ... )
{
return fn( client );
}
}
}
所以:
return Aspect.CallClient( client =>
{
return client.Method( ... );
}
);
然后,你可以把你通常想要应用的所有方面包装起来,创建一个主方面
在我们的一个WCF服务中也有类似的问题,我通过使用助手委托解决了这个问题:
public static void ErrorHandlingWrapper(Action DoWork)
{
try {
DoWork();
}
catch(Exception ex)
{
Logger.log(ex);
// actually will be Fault Exception but you get the idea.
throw;
}
}
用法:
public void MyMethod1()
{
ErrorHandlingWrapper(() => {
// do work
});
}
public void MyMethod2()
{
ErrorHandlingWrapper(() => {
// do work
});
}
您仍然需要重复包装器,但这将减少很多代码,并且您可以在一个地方修改try..catch
中的逻辑。
对于特定于WCF的,您可以考虑添加自己的ErrorHandler。这允许你"注入"自己的代码,以便在每次任何方法抛出异常时执行。
你可以这样设置:
serviceHost.Description.Behaviors.Add(new ErrorHandlerBehavior()); //Add your own ErrorHandlerBehaviour
public class ErrorHandlerBehavior : IErrorHandler, IServiceBehavior
{
private static readonly Logger log = LogManager.GetCurrentClassLogger();
public bool HandleError(Exception error)
{
if (error is CommunicationException)
{
log.Info("Wcf has encountered communication exception.");
}
else
{
// Log
}
return true;
}
public void ProvideFault(Exception error, MessageVersion version, ref Message fault)
{
//Here you can convert any exception to FaultException like this:
if (error is FaultException)
return;
var faultExc = new FaultException(error.Message);
var faultMessage = faultExc.CreateMessageFault();
fault = Message.CreateMessage(version, faultMessage, faultExc.Action);
}
public void AddBindingParameters(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase, Collection<ServiceEndpoint> endpoints,
BindingParameterCollection bindingParameters)
{
}
public void ApplyDispatchBehavior(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
{
foreach (ChannelDispatcherBase channelDispatcher in serviceHostBase.ChannelDispatchers)
{
var channelDisp = channelDispatcher as ChannelDispatcher;
if (channelDisp != null)
channelDisp.ErrorHandlers.Add(this);
}
}
public void Validate(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
{
}
}
这也允许你抛出各种异常并将其转换为错误异常,稍后由WCF正确处理,而无需修改业务层或应用try/catch代码并在那里转换它们。
模板方法模式就是这样做的,您可以使用c#的委托(或lambdas)轻松实现它,而无需继承。
您可能想尝试AspectF(不是我的):http://www.codeproject.com/Articles/42474/AspectF-Fluent-Way-to-Add-Aspects-for-Cleaner-Main。作者是Omar Al-Zabir…Dropthings框架的创建者,以及其他东西。
如果你只是抛出,所以传播异常在调用堆栈的顶部,你可以避免在每个深层处理它,只捕获它在最顶层(可能的)和日志。记住,Exception
对象也包含堆栈跟踪。
顺便说一下,如果您需要在每个级别上捕获,请记住使用throw
,因此调用堆栈不会受到影响,而是以您使用它的方式,它会。
您也可以尝试在global.asax
文件中订阅Application_Error
事件。
对于桌面应用程序,在AppDomain
中有一个UnhandledException
事件
一般来说,您可以编写一些异常处理程序来为您完成冗余的工作:
public abstract class ExceptionHandler
{
/// Returns true if the exception is handled; otherwise returns false.
public abstract bool Handle(Exception ex);
protected void Log(Exception ex)
{
// Log exception here
}
}
public class FileExceptionHandler : ExceptionHandler
{
public override bool Handle(Exception ex)
{
this.Log(ex);
// Tries to handle exceptions gracefully
if (ex is UnauthorizedAccessException)
{
// Add some logic here (for example encapsulate the exception)
// ...
return true;
}
else if (ex is IOException)
{
// Another logic here
// ...
return true;
}
// Did not handled the exception...
return false;
}
}
public class Program
{
private static void Main(string[] args)
{
try
{
// File manipulation
throw new IOException();
}
catch (Exception ex)
{
if (!new FileExceptionHandler().Handle(ex))
{
// Exception not handled, so throw exception
throw;
}
}
Console.WriteLine("end");
}
}
如果您的问题是关于如何使您当前的模式更快地为您工作,您可以通过创建Snippet
来重复锅炉板代码
<CodeSnippets
xmlns="http://schemas.microsoft.com/VisualStudio/2005/CodeSnippet">
<CodeSnippet Format="1.0.0">
<Header>
<Title>
trylog
</Title>
<Shortcut>
trylog
</Shortcut>
</Header>
<Snippet>
<Code Language="CSharp">
<![CDATA[try {
// do some work
}
catch(Exception ex)
{
Logger.log(ex);
// actually will be Fault Exception but you get the idea.
throw ex;
}]]>
</Code>
</Snippet>
</CodeSnippet>
</CodeSnippets>