Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions E-Commerce.Domain/Contracts/IBasketRepository.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
using E_Commerce.Domain.Entities.BasketModule;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace E_Commerce.Domain.Contracts
{
public interface IBasketRepository
{
Task<CustomerBasket?> GetBasketAsync(string basketid);
Task<CustomerBasket?> CreateOrUpdateBasketAsync(CustomerBasket basket , TimeSpan timeTolive=default);
Task<bool> DeleteAsync(string basketid);

}
}
19 changes: 19 additions & 0 deletions E-Commerce.Domain/Entities/BasketModule/BasketItem.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace E_Commerce.Domain.Entities.BasketModule
{
public class BasketItem
{

public int Id { get; set; }
public string ProductName { get; set; } = default!;
public string PictureUrl { get; set; } = default!;
public decimal Price { get; set; }
public int Quantity { get; set; }

}
}
14 changes: 14 additions & 0 deletions E-Commerce.Domain/Entities/BasketModule/CustomerBasket.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace E_Commerce.Domain.Entities.BasketModule
{
public class CustomerBasket
{
public String Id { get; set; } = default!; // Will Be GUID : Created By Client [FrontEnd]
public ICollection<BasketItem> Items { get; set; } = [];
}
}
1 change: 1 addition & 0 deletions E-Commerce.Persistance/E-Commerce.Persistance.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@

<ItemGroup>
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="9.0.10" />
<PackageReference Include="StackExchange.Redis" Version="2.9.32" />
</ItemGroup>

<ItemGroup>
Expand Down
51 changes: 51 additions & 0 deletions E-Commerce.Persistance/Repositories/BasketRepository.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
using E_Commerce.Domain.Contracts;
using E_Commerce.Domain.Entities.BasketModule;
using StackExchange.Redis;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.Json;
using System.Threading.Tasks;

