dotnet webapi miniapi learn note

发布时间 2024-01-07 21:27:12作者: 虎虎生威啊

GameStore.Api/Dtos.cs


using System.ComponentModel.DataAnnotations;

namespace GameStore.Api.Dtos;


public record GameDto(int Id,
                      string Name,
                      string Genre,
                      decimal Price,
                      DateTime ReleaseDate,
                      string ImageUri);
public record CreateGameDto([Required][StringLength(50)] string Name,
                            [Required][StringLength(20)] string Genre,
                            [Range(1, 100)] decimal Price,
                            DateTime ReleaseDate,
                            [Url][StringLength(100)] string ImageUri);



public record UpdateGameDto([Required][StringLength(50)] string Name,
                            [Required][StringLength(20)] string Genre,
                            [Range(1, 100)] decimal Price,
                            DateTime ReleaseDate,
                            [Url][StringLength(100)] string ImageUri);





GameStore.Api/GameStore.Api.csproj

<Project Sdk="Microsoft.NET.Sdk.Web">

  <PropertyGroup>
    <TargetFramework>net8.0</TargetFramework>
    <Nullable>enable</Nullable>
    <ImplicitUsings>enable</ImplicitUsings>
    <UserSecretsId>eff6d453-87a0-41c0-b286-ceb7a9aebd3c</UserSecretsId>
  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="8.0.0">
      <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
      <PrivateAssets>all</PrivateAssets>
    </PackageReference>
    <PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="8.0.0" />
    <PackageReference Include="MinimalApis.Extensions" Version="0.11.0" />
  </ItemGroup>

</Project>


GameStore.Api/appsettings.json

{
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft.AspNetCore": "Warning"
    }
  },
  "AllowedHosts": "*",
  "ConnectionStrings": {
    "DefaultConnection": "Data Source=/Users/song/Code/dotnet_webapi_2/GameStore.Api/Dbs/Games.db"
  }
}

GameStore.Api/appsettings.Development.json

{
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft.AspNetCore": "Warning"
    }
  }
}

GameStore.Api/Program.cs

using GameStore.Api.Data;
using GameStore.Api.Endpoints;
using GameStore.Api.Repositoriesa;
using Microsoft.EntityFrameworkCore;

var builder = WebApplication.CreateBuilder(args);
// builder.Services.AddScoped<IGamesRepository, InMemGamesRepository>();
// builder.Services.AddSingleton<IGamesRepository, InMemGamesRepository>();
builder.Services.AddScoped<IGamesRepository, EntityFrameworkGamesRepository>();

builder.Services.AddRepositories(builder.Configuration);

var app = builder.Build();

app.Services.InitalizeDb();

app.MapGet("/", () => "Hello World!");
// 
app.MapGamesEndpoints();

app.Run();



















GameStore.Api/Endpoints/GamesEndpoints.cs

using System.Diagnostics.CodeAnalysis;
using System.Reflection.Metadata;
using GameStore.Api.Dtos;
using GameStore.Api.Entities;
using GameStore.Api.Repositoriesa;

namespace GameStore.Api.Endpoints;

public static class GamesEndpoints
{

    const string GetGameEndpointName = "GetGame";
    // public static RouteHandlerBuilder MapGet(this IEndpointRouteBuilder endpoints, [StringSyntax("Route")] string pattern, Delegate handler);
    // public static RouteGroupBuilder MapGroup(this IEndpointRouteBuilder endpoints, [StringSyntax("Route")] string prefix);

    // 扩展 IEndpointRouteBuilder 接口 
    public static RouteGroupBuilder MapGamesEndpoints(this IEndpointRouteBuilder endpoints)
    {

        var group = endpoints.MapGroup("/games").WithParameterValidation();

        group.MapGet("/", (IGamesRepository repository) => repository.GetAll().Select(game => game.AsDto()));

        group.MapGet("/{id}", (IGamesRepository repository, int id) =>
        {
            Game? game = repository.Get(id);
            if (game is null)
            {
                return Results.NotFound();
            }
            return Results.Ok(game.AsDto());
        }).WithName(GetGameEndpointName);

        group.MapPost("/", (IGamesRepository repository, CreateGameDto gameDto) =>
        {

            Game game = new()
            {
                Name = gameDto.Name,
                Genre = gameDto.Genre,
                Price = gameDto.Price,
                ReleaseDate = gameDto.ReleaseDate,
                ImageUri = gameDto.ImageUri
            };
            repository.Create(game);

            return Results.CreatedAtRoute(GetGameEndpointName, new { id = game.Id }, game);
        });



        group.MapPut("/{id}", (IGamesRepository repository, int id, UpdateGameDto updatedGame) =>
        {

            Game? existingGame = repository.Get(id);
            if (existingGame is null)
            {
                return Results.NotFound();
            }
            existingGame.Name = updatedGame.Name;
            existingGame.Genre = updatedGame.Genre;
            existingGame.Price = updatedGame.Price;
            existingGame.ReleaseDate = updatedGame.ReleaseDate;
            existingGame.ImageUri = updatedGame.ImageUri;

            repository.Update(existingGame);

            return Results.NoContent();
        });

        group.MapDelete("/{id}", (IGamesRepository repository, int id) =>
        {
            Game? game = repository.Get(id);
            if (game is not null)
            {
                repository.Delete(id);
            }
            return Results.NoContent();
        });


        return group;

    }

}

