使用するNET Coreサービスモニタアプリケーションの作成


目次
紹介する
背景
データベース#データベース#
.NETコアソリューション
ServiceMonitor.Core
ServiceMonitor.Common
に約束
観察者
ServiceMonitor.WebApi
けいきばん
管理
ServiceMonitor
  • GitHubリポジトリ
  • 紹介する


    サービスモニタアプリケーションの作成方法について説明しますが、これは何ですか?簡単に言えば、ネットワーク内のサービスを監視し、監視結果をデータベースに保存できるアプリケーションです.この例ではSQLサーバです.
    私は多くのツールがこの機能を提供することができることを知っていて、更に良いツールがあって、お金で買うことができて、しかし本文の意図はどのように使うことを展示します.NETコア機能は、開発者がカスタム要件を満たすために拡張できるアプリケーションを構築します.
    基本的な考え方は、ホスト、データベース、APIを無限に実行するプロセスがあります.モニタリング結果をSQL Serverデータベースに保存し、エンドユーザーのために優れたUIを構築し、各サービスのステータスを表示できます.多くのターゲットをモニタリングできますが、すべてではなく特定のサービスを購読できるようにすることが望ましいです.例えば、DBAはAPIではなくデータベースサーバを観察する必要があり、開発者は開発データベースやAPIなどを観察する必要がある.
    開発室に大型ディスプレイを設置し、サービスの状態を観察することも考慮し、グラフを使用することが望ましい.:)
    1つの特殊な機能は、1つの通知サービスが1つ以上のサービスに失敗した場合にすべての管理者にメッセージを送信することであり、この場合、サービスはホスト、データベース、APIなどのターゲットを意味する.
    この文書では、次のサービスを使用して監視します.
    名前
    説明
    ホスト
    Ping既存ホスト
    データベース#データベース#
    既存のデータベースの接続を開いて閉じる
    RESTful API
    既存のAPIからアクションを取得

    背景


    前述したように、既存のターゲット(ホスト、データベース、API)を監視するアプリケーションを作成するので、これらの概念に関する基本的な知識を身につける必要があります.
    ホストはping操作を使用して監視されるため、ネットワークに関連するパッケージを追加して実行します.
    データベースは、統合セキュリティを使用しないで、オープン接続とクローズ接続で監視されます.認証情報を使用してサービスモニタプロセスをシミュレートする必要があるため、この場合は特定のユーザーをデータベースに接続し、ハッカー攻撃を回避することが望ましいです.
    RESTful APIは、RESTクライアントを使用して監視を行い、簡単なJSONに戻る動作を特定します.

    データベース#データベース#


    リポジトリの内部には、関連するデータベース・ファイルを含むResourcesDatabaseというディレクトリがあります.次のファイルを次の順序で実行してください.
    ファイル名
    説明
    00 - Database.sql
    データベース定義
    01 - Tables.sql
    テーブル定義
    02 - Constraints.sql
    コンストレイント(プライマリ・キー、外部キー、一意性)
    03 - Rows.sql
    イニシャルデータ
    ここでは、データベース・スクリプトを見つけることができます.
    表の説明

    説明
    EnvironmentCategory
    環境を含むすべてのカテゴリ:開発、qa、および生産
    ServiceCategory
    サービスを含むすべてのカテゴリ:データベース、rest API、サーバ、URL、Webサービス
    Service
    すべてのサービス定義を含める
    ServiceWatcher
    監視操作を実行するC#エンドのすべてのコンポーネントを含む
    ServiceEnvironment
    サービスと環境の関係が含まれています.たとえば、開発、qa、および生産など、異なる環境で命名されたFinanceServiceサービスを定義できます.
    ServiceEnvironmentStatus
    各環境を含む各サービスのステータス
    ServiceEnvironmentStatusLog
    各サービス環境のステータスの詳細を含む
    Owner
    すべての所有者を表すアプリケーションを含むユーザーのリスト
    ServiceOwner
    サービスと所有者の関係を含める
    User
    視聴サービスを含むすべてのユーザー
    ServiceUser
    サービスとユーザーの関係を含める
    テストを実行するためにローカルコンピュータで実行されているソリューションを使用していることを忘れないでください.リソースディレクトリにはAPIの例がありますが、接続文字列を変更し、コンテキストに基づいてサービスを追加する必要があります.
    また、ServiceEnvironmentテーブルに実際の接続文字列を公開することはお勧めしません.データベースのセキュリティがタスクにならないように、DBAにターゲット・データベースに対してのみオープン接続を実行するように要求してください.特定のユーザーを作成して、データベースとの接続のみを開き、機密情報の漏洩を防止します.

    .NETコアソリューション


    このソリューションのプロジェクトを定義して、プロジェクトの範囲に関する明確な概念を得る必要があります.
    プロジェクト名
    を選択します.
    説明
    ServiceMonitor.Core
    クラスライブラリ
    データベース・ストレージに関連するすべての定義を含める
    ServiceMonitor.Common
    クラスライブラリ
    オブジェクト、シーケンサ、クライアント(REST)など、ServiceMonitorプロジェクトを含む一般的な定義
    ServiceMonitor.WebApi
    Web API
    Web APIコントローラを含み、監視情報の読み取りと書き込みを行う
    ServiceMonitor
    コンソールアプリケーション
    すべてのサービスを監視するプロセスを含む

    ServiceMonitor.Core


    このプロジェクトには、エンティティとデータベース・アクセスのすべての定義が含まれているため、プロジェクトに次のパッケージを追加する必要があります.
    名前

    説明
    Microsoft.EntityFrameworkCore.SqlServer
    最新バージョン
    EF CoreによるSQL Serverへのアクセス
    このプロジェクトには、ビジネスロジック、データベース・アクセス、エンティティの3つの階層があります.
    DashboardServiceクラスコード:
    using System;
    using System.Threading.Tasks;
    using Microsoft.EntityFrameworkCore;
    using Microsoft.Extensions.Logging;
    using ServiceMonitor.Core.BusinessLayer.Contracts;
    using ServiceMonitor.Core.BusinessLayer.Responses;
    using ServiceMonitor.Core.DataLayer;
    using ServiceMonitor.Core.DataLayer.DataContracts;
    using ServiceMonitor.Core.EntityLayer;
    
    namespace ServiceMonitor.Core.BusinessLayer
    {
        public class DashboardService : Service, IDashboardService
        {
            public DashboardService(ILogger logger, ServiceMonitorDbContext dbContext)
                : base(logger, dbContext)
            {
            }
    
            public async Task> GetActiveServiceWatcherItemsAsync()
            {
                Logger?.LogDebug("'{0}' has been invoked", nameof(GetActiveServiceWatcherItemsAsync));
    
                var response = new ListResponse();
    
                try
                {
                    response.Model = await DbContext.GetActiveServiceWatcherItems().ToListAsync();
    
                    Logger?.LogInformation("The service watch items were loaded successfully");
                }
                catch (Exception ex)
                {
                    response.SetError(Logger, nameof(GetActiveServiceWatcherItemsAsync), ex);
                }
    
                return response;
            }
    
            public async Task> GetServiceStatusesAsync(string userName)
            {
                Logger?.LogDebug("'{0}' has been invoked", nameof(GetServiceStatusesAsync));
    
                var response = new ListResponse();
    
                try
                {
                    var user = await DbContext.GetUserAsync(userName);
    
                    if (user == null)
                    {
                        Logger?.LogInformation("There isn't data for user '{0}'", userName);
    
                        return new ListResponse();
                    }
                    else
                    {
                        response.Model = await DbContext.GetServiceStatuses(user).ToListAsync();
    
                        Logger?.LogInformation("The service status details for '{0}' user were loaded successfully", userName);
                    }
                }
                catch (Exception ex)
                {
                    response.SetError(Logger, nameof(GetServiceStatusesAsync), ex);
                }
    
                return response;
            }
    
            public async Task> GetServiceStatusAsync(ServiceEnvironmentStatus entity)
            {
                Logger?.LogDebug("'{0}' has been invoked", nameof(GetServiceStatusAsync));
    
                var response = new SingleResponse();
    
                try
                {
                    response.Model = await DbContext.GetServiceEnvironmentStatusAsync(entity);
                }
                catch (Exception ex)
                {
                    response.SetError(Logger, nameof(GetServiceStatusAsync), ex);
                }
    
                return response;
            }
        }
    }

    ServiceMonitor.Common


    に約束

  • IWatcher
  • IWatchResponse
  • ISerializer
  • IWatcherインタフェースコード:
    using System.Threading.Tasks;
    
    namespace ServiceMonitor.Common.Contracts
    {
        public interface IWatcher
        {
            string ActionName { get; }
    
            Task WatchAsync(WatcherParameter parameter);
        }
    }
    IWatchResponseインタフェースコード:
    namespace ServiceMonitor.Common.Contracts
    {
        public interface IWatchResponse
        {
            bool Success { get; set; }
    
            string Message { get; set; }
    
            string StackTrace { get; set; }
        }
    }
    ISerializerインタフェースコード:
    namespace ServiceMonitor.Common.Contracts
    {
        public interface ISerializer
        {
            string Serialize(T obj);
    
            T Deserialze(string source);
        }
    }

    観察者


    これらは実装です.
  • DatabaseWatcher
  • HttpRequestWatcher
  • PingWatcher
  • DatabaseWatcherクラスコード:
    using System;
    using System.Data.SqlClient;
    using System.Threading.Tasks;
    using ServiceMonitor.Common.Contracts;
    
    namespace ServiceMonitor.Common
    {
        public class DatabaseWatcher : IWatcher
        {
            public string ActionName
                => "OpenDatabaseConnection";
    
            public async Task WatchAsync(WatcherParameter parameter)
            {
                var response = new WatchResponse();
    
                using (var connection = new SqlConnection(parameter.Values["ConnectionString"]))
                {
                    try
                    {
                        await connection.OpenAsync();
    
                        response.Success = true;
                    }
                    catch (Exception ex)
                    {
                        response.Success = false;
                        response.Message = ex.Message;
                        response.StackTrace = ex.ToString();
                    }
                }
    
                return response;
            }
        }
    }
    HttpWebRequestWatcherクラスコード:
    using System;
    using System.Threading.Tasks;
    using ServiceMonitor.Common.Contracts;
    
    namespace ServiceMonitor.Common
    {
        public class HttpRequestWatcher : IWatcher
        {
            public string ActionName
                => "HttpRequest";
    
            public async Task WatchAsync(WatcherParameter parameter)
            {
                var response = new WatchResponse();
    
                try
                {
                    var restClient = new RestClient();
    
                    await restClient.GetAsync(parameter.Values["Url"]);
    
                    response.Success = true;
                }
                catch (Exception ex)
                {
                    response.Success = false;
                    response.Message = ex.Message;
                    response.StackTrace = ex.ToString();
                }
    
                return response;
            }
        }
    }
    PingWatcherクラスコード:
    using System.Net.NetworkInformation;
    using System.Threading.Tasks;
    using ServiceMonitor.Common.Contracts;
    
    namespace ServiceMonitor.Common
    {
        public class PingWatcher : IWatcher
        {
            public string ActionName
                => "Ping";
    
            public async Task WatchAsync(WatcherParameter parameter)
            {
                var ping = new Ping();
    
                var reply = await ping.SendPingAsync(parameter.Values["Address"]);
    
                return new WatchResponse
                {
                    Success = reply.Status == IPStatus.Success ? true : false
                };
            }
        }
    }

    ServiceMonitor.WebApi


    このプロジェクトはサービスモニタのRESTful APIを表しているので、DashboardControllerとAdministrationControllerの2つのコントローラがあります.ダッシュボードには、エンドユーザーの結果に関連するすべてのアクションがあり、保存情報(作成、編集、削除)に関連するすべてのアクションを管理します.

    けいきばん


    DashboardControllerクラスコード:
    using System.Threading.Tasks;
    using Microsoft.AspNetCore.Mvc;
    using Microsoft.Extensions.Logging;
    using ServiceMonitor.Core.BusinessLayer.Contracts;
    using ServiceMonitor.WebApi.Responses;
    
    namespace ServiceMonitor.WebApi.Controllers
    {
    #pragma warning disable CS1591
        [Route("api/v1/[controller]")]
        [ApiController]
        public class DashboardController : ControllerBase
        {
            protected ILogger Logger;
            protected IDashboardService Service;
    
            public DashboardController(ILogger logger, IDashboardService service)
            {
                Logger = logger;
                Service = service;
            }
    #pragma warning restore CS1591
    
            /// 
            /// Gets service watcher items (registered services to watch with service monitor)
            /// 
            /// A sequence of services to watch
            [HttpGet("ServiceWatcherItem")]
            [ProducesResponseType(200)]
            [ProducesResponseType(204)]
            [ProducesResponseType(500)]
            public async Task GetServiceWatcherItemsAsync()
            {
                Logger?.LogDebug("'{0}' has been invoked", nameof(GetServiceWatcherItemsAsync));
    
                var response = await Service.GetActiveServiceWatcherItemsAsync();
    
                return response.ToHttpResponse();
            }
    
            /// 
            /// Gets the details for service watch
            /// 
            /// Service ID
            /// 
            [HttpGet("ServiceStatusDetail/{id}")]
            [ProducesResponseType(200)]
            [ProducesResponseType(204)]
            [ProducesResponseType(500)]
            public async Task GetServiceStatusDetailsAsync(string id)
            {
                Logger?.LogDebug("'{0}' has been invoked", nameof(GetServiceStatusDetailsAsync));
    
                var response = await Service.GetServiceStatusesAsync(id);
    
                return response.ToHttpResponse();
            }
        }
    }

    管理


    AdministrationControlクラスコード:
    using System.Threading.Tasks;
    using Microsoft.AspNetCore.Mvc;
    using Microsoft.Extensions.Logging;
    using ServiceMonitor.Core.BusinessLayer.Contracts;
    using ServiceMonitor.WebApi.Requests;
    using ServiceMonitor.WebApi.Responses;
    
    namespace ServiceMonitor.WebApi.Controllers
    {
    #pragma warning disable CS1591
        [Route("api/v1/[controller]")]
        [ApiController]
        public class AdministrationController : ControllerBase
        {
            protected ILogger Logger;
            protected IAdministrationService Service;
    
            public AdministrationController(ILogger logger, IAdministrationService service)
            {
                Logger = logger;
                Service = service;
            }
    #pragma warning restore CS1591
    
            /// 
            /// Saves a result from service watch action
            /// 
            /// Service status result
            /// Ok if save it was successfully, Not found if service not exists else server internal error
            [HttpPost("ServiceEnvironmentStatusLog")]
            [ProducesResponseType(200)]
            [ProducesResponseType(404)]
            [ProducesResponseType(500)]
            public async Task PostServiceStatusLogAsync([FromBody]ServiceEnvironmentStatusLogRequest request)
            {
                Logger?.LogDebug("'{0}' has been invoked", nameof(PostServiceStatusLogAsync));
    
                var response = await Service
                    .CreateServiceEnvironmentStatusLogAsync(request.ToEntity(), request.ServiceEnvironmentID);
    
                return response.ToHttpResponse();
            }
        }
    }

    ServiceMonitor


    このプロジェクトにはService Monitor Clientのすべてのオブジェクトが含まれています.このプロジェクトではNewtonsoftを追加しました.JsonはJSONシーケンス化のためのパッケージで、ServiceMonitorにあります.CommonにはISerializerという名前のインタフェースがあります.特定のシーケンス化プログラムを強制的に使用したくないので、このレイヤで変更することができます.:)
    ServiceMonitorSerializerクラスコード:
    using Newtonsoft.Json;
    using ServiceMonitor.Common.Contracts;
    
    namespace ServiceMonitor
    {
        public class ServiceMonitorSerializer : ISerializer
        {
            public string Serialize(T obj)
                => JsonConvert.SerializeObject(obj);
    
            public T Deserialze(string source)
                => JsonConvert.DeserializeObject(source);
        }
    }
    次に、MonitorControllerクラスを開始します.このクラスでは、すべての観察操作を実行し、Service Monitor APIのAdministrationControllerですべての結果をデータベースに保存します.
    MonitorControlクラスコード:
    using System;
    using System.Threading;
    using System.Threading.Tasks;
    using Microsoft.Extensions.Logging;
    using ServiceMonitor.Common;
    using ServiceMonitor.Common.Contracts;
    using ServiceMonitor.Models;
    
    namespace ServiceMonitor
    {
        public class MonitorController
        {
            public MonitorController(AppSettings appSettings, ILogger logger, IWatcher watcher, RestClient restClient)
            {
                AppSettings = appSettings;
                Logger = logger;
                Watcher = watcher;
                RestClient = restClient;
            }
    
            public AppSettings AppSettings { get; }
    
            public ILogger Logger { get; }
    
            public IWatcher Watcher { get; }
    
            public RestClient RestClient { get; }
    
            public async Task ProcessAsync(ServiceWatchItem item)
            {
                while (true)
                {
                    try
                    {
                        Logger?.LogTrace("{0} - Watching '{1}' for '{2}' environment", DateTime.Now, item.ServiceName, item.Environment);
    
                        var watchResponse = await Watcher.WatchAsync(new WatcherParameter(item.ToDictionary()));
    
                        if (watchResponse.Success)
                            Logger?.LogInformation(" Success watch for '{0}' in '{1}' environment", item.ServiceName, item.Environment);
                        else
                            Logger?.LogError(" Failed watch for '{0}' in '{1}' environment", item.ServiceName, item.Environment);
    
                        var watchLog = new ServiceStatusLog
                        {
                            ServiceID = item.ServiceID,
                            ServiceEnvironmentID = item.ServiceEnvironmentID,
                            Target = item.ServiceName,
                            ActionName = Watcher.ActionName,
                            Success = watchResponse.Success,
                            Message = watchResponse.Message,
                            StackTrace = watchResponse.StackTrace
                        };
    
                        try
                        {
                            await RestClient.PostJsonAsync(AppSettings.ServiceStatusLogUrl, watchLog);
                        }
                        catch (Exception ex)
                        {
                            Logger?.LogError(" Error on saving watch response ({0}): '{1}'", item.ServiceName, ex.Message);
                        }
                    }
                    catch (Exception ex)
                    {
                        Logger?.LogError(" Error watching service: '{0}': '{1}'", item.ServiceName, ex.Message);
                    }
    
                    Thread.Sleep(item.Interval ?? AppSettings.DelayTime);
                }
            }
        }
    }
    コンソールアプリケーションを実行する前に、次の点を確認してください.
  • ServiceMonitorデータベースは、
  • を使用できます.
  • ServiceMonitorデータベースには、サービスカテゴリ、サービス、サービスオブザーバ、およびユーザーの情報
  • があります.
  • ServiceMonitor APIは、
  • を使用できます.
    url api/v 1/Dashboard/serviceWatcherItemsの戻り値を確認できます.
    {  
      "message":null,
      "didError":false,
      "errorMessage":null,
      "model":[  
        {  
          "serviceID":1,
          "serviceEnvironmentID":1,
          "environment":"Development",
          "serviceName":"Northwind Database",
          "interval":15000,
          "url":null,
          "address":null,
          "connectionString":"server=(local);database=Northwind;user id=johnd;password=SqlServer2017$",
          "typeName":"ServiceMonitor.Common.DatabaseWatcher, ServiceMonitor.Common, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"
        },
        {  
          "serviceID":2,
          "serviceEnvironmentID":3,
          "environment":"Development",
          "serviceName":"DNS",
          "interval":3000,
          "url":null,
          "address":"192.168.1.1",
          "connectionString":null,
          "typeName":"ServiceMonitor.Common.PingWatcher, ServiceMonitor.Common, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"
        },
        {  
          "serviceID":3,
          "serviceEnvironmentID":4,
          "environment":"Development",
          "serviceName":"Sample API",
          "interval":5000,
          "url":"http://localhost:5612/api/values",
          "address":null,
          "connectionString":null,
          "typeName":"ServiceMonitor.Common.HttpWebRequestWatcher, ServiceMonitor.Common, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"
        }
      ]
    }
    ご覧のように、APIはDefaultUserのためにすべてのサービスを返します.1人のユーザーが複数のサービスを購読できるという概念を覚えておいてください.この例では、デフォルトのユーザーはすべてのサービスにバインドされていますが、ServiceUserテーブルでこのリンクを変更することができます.
    プログラムクラスコード:
    using System;
    using System.Threading.Tasks;
    using Microsoft.Extensions.Configuration;
    using Microsoft.Extensions.Logging;
    using ServiceMonitor.Common;
    using ServiceMonitor.Common.Contracts;
    
    namespace ServiceMonitor
    {
        class Program
        {
            private static ILogger logger;
            private static readonly AppSettings appSettings;
    
            static Program()
            {
                logger = LoggingHelper.GetLogger();
    
                var builder = new ConfigurationBuilder().AddJsonFile("appsettings.json");
    
                var configuration = builder.Build();
    
                appSettings = new AppSettings
                {
                    ServiceWatcherItemsUrl = configuration["serviceWatcherItemUrl"],
                    ServiceStatusLogUrl = configuration["serviceStatusLogUrl"],
                    DelayTime = Convert.ToInt32(configuration["delayTime"])
                };
            }
    
            static void Main(string[] args)
            {
                StartAsync(args).GetAwaiter().GetResult();
    
                Console.ReadLine();
            }
    
            static async Task StartAsync(string[] args)
            {
                logger.LogDebug("Starting application...");
    
                var initializer = new ServiceMonitorInitializer(appSettings);
    
                try
                {
                    await initializer.LoadResponseAsync();
                }
                catch (Exception ex)
                {
                    logger.LogError("Error on retrieve watch items: {0}", ex);
                    return;
                }
    
                try
                {
                    initializer.DeserializeResponse();
                }
                catch (Exception ex)
                {
                    logger.LogError("Error on deserializing object: {0}", ex);
                    return;
                }
    
                foreach (var item in initializer.Response.Model)
                {
                    var watcherType = Type.GetType(item.TypeName, true);
    
                    var watcherInstance = Activator.CreateInstance(watcherType) as IWatcher;
    
                    var task = Task.Factory.StartNew(async () =>
                    {
                        var controller = new MonitorController(appSettings, logger, watcherInstance, initializer.RestClient);
    
                        await controller.ProcessAsync(item);
                    });
                }
            }
        }
    }
    以前の側面を確認したら、コンソールアプリケーションに移行し続けます.コンソール出力は次のようになります.
    dbug: ServiceMonitor.Program[0]
          Starting application
    sr trce: ServiceMonitor.Program[0]
          06/20/2017 23:09:30 - Watching 'Sample API' for 'Development' environment
    trce: ServiceMonitor.Program[0]
          06/20/2017 23:09:30 - Watching 'Northwind Database' for 'Development' environment
    trce: ServiceMonitor.Program[0]
          06/20/2017 23:09:30 - Watching 'DNS' for 'Development' environment
    trce: ServiceMonitor.Program[0]
          06/20/2017 23:09:35 - Watching 'DNS' for 'Development' environment
    trce: ServiceMonitor.Program[0]
          06/20/2017 23:09:37 - Watching 'Sample API' for 'Development' environment
    trce: ServiceMonitor.Program[0]
          06/20/2017 23:09:39 - Watching 'DNS' for 'Development' environment
    trce: ServiceMonitor.Program[0]
          06/20/2017 23:09:42 - Watching 'DNS' for 'Development' environment
    trce: ServiceMonitor.Program[0]
          06/20/2017 23:09:43 - Watching 'Sample API' for 'Development' environment
    trce: ServiceMonitor.Program[0]
          06/20/2017 23:09:45 - Watching 'DNS' for 'Development' environment
    trce: ServiceMonitor.Program[0]
          06/20/2017 23:09:47 - Watching 'Northwind Database' for 'Development' environment
    trce: ServiceMonitor.Program[0]
          06/20/2017 23:09:48 - Watching 'Sample API' for 'Development' environment
    trce: ServiceMonitor.Program[0]
          06/20/2017 23:09:48 - Watching 'DNS' for 'Development' environment
    trce: ServiceMonitor.Program[0]
          06/20/2017 23:09:51 - Watching 'DNS' for 'Development' environment
    trce: ServiceMonitor.Program[0]
          06/20/2017 23:09:53 - Watching 'Sample API' for 'Development' environment
    trce: ServiceMonitor.Program[0]
          06/20/2017 23:09:54 - Watching 'DNS' for 'Development' environment
    trce: ServiceMonitor.Program[0]
          06/20/2017 23:09:57 - Watching 'DNS' for 'Development' environment
    データベースに保存されているデータを確認し続けます.ServiceEnvironmentStatusテーブルを確認してください.このような結果が得られます.
    ServiceEnvironmentStatusID ServiceEnvironmentID Success WatchCount  LastWatch
    -------------------------- -------------------- ------- ----------- -----------------------
    1                          4                    1       212         2018-11-22 23:11:34.113
    2                          1                    1       78          2018-11-22 23:11:33.370
    3                          3                    1       366         2018-11-22 23:11:34.620
    
    (3 row(s) affected)
    どうやって一緒に仕事をしていますか?コンソール・アプリケーションは、APIからすべてのサービスを監視し、MonitorControllerで監視項目ごとに無限ループでタスクを開始します.各タスクには、サービス定義で設定された遅延時間がありますが、間隔の定義値がない場合は、AppSettingsから間隔を取ります.したがって、Watch操作が実行されると、結果はAPIを介してデータベースに保存され、このプロセスは自分で繰り返されます.watchで他のタイプの操作を行う場合は、独自のWatcherクラスを作成できます.
     
    原文住所:https://www.codeproject.com/Articles/1165961/Creating-Service-Monitor-Application-with-NET-Core