Compare commits
1
Commits
22451db209
..
master
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
165511b57d |
Vendored
+33
@@ -0,0 +1,33 @@
|
||||
{
|
||||
// Use IntelliSense to learn about possible attributes.
|
||||
// Hover to view descriptions of existing attributes.
|
||||
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
"name": ".NET Core Launch (web)",
|
||||
"type": "coreclr",
|
||||
"request": "launch",
|
||||
"preLaunchTask": "build",
|
||||
"program": "${workspaceFolder}/bin/Debug/net10.0/example-csharp-app.dll",
|
||||
"args": [],
|
||||
"cwd": "${workspaceFolder}",
|
||||
"stopAtEntry": false,
|
||||
"serverReadyAction": {
|
||||
"action": "openExternally",
|
||||
"pattern": "\\bNow listening on:\\s+(https?://\\S+)"
|
||||
},
|
||||
"env": {
|
||||
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||
},
|
||||
"sourceFileMap": {
|
||||
"/Views": "${workspaceFolder}/Views"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": ".NET Core Attach",
|
||||
"type": "coreclr",
|
||||
"request": "attach"
|
||||
}
|
||||
]
|
||||
}
|
||||
Vendored
+41
@@ -0,0 +1,41 @@
|
||||
{
|
||||
"version": "2.0.0",
|
||||
"tasks": [
|
||||
{
|
||||
"label": "build",
|
||||
"command": "dotnet",
|
||||
"type": "process",
|
||||
"args": [
|
||||
"build",
|
||||
"${workspaceFolder}/example-csharp-app.csproj",
|
||||
"/property:GenerateFullPaths=true",
|
||||
"/consoleloggerparameters:NoSummary;ForceNoAlign"
|
||||
],
|
||||
"problemMatcher": "$msCompile"
|
||||
},
|
||||
{
|
||||
"label": "publish",
|
||||
"command": "dotnet",
|
||||
"type": "process",
|
||||
"args": [
|
||||
"publish",
|
||||
"${workspaceFolder}/example-csharp-app.csproj",
|
||||
"/property:GenerateFullPaths=true",
|
||||
"/consoleloggerparameters:NoSummary;ForceNoAlign"
|
||||
],
|
||||
"problemMatcher": "$msCompile"
|
||||
},
|
||||
{
|
||||
"label": "watch",
|
||||
"command": "dotnet",
|
||||
"type": "process",
|
||||
"args": [
|
||||
"watch",
|
||||
"run",
|
||||
"--project",
|
||||
"${workspaceFolder}/example-csharp-app.csproj"
|
||||
],
|
||||
"problemMatcher": "$msCompile"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -18,9 +18,10 @@ public class ErrorLogMiddleware
|
||||
await _next(httpContext);
|
||||
return;
|
||||
}
|
||||
catch (Exception)
|
||||
catch (Exception ex)
|
||||
{
|
||||
// todo: logme
|
||||
Console.WriteLine(ex.Message);
|
||||
httpContext.Response.StatusCode = 500;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,7 +13,7 @@ public static class RequestMapper
|
||||
Author = request.Author,
|
||||
BookIndex = request.BookIndex.Trim(),
|
||||
Title = request.Title,
|
||||
YearPublished = request.YearPublished
|
||||
YearPublished = request.YearPublished.Date.ToUniversalTime()
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
+11
-3
@@ -16,8 +16,6 @@ namespace Program
|
||||
{
|
||||
private static void SetupDi(IServiceCollection services, IConfiguration configuration)
|
||||
{
|
||||
services.AddCors();
|
||||
|
||||
services.AddScoped<IBookAggregator, BookAggregator>();
|
||||
services.AddScoped<IBookService, BookService>();
|
||||
services.AddScoped<IBookRepository, BookRepository>();
|
||||
@@ -48,11 +46,21 @@ namespace Program
|
||||
|
||||
builder.Services.AddControllers();
|
||||
builder.Services.AddSwaggerGen();
|
||||
builder.Services.AddCors(options =>
|
||||
{
|
||||
options.AddPolicy("AllowAll", policy =>
|
||||
{
|
||||
policy.AllowAnyOrigin()
|
||||
.AllowAnyMethod()
|
||||
.AllowAnyHeader();
|
||||
});
|
||||
});
|
||||
|
||||
SetupDi(builder.Services, configuration);
|
||||
|
||||
|
||||
var app = builder.Build();
|
||||
app.UseCors("AllowAll");
|
||||
app.UseCors(cors => cors.AllowAnyOrigin().AllowAnyHeader().AllowAnyMethod());
|
||||
//if(app.Environment.IsDevelopment())
|
||||
//{
|
||||
app.UseSwagger();
|
||||
|
||||
@@ -1,2 +1,95 @@
|
||||
# example-csharp-app
|
||||
|
||||
DB sql
|
||||
|
||||
// pgsql
|
||||
|
||||
-- Вставка
|
||||
CREATE PROCEDURE "sp_InsertBook"(
|
||||
Id UUID,
|
||||
Title VARCHAR(255),
|
||||
Author VARCHAR(255),
|
||||
YearPublished TimestampTz = NULL,
|
||||
BookIndex XML = NULL
|
||||
)
|
||||
LANGUAGE plpgsql
|
||||
as $$
|
||||
BEGIN
|
||||
INSERT INTO "Books" ("Id", "Title", "Author", "YearPublished", "BookIndex")
|
||||
VALUES (Id, Title, Author, YearPublished, BookIndex);
|
||||
|
||||
end
|
||||
$$
|
||||
|
||||
-- Обновление
|
||||
CREATE PROCEDURE "sp_UpdateBook"(
|
||||
Id UUID,
|
||||
Title VARCHAR(255),
|
||||
Author VARCHAR(255),
|
||||
YearPublished TimestampTz,
|
||||
BookIndex xml
|
||||
)
|
||||
LANGUAGE plpgsql
|
||||
as $$
|
||||
BEGIN
|
||||
UPDATE "Books"
|
||||
SET
|
||||
"Title" = Title,
|
||||
"Author" = Author,
|
||||
"YearPublished" = YearPublished,
|
||||
"BookIndex" = BookIndex
|
||||
WHERE "Id" = Id;
|
||||
end
|
||||
$$
|
||||
|
||||
-- удаление
|
||||
CREATE PROCEDURE "sp_DeleteBook"(
|
||||
Id UUID
|
||||
)
|
||||
LANGUAGE plpgsql
|
||||
as $$
|
||||
BEGIN
|
||||
DELETE FROM "Books" WHERE "Id" = Id;
|
||||
end
|
||||
$$
|
||||
|
||||
-- получение данных
|
||||
CREATE FUNCTION "sp_GetBookById"(BookId UUID)
|
||||
returns table (
|
||||
"Id" UUID,
|
||||
"Author" TEXT,
|
||||
"Title" TEXT,
|
||||
"YearPublished" TIMESTAMPTZ,
|
||||
"BookIndex" XML
|
||||
)
|
||||
LANGUAGE plpgsql
|
||||
as $$
|
||||
BEGIN
|
||||
RETURN QUERY
|
||||
SELECT b."Id", b."Author", b."Title", b."YearPublished", b."BookIndex"
|
||||
FROM "Books" b
|
||||
WHERE b."Id" = BookId;
|
||||
END
|
||||
$$
|
||||
|
||||
-- поиск по библиотеке
|
||||
CREATE FUNCTION "sp_SearchBooks"(p_Title TEXT, p_Author TEXT, p_Index TEXT)
|
||||
returns table (
|
||||
"Id" UUID,
|
||||
"Author" TEXT,
|
||||
"Title" TEXT,
|
||||
"YearPublished" TIMESTAMPTZ,
|
||||
"BookIndex" XML
|
||||
)
|
||||
LANGUAGE plpgsql
|
||||
as $$
|
||||
BEGIN
|
||||
RETURN QUERY
|
||||
SELECT b."Id", b."Author", b."Title", b."YearPublished", b."BookIndex"
|
||||
FROM "Books" b
|
||||
WHERE
|
||||
(p_Author IS NULL OR b."Author" ~* p_Author) AND
|
||||
(p_Title IS NULL OR b."Title" ~* p_Title) AND
|
||||
(b."BookIndex"::TEXT ILIKE CONCAT('%', p_Index, '%'));
|
||||
END
|
||||
$$
|
||||
@@ -4,6 +4,8 @@ using example_csharp_app.Dto;
|
||||
using example_csharp_app.Interfaces;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using System.Linq;
|
||||
using System.Data.SqlClient;
|
||||
using System.Globalization;
|
||||
using example_csharp_app.Models.Request;
|
||||
|
||||
namespace example_csharp_app.Repositories;
|
||||
@@ -19,57 +21,80 @@ public class BookRepository : IBookRepository
|
||||
|
||||
public async Task<int> AddBookAsync(BookDto dto)
|
||||
{
|
||||
_dbContext.Add(dto);
|
||||
return await _dbContext.SaveChangesAsync();
|
||||
var result = await _dbContext.Database.ExecuteSqlInterpolatedAsync(
|
||||
$"CALL public.\"sp_InsertBook\" ({dto.Id}, {dto.Title}, {dto.Author}, {dto.YearPublished}, {dto.BookIndex}::XML)"
|
||||
);
|
||||
|
||||
return 1; // Процедура в pgsql не возвращает значение - это делают функции; даже rowcount только в функции
|
||||
|
||||
// _dbContext.Add(dto);
|
||||
// return await _dbContext.SaveChangesAsync();
|
||||
}
|
||||
|
||||
public async Task<BookDto?> GetBookAsync(Guid bookId, bool enableEdit = true)
|
||||
{
|
||||
var query = _dbContext.Books.Where(x => x.Id == bookId);
|
||||
if(enableEdit == false)
|
||||
{
|
||||
query = query.AsNoTracking();
|
||||
}
|
||||
var query = _dbContext.Books.FromSqlInterpolated(
|
||||
$"SELECT * FROM \"sp_GetBookById\"({bookId})"
|
||||
);
|
||||
|
||||
return await query.FirstOrDefaultAsync();
|
||||
|
||||
// var query = _dbContext.Books.Where(x => x.Id == bookId);
|
||||
// if(enableEdit == false)
|
||||
// {
|
||||
// query = query.AsNoTracking();
|
||||
// }
|
||||
// return await query.FirstOrDefaultAsync();
|
||||
}
|
||||
|
||||
public async Task<IReadOnlyCollection<BookDto>> GetBookListAsync(BookFilterRequest request)
|
||||
{
|
||||
IQueryable<BookDto> query = _dbContext.Books.AsNoTracking();
|
||||
if(request.Author != null)
|
||||
{
|
||||
query = query.Where(x => x.Author.Contains(request.Author));
|
||||
}
|
||||
if(request.Title != null)
|
||||
{
|
||||
query = query.Where(x => x.Title.Contains(request.Title));
|
||||
}
|
||||
if(request.Index != null)
|
||||
{
|
||||
var interminentQuery = await query.ToListAsync();
|
||||
return interminentQuery.Where(x => x.BookIndex.Contains(request.Index)).ToList();
|
||||
// решение плохое, но для поиска более хорошего нужно время
|
||||
// возможно, потребуется изменить тип поля, либо раскидывать xml по другим таблицам.
|
||||
// также возможно что в TransactSql этот момент учтен и поиск будет работать нативно
|
||||
// но TransactSql у меня не раскатан, а Postgre уже стоит и крутится; так быстрее и проще в текущей ситуации
|
||||
|
||||
// xml ~~ xml - operation does not exist
|
||||
//query = query.Where(x => x.BookIndex.Contains(request.Index));
|
||||
}
|
||||
var books = await _dbContext.Books.FromSqlInterpolated($"SELECT * FROM \"sp_SearchBooks\" ({request.Title}, {request.Author}, {request.Index})").ToListAsync();
|
||||
return books;
|
||||
|
||||
return await query.ToListAsync(); // можно возвращать query как IEnumerable, но в некоторых случаях это будет приводить к исключению
|
||||
// например если контекст инжектится фабрикой
|
||||
// IQueryable<BookDto> query = _dbContext.Books.AsNoTracking();
|
||||
// if(request.Author != null)
|
||||
// {
|
||||
// query = query.Where(x => x.Author.Contains(request.Author));
|
||||
// }
|
||||
// if(request.Title != null)
|
||||
// {
|
||||
// query = query.Where(x => x.Title.Contains(request.Title));
|
||||
// }
|
||||
// if(request.Index != null)
|
||||
// {
|
||||
// var interminentQuery = await query.ToListAsync();
|
||||
// return interminentQuery.Where(x => x.BookIndex.Contains(request.Index)).ToList();
|
||||
// // решение плохое
|
||||
|
||||
// // xml ~~ xml - operation does not exist
|
||||
// //query = query.Where(x => x.BookIndex.Contains(request.Index));
|
||||
// }
|
||||
|
||||
// return await query.ToListAsync();
|
||||
}
|
||||
|
||||
public async Task<int> RemoveBookAsync(BookDto dto)
|
||||
{
|
||||
_dbContext.Books.Remove(dto);
|
||||
return await _dbContext.SaveChangesAsync();
|
||||
var result = await _dbContext.Database.ExecuteSqlInterpolatedAsync(
|
||||
$"CALL \"sp_DeleteBook\"({dto.Id})"
|
||||
);
|
||||
return 1; // Процедура в pgsql не возвращает значение - это делают функции; даже rowcount только в функции
|
||||
|
||||
// _dbContext.Books.Remove(dto);
|
||||
// return await _dbContext.SaveChangesAsync();
|
||||
}
|
||||
|
||||
public async Task<int> UpdateBookAsync(BookDto dto)
|
||||
{
|
||||
_dbContext.Books.Update(dto);
|
||||
return await _dbContext.SaveChangesAsync();
|
||||
var result = await _dbContext.Database.ExecuteSqlInterpolatedAsync(
|
||||
$"CALL \"sp_UpdateBook\"({dto.Id}, {dto.Title}, {dto.Author}, {dto.YearPublished.ToString()}, {dto.BookIndex})"
|
||||
);
|
||||
|
||||
return 1; // Процедура в pgsql не возвращает значение - это делают функции; даже rowcount только в функции
|
||||
|
||||
// _dbContext.Books.Update(dto);
|
||||
// return await _dbContext.SaveChangesAsync();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -56,6 +56,6 @@ public class BookService : IBookService
|
||||
private async Task<BookDto> GetBookForEdit(Guid bookId)
|
||||
{
|
||||
var book = await _bookRepository.GetBookAsync(bookId);
|
||||
return book ?? throw new ArgumentException();
|
||||
return book ?? throw new ArgumentException("Book does not exist");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"Logging": {
|
||||
"LogLevel": {
|
||||
"Default": "Information",
|
||||
"Microsoft.AspNetCore": "Warning"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"Logging": {
|
||||
"LogLevel": {
|
||||
"Default": "Information",
|
||||
"Microsoft.AspNetCore": "Warning"
|
||||
}
|
||||
},
|
||||
"AllowedHosts": "*",
|
||||
"ConnectionStrings": {
|
||||
"DefaultConnection": "Server=192.168.1.40:5455;Database=aspnet-testdata;User Id={SECRET_USER};Password={SECRET_PASS}"
|
||||
}
|
||||
}
|
||||
@@ -18,6 +18,7 @@
|
||||
<PackageReference Include="Swashbuckle.AspNetCore.Swagger" Version="10.2.3" />
|
||||
<PackageReference Include="Swashbuckle.AspNetCore.SwaggerGen" Version="10.2.3" />
|
||||
<PackageReference Include="Swashbuckle.AspNetCore.Annotations" Version="10.2.3" />
|
||||
<PackageReference Include="Microsoft.Data.SqlClient" Version="7.1.0" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
Reference in New Issue
Block a user