GameStore.Api/Repositories/EntityFrameworkGamesRepository.cs


using GameStore.Api.Data;
using GameStore.Api.Entities;
using Microsoft.EntityFrameworkCore;

namespace GameStore.Api.Repositoriesa;


public class EntityFrameworkGamesRepository : IGamesRepository
{
    private readonly GameStoreContext dbContext;
    public EntityFrameworkGamesRepository(GameStoreContext dbContext)
    {
        this.dbContext = dbContext;
    }

    public void Create(Game game)
    {
        dbContext.Games.Add(game);
        dbContext.SaveChanges();
    }

    public void Delete(int id)
    {
        dbContext.Games.Where(game => game.Id == id).ExecuteDelete();
    }

    public Game? Get(int id)
    {

        return dbContext.Games.Find(id);
    }

    public IEnumerable<Game> GetAll()
    {

        return dbContext.Games.AsNoTracking().ToList();
    }

    public void Update(Game updatedGame)
    {
        dbContext.Games.Update(updatedGame);
        dbContext.SaveChanges();
    }
}

GameStore.Api/Repositories/IGamesRepository.cs


using GameStore.Api.Entities;

namespace GameStore.Api.Repositoriesa;

public interface IGamesRepository
{

    IEnumerable<Game> GetAll();
    Game? Get(int id);
    void Create(Game game);
    void Update(Game updatedGame);
    void Delete(int id);
}


GameStore.Api/Repositories/InMemGamesRepository.cs


using GameStore.Api.Entities;

namespace GameStore.Api.Repositoriesa;


public class InMemGamesRepository : IGamesRepository
{

    private readonly List<Game> games = new()
    {
        new Game(){
            Id = 1,
            Name="Street Fighter II",
            Genre ="Fighting",
            Price = 19.99M,
            ReleaseDate = new DateTime(1991,2,1),
            ImageUri = "https://palcehold.co/100"
        },
        new Game(){
            Id = 2,
            Name="FIFA 23",
            Genre ="Sports",
            Price = 29.99M,
            ReleaseDate = new DateTime(2022,2,1),
            ImageUri = "https://palcehold.co/100"
        },

};

    public IEnumerable<Game> GetAll()
    {
        return games;
    }
    public Game? Get(int id)
    {
        return games.Find(game => game.Id == id);
    }
    public void Create(Game game)
    {
        game.Id = games.Max(game => game.Id) + 1;
        games.Add(game);
    }
    public void Update(Game updatedGame)
    {
        var index = games.FindIndex(game => game.Id == updatedGame.Id);
        games[index] = updatedGame;
    }
    public void Delete(int id)
    {
        var index = games.FindIndex(game => game.Id == id);
        games.RemoveAt(index);
    }


}

GameStore.Api/Data/GameStoreContext.cs

using System.Reflection;
using GameStore.Api.Entities;
using Microsoft.EntityFrameworkCore;

namespace GameStore.Api.Data;
public class GameStoreContext : DbContext
{
    public GameStoreContext(DbContextOptions<GameStoreContext> options) : base(options)
    {
    }
    public DbSet<Game> Games => Set<Game>();

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {

        modelBuilder.ApplyConfigurationsFromAssembly(Assembly.GetExecutingAssembly());

    }
}

GameStore.Api/Data/DataExtensions.cs


using Microsoft.EntityFrameworkCore;

namespace GameStore.Api.Data;

public static class DataExtensions
{

    // 扩展 IServiceProvider接口的方法
    public static void InitalizeDb(this IServiceProvider provider)
    {
        using var scope = provider.CreateScope();
        var dbContext = scope.ServiceProvider.GetRequiredService<GameStoreContext>();
        dbContext.Database.Migrate();
    }
    // 扩展IServiceCollection接口的方法
    public static IServiceCollection AddRepositories(this IServiceCollection services, IConfiguration configuration)
    {
        // config sqlite
        services.AddSqlite<GameStoreContext>(configuration.GetConnectionString("DefaultConnection"));
        return services;
    }
}

GameStore.Api/Data/Configurations/GameConfigurations.cs


using GameStore.Api.Entities;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders;

namespace GameStore.Api.Data.Configurationsa;

public class GameConfiguration : IEntityTypeConfiguration<Game>
{

    public void Configure(EntityTypeBuilder<Game> builder)
    {
        builder.Property(game => game.Price).HasPrecision(5, 2);
    }
}

GameStore.Api/Entities/Game.cs

using System.ComponentModel.DataAnnotations;

namespace GameStore.Api.Entities;

public class Game
{
    public int Id { get; set; }
    [Required]
    [StringLength(50)]
    public required string Name { get; set; }
    [Required]
    [StringLength(20)]
    public required string Genre { get; set; }
    [Range(1,100)]
    public decimal Price { get; set; }
    public DateTime ReleaseDate { get; set; }
    public required string ImageUri { get; set; }
    
}

GameStore.Api/Entities/EntityExtensions.cs

using GameStore.Api.Dtos;

namespace GameStore.Api.Entities;

public static class EntityExtensions
{
    public static GameDto AsDto(this Game game)
    {
        return new GameDto(game.Id,
                           game.Name,
                           game.Genre,
                           game.Price + 10,
                           game.ReleaseDate,
                           game.ImageUri);

    }
}