| Version 5 (modified by , 3 hours ago) ( diff ) |
|---|
Напреден развој на апликација
Pooling
Во backend слојот на StockMaster апликацијата користиме ASP.NET Core со Entity Framework Core за комуникација со PostgreSQL базата на податоци.
Поради оваа архитектура, конекциите со базата не се креираат рачно во кодот. Наместо тоа, тие автоматски се управуваат од Npgsql, кој е стандардниот .NET data provider за PostgreSQL. Connection pooling е вградена функционалност на Npgsql и е активирана по default.
appsettings.json
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"AllowedHosts": "*",
"ConnectionStrings": {
"DefaultConnection": "Host=localhost;Database=StockDb;Username=YOUR_USERNAME;Password=YOUR_PASSWORD;Pooling=true;MinPoolSize=5;MaxPoolSize=100;Connection Idle Lifetime=300;"
}
}
Pooling=true Овозможува connection pooling, што значи дека базата ги реупотребува постоечките конекции наместо да креира нова конекција за секое барање.
MinPoolSize=5 Го дефинира минималниот број на активни конекции што се одржуваат во connection pool при стартување на апликацијата.
MaxPoolSize=100 Го ограничува максималниот број на истовремени конекции дозволени во connection pool при зголемен traffic.
Connection Idle Lifetime=300 Го одредува времето (во секунди) колку една неактивна конекција останува во pool пред автоматски да биде затворена и отстранета ако не се користи.
Трансакции
Креирање на продажба (SaleService.cs)
Зошто Transaction?
Кога се креира продажен запис, количината мора да се намали од залихата. Ако настане грешка при намалувањето на залихата, тогаш и продажниот запис не треба да се зачува, за да се обезбеди податоците да останат конзистентни.
public async Task<bool> CreateSaleAsync(Sale sale)
{
using var transaction = await _context.Database.BeginTransactionAsync();
try
{
_context.Sales.Add(sale);
await _context.SaveChangesAsync();
foreach (var item in sale.SaleItems)
{
var stock = await _context.WarehouseStocks
.FirstOrDefaultAsync(ws => ws.WarehouseId == sale.WarehouseId
&& ws.ProductId == item.ProductId);
if (stock == null || stock.QuantityOnHand < item.Quantity)
{
throw new Exception("Insufficient Stock");
}
stock.QuantityOnHand -= item.Quantity;
stock.LastUpdated = DateTime.Now;
}
await _context.SaveChangesAsync();
await transaction.CommitAsync();
return true;
}
catch
{
await transaction.RollbackAsync();
return false;
}
}
Примање на нарачка (PurchaseOrderService.cs)
Зошто Transaction?
Кога статусот на нарачката се менува во „Received“, залихите мора да се зголемат. Ако едната операција се изврши, а другата не, ќе настане неконзистентност во податоците.
public async Task<bool> ReceivePurchaseOrderAsync(int poId)
{
using var transaction = await _context.Database.BeginTransactionAsync();
try
{
var po = await GetPurchaseOrderByIdAsync(poId);
if (po == null || po.Status == "Received")
return false;
foreach (var item in po.PurchaseOrderItems)
{
var stock = await _context.WarehouseStocks
.FirstOrDefaultAsync(ws => ws.WarehouseId == po.WarehouseId && ws.ProductId == item.ProductId);
if (stock == null)
{
stock = new WarehouseStock
{
WarehouseId = po.WarehouseId,
ProductId = item.ProductId,
QuantityOnHand = item.Quantity
};
_context.WarehouseStocks.Add(stock);
}
else
{
stock.QuantityOnHand += item.Quantity;
}
stock.LastUpdated = DateTime.Now;
item.ReceivedQuantity = item.Quantity;
}
po.Status = "Received";
po.ActualDeliveryDate = DateTime.Now.Date;
await _context.SaveChangesAsync();
await transaction.CommitAsync();
return true;
}
catch
{
await transaction.RollbackAsync();
return false;
}
}
Креирање на нарачка (PurchaseOrderService.cs)
Зошто Transaction?
Насловот на нарачката и ставките (Items) претставуваат една целина. Ако едното се зачува, а другото не, ќе се создаде празна нарачка и податоците ќе бидат неконзистентни.
public async Task<bool> CreatePurchaseOrderAsync(PurchaseOrder po)
{
using var transaction = await _context.Database.BeginTransactionAsync();
try
{
_context.PurchaseOrders.Add(po);
await _context.SaveChangesAsync();
await transaction.CommitAsync();
return true;
}
catch
{
await transaction.RollbackAsync();
return false;
}
}
Креирање на корисник (AuthService.cs)
Зошто Transaction?
При креирање на корисник, за да се обезбеди целосност при хеширање на лозинка и при доделување улоги, како и за идни дополнителни операции.
public async Task<bool> CreateUserAsync(User user, string password)
{
using var transaction = await _context.Database.BeginTransactionAsync();
try
{
user.Password = BCrypt.Net.BCrypt.HashPassword(password);
_context.Users.Add(user);
await _context.SaveChangesAsync();
await transaction.CommitAsync();
return true;
}
catch
{
await transaction.RollbackAsync();
return false;
}
}
Бришење на производ (ProductService.cs)
Зошто Transaction?
Се користи Soft Delete (поставување на производот како неактивен). Ова е критична промена на податоци и ако врската со базата се прекине за време на процесот, состојбата на производот не смее да остане недефинирана.
public async Task<bool> DeleteProductAsync(int id)
{
using var transaction = await _context.Database.BeginTransactionAsync();
try
{
var product = await _context.Products.FindAsync(id);
if (product != null)
{
product.IsActive = false;
await _context.SaveChangesAsync();
await transaction.CommitAsync();
return true;
}
return false;
}
catch
{
await transaction.RollbackAsync();
return false;
}
}
