Skip to content

Shuhan Jin-homework with adult step commit #5

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 9 commits into
base: main
Choose a base branch
from
81 changes: 81 additions & 0 deletions ParkingLotApi/Controllers/ParkingLotsController.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
using Microsoft.AspNetCore.Mvc;
using ParkingLotApi.Dtos;
using ParkingLotApi.Exceptions;
using ParkingLotApi.Models;
using ParkingLotApi.Services;

namespace ParkingLotApi.Controllers
{
[ApiController]
[Route("[controller]")] //Ĭ��ParkingLotsController��controllerǰ����ַ���·��
public class ParkingLotsController : ControllerBase
{
private readonly ParkingLotsService _parkingLotsService;
public ParkingLotsController(ParkingLotsService parkingLotsService) //��ParkingLotsServiceע�빹�캯��
{
_parkingLotsService = parkingLotsService;
}

[HttpPost]
public async Task<ActionResult<ParkingLotDto>> AddParkingLotAsync([FromBody] ParkingLotDto parkingLotDto)
{
//try

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion: remove dead code

//{
return StatusCode(StatusCodes.Status201Created, await _parkingLotsService.AddAsync(parkingLotDto));
//}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggestion:
Remove unused comment

//catch (InvalidCapacityException ex)
//{
// return BadRequest();
//}

//if (parkingLotDto.Capacity < 10)
//{
// return BadRequest();
//}
// return null;
}

[HttpDelete]
public async Task<ActionResult> DeleteParkingLotAsync(string id)
{
await _parkingLotsService.DeleteAsync(id);
return NoContent();
}

[HttpGet]
public async Task<ActionResult<List<ParkingLot>>> GetOnePageAsync([FromQuery] int? pageIndex)
{
if (pageIndex == null)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good job to check the sad path

{
pageIndex = 1;
return Ok(await _parkingLotsService.GetOnePageAsync((int)pageIndex));
}
else

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion: else can be removed, since there is no other branch

{
return Ok(await _parkingLotsService.GetOnePageAsync((int)pageIndex));
}
}

[HttpGet("{id}")]
public async Task<ActionResult> GetParkingLotByIdAsync(string id)
{
var parkingLot = await _parkingLotsService.GetByIdAsync(id);
if (parkingLot == null)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion: avoid return null from method, since it's hard to understand the meaning

{
return NotFound();
}
return Ok(parkingLot);
}

[HttpPut("{id}")]
public async Task<ActionResult> GetParkingLotByIdAsync(string id, int capacity)
{
var updatedParkingLot = await _parkingLotsService.UpdateParkingLotCapacityAsync(capacity, id);
if (updatedParkingLot == null)
{
return NotFound();
}
return Ok(updatedParkingLot);
}
}
}
20 changes: 20 additions & 0 deletions ParkingLotApi/Dtos/ParkingLotDto.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
using ParkingLotApi.Models;

namespace ParkingLotApi.Dtos;

public class ParkingLotDto
{
public string Name { get; set; } //ctrl+d自动向下复制一行
public int Capacity { get; set; }
public string Location { get; set; }

internal ParkingLot ToEntity()
{
return new ParkingLot()
{
Name = Name,
Capacity = Capacity,
Location = Location
};
}
}
7 changes: 7 additions & 0 deletions ParkingLotApi/Exceptions/InvalidCapacityException.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
namespace ParkingLotApi.Exceptions
{
public class InvalidCapacityException : Exception
{

}
}
24 changes: 24 additions & 0 deletions ParkingLotApi/Filters/InvalidCapacityExceptionFilter.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Filters;
using ParkingLotApi.Exceptions;

namespace ParkingLotApi.Filters
{
public class InvalidCapacityExceptionFilter : IActionFilter, IOrderedFilter
{
int IOrderedFilter.Order => int.MaxValue - 10;

void IActionFilter.OnActionExecuted(ActionExecutedContext context) //excute when controller method return
{
if (context.Exception is InvalidCapacityException invalidCapacityException)
{
context.Result = new BadRequestResult();
context.ExceptionHandled = true;
}
}

void IActionFilter.OnActionExecuting(ActionExecutingContext context) //excute before controller method run
{
}
}
}
16 changes: 16 additions & 0 deletions ParkingLotApi/Models/ParkingLot.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
using MongoDB.Bson;
using MongoDB.Bson.Serialization.Attributes;

