You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

新手求助:Windows Service项目如何应用合适的架构模式

Hey there! Great job getting your Windows Service up and running—even if all the code is in one file right now, having a working service is a solid start. Since MVC relies on a view layer that you don't need here, let's look at architecture patterns that fit headless services perfectly.

推荐:分层架构(关注点分离)

This pattern is perfect for Windows Services because it splits your code into distinct layers, each with a single responsibility—no view layer required. Here's how to map it to your project:

1. 服务宿主层(你的MyNewService.cs)

This layer should only handle Windows Service lifecycle management—think OnStart, OnStop, and timer triggers. It shouldn't contain any business logic, API calls, or logging code. Its only job is to kick off your core work when the timer fires.

2. 业务逻辑层

This is where your core rules live: "fetch data every hour", "validate API responses", "decide what to log". Extract these into dedicated classes (like DataFetchingService) so they're separate from the service's infrastructure code.

3. 外部依赖层

This layer handles interactions with external systems: calling your API, writing to the event log, or any other outside services. Wrap these in interfaces (like IApiClient, IEventLogWriter) so you can easily swap implementations later (or mock them for testing).


简单代码示例

Let's refactor your existing code to fit this structure:

宿主层 (MyNewService.cs)

using System.ServiceProcess;
using System.Timers;

public class MyNewService : ServiceBase
{
    private Timer _hourlyTimer;
    private readonly IDataFetchingService _dataFetchingService;

    // Use dependency injection (DI) for better decoupling (more on this later)
    public MyNewService(IDataFetchingService dataFetchingService)
    {
        _dataFetchingService = dataFetchingService;
    }

    protected override void OnStart(string[] args)
    {
        // Initialize timer to trigger every hour
        _hourlyTimer = new Timer(TimeSpan.FromHours(1).TotalMilliseconds);
        _hourlyTimer.Elapsed += (sender, e) => _dataFetchingService.FetchAndLogData();
        _hourlyTimer.Start();
    }

    protected override void OnStop()
    {
        _hourlyTimer?.Stop();
        _hourlyTimer?.Dispose();
    }
}

业务逻辑层 (DataFetchingService.cs)

public interface IDataFetchingService
{
    void FetchAndLogData();
}

public class DataFetchingService : IDataFetchingService
{
    private readonly IApiClient _apiClient;
    private readonly IEventLogWriter _eventLogWriter;

    public DataFetchingService(IApiClient apiClient, IEventLogWriter eventLogWriter)
    {
        _apiClient = apiClient;
        _eventLogWriter = eventLogWriter;
    }

    public void FetchAndLogData()
    {
        try
        {
            var apiResponse = _apiClient.GetLatestData();
            
            // Add your business validation here
            if (apiResponse.IsValid())
            {
                _eventLogWriter.Write("Successfully fetched data", EventLogEntryType.Information);
            }
            else
            {
                _eventLogWriter.Write("API returned invalid data", EventLogEntryType.Warning);
            }
        }
        catch (Exception ex)
        {
            _eventLogWriter.Write($"Failed to fetch data: {ex.Message}", EventLogEntryType.Error);
        }
    }
}

外部依赖层

API Client (ApiClient.cs)

public interface IApiClient
{
    ApiResponse GetLatestData();
}

public class ApiClient : IApiClient
{
    private readonly HttpClient _httpClient;

    public ApiClient(HttpClient httpClient)
    {
        _httpClient = httpClient;
        _httpClient.BaseAddress = new Uri("https://your-api-endpoint.com/");
    }

    public async Task<ApiResponse> GetLatestData()
    {
        var response = await _httpClient.GetAsync("data");
        response.EnsureSuccessStatusCode();
        return await response.Content.ReadFromJsonAsync<ApiResponse>();
    }
}

Event Log Writer (EventLogWriter.cs)

using System.Diagnostics;

public interface IEventLogWriter
{
    void Write(string message, EventLogEntryType entryType);
}

public class EventLogWriter : IEventLogWriter
{
    private readonly EventLog _eventLog;

    public EventLogWriter()
    {
        _eventLog = new EventLog();
        if (!EventLog.SourceExists("MyNewService"))
        {
            EventLog.CreateEventSource("MyNewService", "Application");
        }
        _eventLog.Source = "MyNewService";
    }

    public void Write(string message, EventLogEntryType entryType)
    {
        _eventLog.WriteEntry(message, entryType);
    }
}

额外建议
  • Use Dependency Injection: Tools like Microsoft.Extensions.DependencyInjection (built into .NET) or Autofac will handle creating instances of your services and injecting dependencies. This makes testing easier—you can mock IApiClient in tests instead of calling a real API.
  • Keep Layers Separate: Never put API calls or logging logic in the service host. If you need to change your API endpoint or switch to a different logging system later, you only have to modify one layer.
  • This aligns with MVC principles: Think of the service host as the "controller" (it triggers actions), the business logic as the "model" (handles core rules), and since there's no UI, you just drop the view layer entirely. The core idea of separation of concerns is the same!

内容的提问来源于stack exchange,提问作者Deathibrylator

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.08 22:13:11