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

C#进阶:项目中Service类与Repository类的应用方法问询

Hey there! Let's break this down clearly—since you're diving into loose coupling and dependency injection (DI) in C#, understanding the divide between Service and Repository classes is key to keeping your code clean, maintainable, and scalable.


Core Responsibilities: What Each Class Does

First, let's nail down the distinct roles of these two types of classes—mixing them up is a common pitfall, so clarity here goes a long way.

Repository Classes

Repos are your data access layer—they’re the only part of your code that should talk directly to databases, file systems, APIs, or any other data store. Their sole job is to encapsulate CRUD (Create, Read, Update, Delete) operations for a specific entity (like your Player class).

  • They should never contain business logic—only logic related to fetching, saving, or manipulating data.
  • Example: A PlayerRepository might have methods like GetPlayerById(int id), AddPlayer(Player player), or CheckIfUsernameExists(string username).
  • Always implement an interface (e.g., IPlayerRepository)—this lets you swap data stores later (say, from SQL to MongoDB) without touching any other code, which is perfect for loose coupling.

Service Classes

Services are where your business logic lives. They use repositories to pull the data they need, then apply rules, transformations, or orchestration to that data to fulfill business requirements.

  • For example, a PlayerService might have a RegisterNewPlayer method that:
    1. Uses PlayerRepository to check if the username/email is already taken
    2. Hashes the user's password (using a separate password-hashing service)
    3. Creates a valid Player object with default values (like a creation timestamp)
    4. Calls PlayerRepository.AddPlayer() to save the new user
    5. Triggers a welcome email (via another service, if you’re splitting responsibilities further)
  • Services can also coordinate multiple repositories—like updating a player’s stats and logging the change in an AuditRepository.
  • Just like repos, services should implement interfaces (e.g., IPlayerService) to enable mocking for testing and easy implementation swaps.

Putting It All Together with Dependency Injection

Since you’re focused on loose coupling, DI is how you wire these layers together without hardcoding dependencies. Here’s a concrete C# example:

1. Define Interfaces (Critical for Loose Coupling)

// Repository interface
public interface IPlayerRepository
{
    Player GetById(int id);
    void Add(Player player);
    bool UsernameExists(string username);
}

// Service interface
public interface IPlayerService
{
    bool RegisterPlayer(string username, string email, string password);
}

2. Implement the Repository (SQL Example)

public class PlayerRepository : IPlayerRepository
{
    private readonly MyDbContext _dbContext;

    // Inject DbContext via constructor (DI handles this)
    public PlayerRepository(MyDbContext dbContext)
    {
        _dbContext = dbContext;
    }

    public Player GetById(int id)
    {
        return _dbContext.Players.Find(id);
    }

    public void Add(Player player)
    {
        _dbContext.Players.Add(player);
        _dbContext.SaveChanges();
    }

    public bool UsernameExists(string username)
    {
        return _dbContext.Players.Any(p => p.Username == username);
    }
}

3. Implement the Service (Inject the Repository)

public class PlayerService : IPlayerService
{
    private readonly IPlayerRepository _playerRepository;
    private readonly IPasswordHasher _passwordHasher; // Another injected dependency

    // Inject the repository interface (not the concrete class!)
    public PlayerService(IPlayerRepository playerRepository, IPasswordHasher passwordHasher)
    {
        _playerRepository = playerRepository;
        _passwordHasher = passwordHasher;
    }

    public bool RegisterPlayer(string username, string email, string password)
    {
        // Business rule: Username must be unique
        if (_playerRepository.UsernameExists(username))
            return false;

        // Apply business logic (hash password, set defaults)
        var newPlayer = new Player
        {
            Username = username,
            Email = email,
            PasswordHash = _passwordHasher.Hash(password),
            CreatedAt = DateTime.UtcNow
        };

        // Delegate data saving to the repository
        _playerRepository.Add(newPlayer);
        return true;
    }
}

4. Register Dependencies in Your DI Container (ASP.NET Core Example)

In Program.cs, tell the DI system which implementations to use for each interface:

builder.Services.AddDbContext<MyDbContext>(options => 
    options.UseSqlServer(builder.Configuration.GetConnectionString("Default")));
builder.Services.AddScoped<IPlayerRepository, PlayerRepository>();
builder.Services.AddScoped<IPlayerService, PlayerService>();
builder.Services.AddScoped<IPasswordHasher, PasswordHasher>();

5. Use the Service in Your Controller/UI Layer

public class PlayerController : ControllerBase
{
    private readonly IPlayerService _playerService;

    // Inject the service interface
    public PlayerController(IPlayerService playerService)
    {
        _playerService = playerService;
    }

    [HttpPost("register")]
    public IActionResult Register([FromBody] RegisterRequest request)
    {
        bool success = _playerService.RegisterPlayer(request.Username, request.Email, request.Password);
        return success ? Ok("Player registered!") : BadRequest("Username already exists.");
    }
}

Key Best Practices to Follow

  • Keep repos focused: One repo per entity (or aggregate root)—don’t make a single "AllInOneRepository" that handles every table.
  • Keep services cohesive: A service should handle a specific business domain (e.g., PlayerService for player logic, MatchService for match logic) instead of a single "GodService" that does everything.
  • Depend on interfaces, not concrete classes: This is the backbone of loose coupling—you can mock IPlayerRepository in unit tests for PlayerService without hitting a real database.
  • No business logic in repos: Repos are for data access only. Keeping business rules in services ensures they’re centralized and easy to modify.

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.21 08:13:30