namespace ParkingLotApi.Models
{
public class ParkingLot
{
[BsonId]
[BsonRepresentation(BsonType.ObjectId)]
public string? Id { get; set; }

public string Name { get; set; }
public int Capacity { get; set; }
public string Location { get; set; }
}
}
9 changes: 9 additions & 0 deletions ParkingLotApi/Models/ParkingLotDatabaseSettings.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
namespace ParkingLotApi.Models
{
public class ParkingLotDatabaseSettings
{
public string ConnectionString { get; set; } = null!;
public string DatabaseName { get; set; } = null!;
public string CollectionName { get; set; } = null!;
}
}
2 changes: 2 additions & 0 deletions ParkingLotApi/ParkingLotApi.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@

<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="7.0.13" />
<PackageReference Include="MongoDB.Bson" Version="2.22.0" />
<PackageReference Include="MongoDB.Driver" Version="2.22.0" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.5.0" />
</ItemGroup>

Expand Down
21 changes: 18 additions & 3 deletions ParkingLotApi/Program.cs
Original file line number Diff line number Diff line change
@@ -1,16 +1,29 @@
using ParkingLotApi.Exceptions;
using ParkingLotApi.Filters;
using ParkingLotApi.Models;
using ParkingLotApi.Repository;
using ParkingLotApi.Services;

var builder = WebApplication.CreateBuilder(args);

// Add services to the container.

builder.Services.AddControllers();
builder.Services.AddControllers(options =>
{
options.Filters.Add<InvalidCapacityExceptionFilter>(); //inject filter in program
});
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
builder.Services.AddScoped<ParkingLotsService>();
builder.Services.AddSingleton<IParkingLotsRepository, ParkingLotsRepository>(); //singleton bc ϣ��ֻ��һ������
builder.Services.Configure<ParkingLotDatabaseSettings>
(builder.Configuration.GetSection("ParkingLotStoreDatabase")); //ParkingLotStoreDatabase is defined in appsettings.json

var app = builder.Build();

// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
if (app.Environment.IsDevelopment()) //only open swagger under development enviroment
{
app.UseSwagger();
app.UseSwaggerUI();
Expand All @@ -22,4 +35,6 @@

app.MapControllers();

app.Run();
app.Run();

public partial class Program { } //or add StartUp.cs
13 changes: 13 additions & 0 deletions ParkingLotApi/Repository/IParkingLotsRepository.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
using ParkingLotApi.Models;

namespace ParkingLotApi.Repository
{
public interface IParkingLotsRepository
{
Task<ParkingLot> CreateParkingLot(ParkingLot parkingLot);
Task DeleteParkingLot(string ParkingLotId);
Task<List<ParkingLot>> GetAllParkingLots();
Task<ParkingLot> GetById(string ParkingLotId);
Task<ParkingLot> UpdateParkingLot(string ParkingLotId, ParkingLot updatedParkingLot);
}
}
49 changes: 49 additions & 0 deletions ParkingLotApi/Repository/ParkingLotsRepository.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
using Microsoft.Extensions.Options;
using MongoDB.Driver;
using ParkingLotApi.Models;

namespace ParkingLotApi.Repository
{
public class ParkingLotsRepository : IParkingLotsRepository //alt+enter implement members in interface

{
private readonly IMongoCollection<ParkingLot> _parkingLotCollection; //连mongodb数据库

public ParkingLotsRepository(IOptions<ParkingLotDatabaseSettings> parkingLotDatabaseSettings)
{
var mongoClient = new MongoClient(parkingLotDatabaseSettings.Value.ConnectionString); //connections tring连接到数据库url
var mongoDatabase = mongoClient.GetDatabase(parkingLotDatabaseSettings.Value.DatabaseName); //通过客户端连接相应数据库

_parkingLotCollection = mongoDatabase.GetCollection<ParkingLot>(parkingLotDatabaseSettings.Value.CollectionName); //用database取相应collection

//从数据存储的地方获取collection
}

public async Task<ParkingLot> CreateParkingLot(ParkingLot parkingLot) //repo是最底层的,所以用parkinglot,Dto是外界和controller的对象
{
await _parkingLotCollection.InsertOneAsync(parkingLot);
return await _parkingLotCollection.Find(a => a.Name == parkingLot.Name).FirstAsync(); //find created item by name
}

public async Task DeleteParkingLot(string ParkingLotId) //没有给return的值就不用return await
{
await _parkingLotCollection.DeleteOneAsync(p => p.Id == ParkingLotId);
}

public async Task<List<ParkingLot>> GetAllParkingLots()
{
return await _parkingLotCollection.Find(_ => true).ToListAsync();
}

public async Task<ParkingLot> GetById(string ParkingLotId)
{
return await _parkingLotCollection.Find(p => p.Id == ParkingLotId).FirstAsync();

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggestion: use FirstOrDefaultAsync instead.

}

public async Task<ParkingLot> UpdateParkingLot(string ParkingLotId, ParkingLot updatedParkingLot)
{
await _parkingLotCollection.ReplaceOneAsync(p => p.Id == ParkingLotId, updatedParkingLot);
return await GetById(ParkingLotId);
}
}
}
66 changes: 66 additions & 0 deletions ParkingLotApi/Services/ParkingLotsService.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
using ParkingLotApi.Dtos;
using ParkingLotApi.Exceptions;
using ParkingLotApi.Models;
using ParkingLotApi.Repository;

namespace ParkingLotApi.Services
{
public class ParkingLotsService
{
private readonly IParkingLotsRepository parkingLotsRepository;
public ParkingLotsService(IParkingLotsRepository parkingLotsRepository)
{
this.parkingLotsRepository = parkingLotsRepository;
}

public async Task<ParkingLot> AddAsync(ParkingLotDto parkingLotDto) //Task<返回值的类型>
{
if (parkingLotDto.Capacity < 10)
{
throw new InvalidCapacityException();
}

return await parkingLotsRepository.CreateParkingLot(parkingLotDto.ToEntity());

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

well: hold transfer to entity logic in DTO

//用外界传进来的dto,return parkinglot给repo
}

public async Task DeleteAsync(string parkingLotId)
{
await parkingLotsRepository.DeleteParkingLot(parkingLotId);
}

public async Task<List<ParkingLot>> GetOnePageAsync(int pageIndex)
{
List<ParkingLot> allParkingLots = await parkingLotsRepository.GetAllParkingLots();
int pageSize = 15;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion: extract this variable to a constant

int startIndex = (pageIndex - 1) * pageSize;
return allParkingLots.Skip(startIndex).Take(pageSize).ToList();
}

public async Task<ParkingLot> GetByIdAsync(string parkingLotId)
{
return await parkingLotsRepository.GetById(parkingLotId);
}

public async Task<ParkingLot> UpdateParkingLotCapacityAsync(int capacity, string parkingLotId)
{
if (capacity < 10)
{
throw new InvalidCapacityException();
}

var parkingLot = await parkingLotsRepository.GetById(parkingLotId);
var updatedParkingLot = new ParkingLot()
{
Capacity = capacity,
Id = parkingLot.Id,
Name = parkingLot.Name,
Location = parkingLot.Location
};

return await parkingLotsRepository.UpdateParkingLot(parkingLotId, updatedParkingLot);

}

}
}
8 changes: 7 additions & 1 deletion ParkingLotApi/appsettings.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,11 @@
"Microsoft.AspNetCore": "Warning"
}
},
"AllowedHosts": "*"
"AllowedHosts": "*",
"ParkingLotStoreDatabase": {
"ConnectionString": "mongodb://localhost:27017",
"DatabaseName": "ParkingLots",
"CollectionName": "ParkingLot"
}
}

Loading