有什么方法可以在 ASP.NET Web API REST 服务中执行一次代码?

Is there any way to execute code once in a ASP.NET Web API REST service?

我有一个 ASP.NET Web API REST 服务,我想在第一次启动服务时执行一些代码,而不是每次 web api 方法requested/invoked 来自我的 ASP.NET MVC 应用程序。

我想这样做是因为我想初始化一个 EventLog,然后用它在 windows 事件查看器中创建条目。

有什么简单的方法吗?

更新: 正如 Jonhatan 在他的回答中所建议的那样,我在 global.asax.cs:

中创建了一个方法

Global.asax.cs:

namespace MyWebAPIApp
{
    public class WebApiApplication : System.Web.HttpApplication
    {
        public MyLog _myLog;

        protected void Application_Start()
        {
             // Here some stuff

             SetupEventLogging();
        }

        private void SetupEventLogging()
        {
            if (!EventLog.SourceExists("MyWebApiLog"))
            {
                EventLog.CreateEventSource("MyWebApiLog", "MyWebApiLogLog");
            }

            EventLog eventLog = new EventLog();
            eventLog.Source = "MyWebApiLog";
            eventLog.Log = "MyWebApiLog";
           
            _myLog = new MyLog(eventLog, "MyWebApiService");
        }
    }
}

控制器:

namespace MyWebAPIApp.Controllers
{
    public class MyController : ApiController
    {
        public void GetAll()
        {
            _myLog.Success("All records read");
        }
    }
}

但是现在如果我创建一个全局变量 _myLog,我如何从我的控制器中的所有方法访问这个变量以执行 _myLog.Error(...) 或 _myLog.Success(。 ..)?

您通常会在 global.asax.cs:

ApplicationStart 方法中执行此操作
    protected void Application_Start()
    {
        AreaRegistration.RegisterAllAreas();
        GlobalConfiguration.Configure(WebApiConfig.Register);
        FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
        RouteConfig.RegisterRoutes(RouteTable.Routes);
        BundleConfig.RegisterBundles(BundleTable.Bundles);

        SetupLogging(); // do something in here / wire up your flavour of logging
    }

通常,模式是:

  1. 在应用程序启动时设置您的日志记录 - 这是您设置数据库连接以存储日志等的地方
  2. 每当您想写入日志时,在整个代码中调用静态 logger.Write 方法。

我使用 Microsoft.Practices.EnterpriseLibrary.Logging,但我认为 Serilog 或 Log4Net 现在可能是两个更常见的框架。

因此,在我的 global.asax.cs 中,SetupLogging() 方法是:

    private static void SetupLogging()
    {

        var configurationSource = ConfigurationSourceFactory.Create();
        DatabaseFactory.SetDatabaseProviderFactory(new DatabaseProviderFactory(configurationSource));

        var logWriterFactory = new LogWriterFactory(configurationSource);
        Logger.SetLogWriter(logWriterFactory.Create());

        var daysToKeepLogsInDb = int.Parse(ConfigurationManager.AppSettings["DaysToKeepLogsInDb"]);
        CustomLogger.PurgeLogs(daysToKeepLogsInDb); // only keep last 90 etc days of event logging in the db

        CustomLogger.Write("Application Starting", TraceEventType.Information);
    }

基本上只是框架需要的东西 'get going',以及一些自定义清理。然后我有一个 CustomLogger class 来帮助按照我想要的方式编写条目,运行 一个自定义存储过程来清理旧日志,等等:

 using Microsoft.Practices.EnterpriseLibrary.Logging;
 using System;
 using System.Collections.Generic;
 using System.Configuration;
 using System.Data;
 using System.Data.SqlClient;
 using System.Diagnostics;
 
 namespace MyApplication.Helpers
 {
     public class CustomLogger
     {
         private static readonly ICollection<string> EmptyCategoriesList = new List<string>(0);
         private const string LogTitle = "MyApplication Name";
 
         public static void Write(object message)
         {
             Write(message, TraceEventType.Error);
         }
 
         public static void Write(object message, TraceEventType severity)
         {
             Logger.Write(message, EmptyCategoriesList, -1, 1, severity, LogTitle);
         }
 
         public static void PurgeLogs(int keepLastXDays)
         {
 
             var connectionString = ConfigurationManager.ConnectionStrings["MyLoggingConnectionString"].ConnectionString;
 
             using (var con = new SqlConnection(connectionString))
             {
                 using (var command = new SqlCommand("PurgeLogs", con)) // custom stored procedure
                 {
                     var dateTo = DateTime.Now.AddDays(keepLastXDays * -1);
 
                     command.CommandType = CommandType.StoredProcedure;
                     command.Parameters.Add(new SqlParameter("@dateTo", dateTo));
                     command.Parameters.Add(new SqlParameter("@title", LogTitle));
 
                     con.Open();
                     command.ExecuteNonQuery();
                     con.Close(); // technically not required because in using, but leaving in case this block gets copy-pasted out of here
                 }
             }
         }
     }
 }

然后,在我的代码(控制器、帮助程序等)中,我通过自定义记录器中的静态方法写入日志:

    public static void EndSession(Session session)
    {
        try
        {
            Logon.DoLogoff(session);
        }
        catch (Exception exception)
        {
            CustomLogger.Write(exception);
            throw new Exception("Error ending session.");
        }
    }

如果您使用依赖注入来执行此操作,它将(特别是)允许您更轻松地更换日志框架,并允许您更轻松地进行单元测试。但是你必须在你的应用程序和记录器之间创建另一个 'layer' 来抽象出更多的关系。您应该阅读依赖注入,因为它通常值得使用。

But now if I create a global variable _myLog, how can I access this variable from all the methods in my Controller in order to do _myLog.Error(...) or _myLog.Success(...)?

_myLog 设为静态并引用它 WebApiApplication._myLog 其中 WebApiApplication 是在 global.asax.cs.

中定义的应用程序 class

我宁愿用 MyLog static 属性:

创建一些静态 class
public static class LogManager
{
    public static MyLog Logger;
}

并在 global.asax.csSetupEventLogging() 中放入

LogManager.Logger = new MyLog(eventLog, "MyWebApiService");