namespace E_Commerce.Persistance.Repositories
{
public class BasketRepository : IBasketRepository
{
private readonly IDatabase _database;
public BasketRepository(IConnectionMultiplexer connection)
{
_database = connection.GetDatabase();
}
public async Task<CustomerBasket?> CreateOrUpdateBasketAsync(CustomerBasket basket, TimeSpan timeTolive = default)
{
var jsonBasket = JsonSerializer.Serialize(basket);
var IsCreatedOrUpdated = await _database.StringSetAsync(basket.Id, jsonBasket,
(timeTolive == default) ? TimeSpan.FromDays(7) : timeTolive);
if (IsCreatedOrUpdated)
{
//var Basket = await _database.StringGetAsync(basket.Id);
//return JsonSerializer.Deserialize<CustomerBasket>(Basket!);
// =
return await GetBasketAsync(basket.Id);

}
else
{
return null;
}
}

public async Task<bool> DeleteAsync(string basketid) => await _database.KeyDeleteAsync(basketid);


public async Task<CustomerBasket?> GetBasketAsync(string basketid)
{
var Basket = await _database.StringGetAsync(basketid);
if(Basket.IsNullOrEmpty)
return null;
else
return JsonSerializer.Deserialize<CustomerBasket?>(Basket!);
}
}
}
48 changes: 48 additions & 0 deletions E-Commerce.Presentaion/Controllers/BasketsController.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
using E_Commerce.Services.Abstraction;
using E_Commerce.Shared.DTOs.BasketDTOs;
using Microsoft.AspNetCore.Mvc;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace E_Commerce.Presentaion.Controllers
{
[ApiController]
[Route("api/[Controller]")]
public class BasketsController : ControllerBase
{
private readonly IBasketService _basketService;

public BasketsController(IBasketService basketService)
{
this._basketService = basketService;
}

// GET : BaseUrl/api/Baskets?id= => Id Will Be Token As A Query String
[HttpGet]
public async Task<ActionResult<BasketDTO>> GetBasket(string id)
{
var Basket = await _basketService.GetBasketAsync(id);
return Ok(Basket);
}

// POST : BaseUrl/api/Baskets
[HttpPost]
public async Task<ActionResult<BasketDTO>> CreateOrUpdateBasket(BasketDTO basket)
{
var Basket = await _basketService.CreateOrUpdateBasketAsync(basket);
return Ok(Basket);
}

// Delete : BaseUrl/api/Bakets/{id} => Id Will Be Token From Route
[HttpDelete("{id}")]
public async Task<ActionResult<bool>> DeleteBasket(string id)
{
var result = await _basketService.DeleteBasketAsync(id);
return Ok(result);
}

}
}
16 changes: 16 additions & 0 deletions E-Commerce.Services.Abstraction/IBasketService.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
using E_Commerce.Shared.DTOs.BasketDTOs;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace E_Commerce.Services.Abstraction
{
public interface IBasketService
{
Task<BasketDTO> GetBasketAsync(string id);
Task<BasketDTO> CreateOrUpdateBasketAsync(BasketDTO basketDTO);
Task<bool> DeleteBasketAsync(string id);
}
}
41 changes: 41 additions & 0 deletions E-Commerce.Services/BasketService.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
using AutoMapper;
using E_Commerce.Domain.Contracts;
using E_Commerce.Domain.Entities.BasketModule;
using E_Commerce.Services.Abstraction;
using E_Commerce.Shared.DTOs.BasketDTOs;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace E_Commerce.Services
{
public class BasketService : IBasketService
{
private readonly IBasketRepository _basketRepository;
private readonly IMapper _mapper;

public BasketService(IBasketRepository basketRepository, IMapper mapper)
{
this._basketRepository = basketRepository;
this._mapper = mapper;
}
public async Task<BasketDTO> CreateOrUpdateBasketAsync(BasketDTO basketDTO)
{
var CustomerBasket = _mapper.Map<BasketDTO, CustomerBasket>(basketDTO);
var CreatedOrUpdatedBasket = await _basketRepository.CreateOrUpdateBasketAsync(CustomerBasket);
return _mapper.Map<CustomerBasket, BasketDTO>(CreatedOrUpdatedBasket!);

}

public async Task<bool> DeleteBasketAsync(string id) => await _basketRepository.DeleteAsync(id);


public async Task<BasketDTO> GetBasketAsync(string id)
{
var Basket = await _basketRepository.GetBasketAsync(id);
return _mapper.Map<CustomerBasket, BasketDTO>(Basket!);
}
}
}
21 changes: 21 additions & 0 deletions E-Commerce.Services/MappingProfiels/BasketProfile.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
using AutoMapper;
using E_Commerce.Domain.Entities.BasketModule;
using E_Commerce.Shared.DTOs.BasketDTOs;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace E_Commerce.Services.MappingProfiels
{
public class BasketProfile :Profile
{
public BasketProfile()
{
CreateMap<BasketDTO,CustomerBasket>().ReverseMap();

CreateMap<BasketItemDTO,BasketItem>().ReverseMap();
}
}
}
18 changes: 18 additions & 0 deletions E-Commerce.Shared/DTOs/BasketDTOs/BasketDTO.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace E_Commerce.Shared.DTOs.BasketDTOs
{
public record BasketDTO(string Id, ICollection<BasketItemDTO> Items);

// record: A special type in C# used for concise data storage, ideal for DTOs
// immutable: Values cannot be changed after creation (safe for transfer and comparison)
// Value equality: Compared by content, not by reference (if two objects have identical content → considered equal)
// Supports quick construction via a built-in constructor in the same line
// Facilitates working with JSON in Web APIs
// Less code and clearer than a regular class

}
20 changes: 20 additions & 0 deletions E-Commerce.Shared/DTOs/BasketDTOs/BasketItemDTO.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace E_Commerce.Shared.DTOs.BasketDTOs
{
public record BasketItemDTO(
int Id,
String ProductName ,
string PictureUrl,
[Range(1,double.MaxValue)]
decimal Price,
[Range(1,100)]
int Quantity
);

}
11 changes: 10 additions & 1 deletion E-CommerceProject/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
using E_Commerce.Services.MappingProfiels;
using E_CommerceProject.Extentions;
using Microsoft.EntityFrameworkCore;
using StackExchange.Redis;
using System.Reflection;
using System.Threading.Tasks;

Expand Down Expand Up @@ -39,10 +40,18 @@ public static async Task Main(string[] args)

builder.Services.AddScoped<IProductService, ProductService>();


builder.Services.AddSingleton<IConnectionMultiplexer>(sp =>
{
return ConnectionMultiplexer.Connect(builder.Configuration.GetConnectionString("RedisConnection")!);
});
builder.Services.AddScoped<IBasketRepository, BasketRepository>();
builder.Services.AddScoped<IBasketService, BasketService>();

#endregion

#region Redis Connection

#endregion
var app = builder.Build();

#region Data Seeding - Pending Migations
Expand Down
4 changes: 3 additions & 1 deletion E-CommerceProject/appsettings.Development.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@
}
},
"ConnectionStrings": {
"DefaultConnection": " Server =.; Database =Ecommerce; Trusted_Connection=True;TrustServerCertificate=True;"
"DefaultConnection": " Server =.; Database =Ecommerce; Trusted_Connection=True;TrustServerCertificate=True;",
"RedisConnection": "localhost"



},
Expand Down