引入redis服务,支持自动化单元测试
大石头 编写于 2022-03-31 22:56:30
X
using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using NewLife.Log;

namespace NewLife.Model
{
    /// <summary>轻量级主机服务</summary>
    /// <remarks>
    /// 文档 https://www.yuque.com/smartstone/nx/host
    /// </remarks>
    public interface IHostedService
    {
        /// <summary>开始服务</summary>
        /// <param name="cancellationToken"></param>
        /// <returns></returns>
        Task StartAsync(CancellationToken cancellationToken);

        /// <summary>停止服务</summary>
        /// <param name="cancellationToken"></param>
        /// <returns></returns>
        Task StopAsync(CancellationToken cancellationToken);
    }

    /// <summary>轻量级应用主机</summary>
    /// <remarks>
    /// 文档 https://www.yuque.com/smartstone/nx/host
    /// </remarks>
    public interface IHost
    {
        /// <summary>添加服务</summary>
        /// <param name="service"></param>
        void Add(IHostedService service);

        /// <summary>添加服务</summary>
        /// <typeparam name="TService"></typeparam>
        void Add<TService>() where TService : IHostedService;

        /// <summary>同步运行,大循环阻塞</summary>
        void Run();

        /// <summary>异步允许,大循环阻塞</summary>
        /// <returns></returns>
        Task RunAsync();
    }

    /// <summary>轻量级应用主机</summary>
    /// <remarks>
    /// 文档 https://www.yuque.com/smartstone/nx/host
    /// </remarks>
    public class Host : IHost
    {
        #region 属性
        /// <summary>服务提供者</summary>
        public IServiceProvider ServiceProvider { get; set; }

        private readonly IList<Type> _serviceTypes = new List<Type>();
        /// <summary>服务集合</summary>
        public IList<IHostedService> Services { get; } = new List<IHostedService>();
        #endregion

        #region 构造
        /// <summary>通过制定服务提供者来实例化一个应用主机</summary>
        /// <param name="serviceProvider"></param>
        public Host(IServiceProvider serviceProvider) => ServiceProvider = serviceProvider;
        #endregion

        #region 服务集合
        /// <summary>添加服务</summary>
        /// <typeparam name="TService"></typeparam>
        public void Add<TService>() where TService : IHostedService
        {
            var type = typeof(TService);
            _serviceTypes.Add(type);

            var ioc = (ServiceProvider as ServiceProvider)?.Container ?? ObjectContainer.Current;
            ioc.TryAddTransient(type, type);
        }

        /// <summary>添加服务</summary>
        /// <param name="service"></param>
        public void Add(IHostedService service) => Services.Add(service);
        #endregion

        #region 开始停止
        /// <summary>开始</summary>
        /// <param name="cancellationToken"></param>
        /// <returns></returns>
        public async Task StartAsync(CancellationToken cancellationToken)
        {
            foreach (var item in _serviceTypes)
            {
                if (ServiceProvider.GetService(item) is IHostedService service) Services.Add(service);
            }
            foreach (var item in Services)
            {
                await item.StartAsync(cancellationToken).ConfigureAwait(false);
            }
        }

        /// <summary>停止</summary>
        /// <param name="cancellationToken"></param>
        /// <returns></returns>
        public async Task StopAsync(CancellationToken cancellationToken)
        {
            foreach (var item in Services)
            {
                try
                {
                    await item.StopAsync(cancellationToken).ConfigureAwait(false);
                }
                catch (Exception ex)
                {
                    XTrace.WriteException(ex);
                }
            }
        }
        #endregion

        #region 运行大循环
        private TaskCompletionSource<Object> _life;
        /// <summary>同步运行,大循环阻塞</summary>
        public void Run() => RunAsync().GetAwaiter().GetResult();

        /// <summary>异步允许,大循环阻塞</summary>
        /// <returns></returns>
        public async Task RunAsync()
        {
            XTrace.WriteLine("Starting......");

            using var source = new CancellationTokenSource();

            _life = new TaskCompletionSource<Object>();

            AppDomain.CurrentDomain.ProcessExit += (s, e) => _life.TrySetResult(null);
            Console.CancelKeyPress += (s, e) => _life.TrySetResult(null);

            await StartAsync(source.Token);
            XTrace.WriteLine("Application started. Press Ctrl+C to shut down.");

            await _life.Task;

            XTrace.WriteLine("Application is shutting down...");

            await StopAsync(source.Token);

            XTrace.WriteLine("Stopped!");
        }
        #endregion
    }

    /// <summary>后台任务</summary>
    /// <remarks>
    /// 文档 https://www.yuque.com/smartstone/nx/host
    /// </remarks>
    public abstract class BackgroundService : IHostedService, IDisposable
    {
        private Task _executingTask;

        private CancellationTokenSource _stoppingCts;

        /// <summary>执行</summary>
        /// <param name="stoppingToken"></param>
        /// <returns></returns>
        protected abstract Task ExecuteAsync(CancellationToken stoppingToken);

        /// <summary>开始</summary>
        /// <param name="cancellationToken"></param>
        /// <returns></returns>
        public virtual Task StartAsync(CancellationToken cancellationToken)
        {
            _stoppingCts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken);
            _executingTask = ExecuteAsync(_stoppingCts.Token);
            if (_executingTask.IsCompleted)
            {
                return _executingTask;
            }
            return Task.CompletedTask;
        }

        /// <summary>停止</summary>
        /// <param name="cancellationToken"></param>
        /// <returns></returns>
        public virtual async Task StopAsync(CancellationToken cancellationToken)
        {
            if (_executingTask != null)
            {
                try
                {
                    _stoppingCts.Cancel();
                }
                finally
                {
                    await Task.WhenAny(_executingTask, Task.Delay(-1, cancellationToken)).ConfigureAwait(continueOnCapturedContext: false);
                }
            }
        }

        /// <summary>销毁</summary>
        public virtual void Dispose() => _stoppingCts?.Cancel();
    }
}