如何在c#中创建异常处理和日志类

本文关键字:异常处理 日志 创建 | 更新日期: 2023-09-27 18:12:56

我正在做一个小项目,我正在尝试创建一个处理异常和日志的层。

这一层将位于用户界面和数据访问层之间,就像BAL一样,它将有一些通用的方法,然后启动对数据访问层的进一步调用。

像这样的

Public Class ExceptionHandler
{
//which should take a method name,parameters and return a object.
Public T InitiateDatabaseCall(//method name as input,parameters)
{
try
{
//then make the call to the method using the input parameter and pass the parameters
}
catch(Exception e)
{
// do logging
}
}

该层将作为处理和记录异常的中心存储库。我不能创建我所描述的方法,专家可以提供一些代码片段来展示这个场景。

编辑:添加代码

static void Main(string[] args)
        {
            BAL b = new BAL();
            var ll = b.GetFieldList("xxxxyyyy");
        }


    public class BAL
    {
        public List<Fields> GetFieldList(string screen)
        {
            if(!string.IsNullOrEmpty(screen))
            {
                ExceptionHandler.InitiateCall(() =>GetList(screen) ));
            }
        }
    }

    public static class ExceptionHandler
    {
        public T InitiateCall<T>(Func<T>method,object[] parms) where T : object 
        {
            try
            {
                return method.Invoke();
            }
            catch(Exception ex)
            {
                return default(T);
            }
        }
    }
    public class DAL
    {
        public  List<Fields> GetList(string name)
        {
            VipreDBDevEntities context = new VipreDBDevEntities();
            return context.Database.SqlQuery<Fields>("SCREEN_FIELDS_SELECT @SCREEN_NAME", name).ToList();
        }
    } 

给出GetList()在当前上下文中不存在的错误

如何在c#中创建异常处理和日志类

对于这些事情,AOP(面向方面编程,参见https://en.wikipedia.org/wiki/Aspect-oriented_programming)非常适合。

这些是横切关注点,如果处理不当,会使代码混乱。

参见AOP框架PostSharp的例子。即使使用免费版本,也很容易编写代码。也有(可能是付费的)内置方面,如http://doc.postsharp.net/exception-tracing。

一个简单的替代方法是使用Func或Action(在控制台应用中尝试一下):

static void Main(string[] args)
{
    ExceptionHandler.InitiateDatabaseCall(() => CallDb("Dummy"));
    ExceptionHandler.InitiateDatabaseCall<int>(() => { throw new InvalidOperationException(); });
}
int CallDb(string justToShowExampleWithParameters)
{
    return 5;   
}
public static class ExceptionHandler
{
    public static T InitiateDatabaseCall<T>(Func<T> method)
    {
        try
        {
            return method.Invoke();
        }
        catch (Exception e)
        {
            // do logging
            Console.WriteLine(e.Message);
            return default(T); // or `throw` to pass the exception to the caller
        }
    }
}
编辑:

基于你在问题中添加的代码,你可以通过一些小的修改来解决关于GetList()的错误:

static void Main(string[] args)    {
    BAL b = new BAL();
    var ll = b.GetFieldList("xxxxyyyy");
}
public class BAL
{
    public List<Fields> GetFieldList(string screen)
    {
        if (!string.IsNullOrEmpty(screen))
        {
            return ExceptionHandler.InitiateCall(() => new DAL().GetList(screen)); // Slight modification of your code here
        }
        else
        {
            return null; // or whatever fits your needs
        }
    }
}
public class ExceptionHandler
{
    public static T InitiateCall<T>(Func<T> method) 
    {
        try
        {
            return method.Invoke();
        }
        catch (Exception ex)
        {
            //log
            return default(T);
        }
    }
}
public class DAL
{
    public  List<Fields> GetList(string name)
    {
        VipreDBDevEntities context = new VipreDBDevEntities();
        return context.Database.SqlQuery<Fields>("SCREEN_FIELDS_SELECT @SCREEN_NAME", name).ToList();
    }
}

根据提供的代码,在InitiateCall中不需要object[] parms参数。方法调用所需的任何参数都在Func<T>

中给出。

我个人认为日志记录应该以两种方式完成:

  1. 步骤日志(当你在代码中记录一些步骤时)

  2. 范围日志(当你记录一些代码的开始/结束或时间)

所以,我总是选择用以下两种方式创建ILogger类:
   public sealed class Logger : ILogger
    {
        private readonly Serilog.ILogger _seriLogger;
        public Logger(Serilog.ILogger seriLogger)
        {
            _seriLogger = seriLogger;
        }
        public void Debug(string format, params object[] args)
        {
            _seriLogger.Debug(format, args);
        }
        public void Info(string format, params object[] args)
        {
            _seriLogger.Information(format, args);
        }
        public void Warn(string format, params object[] args)
        {
            _seriLogger.Warning(format, args);
        }
        public void Error(Exception e, string format, params object[] args)
        {
            _seriLogger.Error(e, format, args);
        }
        public void Fatal(Exception e, string format, params object[] args)
        {
            _seriLogger.Fatal(e, format, args);
        }
        public IDisposable GetScope(string name, long timeout = 0)
        {
            return new LoggerScope(this, name, timeout);
        }
    }
    internal class LoggerScope : IDisposable
    {
        private readonly ILogger _logger;
        private readonly string _name;
        private readonly long _timeout;
        private readonly Stopwatch _sw;
        private bool ExceedScope
        {
            get { return _timeout > 0; }
        }
        public LoggerScope(ILogger logger, string name, long timeout)
        {
            _logger = logger;
            _name = name;
            _timeout = timeout;
            if (!ExceedScope)
            {
                _logger.Debug("Start execution of {0}.", name);
            }
            _sw = Stopwatch.StartNew();
        }
        public void Dispose()
        {
            _sw.Stop();
            if (ExceedScope)
            {
                if (_sw.ElapsedMilliseconds >= (long)_timeout)
                {
                    _logger.Debug("Exceeded execution of {0}. Expected: {1}ms; Actual: {2}ms.", _name, _timeout.ToString("N"), _sw.Elapsed.TotalMilliseconds.ToString("N"));
                }
            }
            else
            {
                _logger.Debug("Finish execution of {0}. Elapsed: {1}ms", _name, _sw.Elapsed.TotalMilliseconds.ToString("N"));
            }
        }
    }

然后,如果我想记录一些东西,我这样使用它,不使用AOP:

using(_log.GetScope("Some describable name"))
{
   //Some code here
}