Compare commits
4
Commits
2b05c26eb3
..
master
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
165511b57d | ||
|
|
22451db209 | ||
|
|
c76e42d89d | ||
|
|
68da43a673 |
@@ -0,0 +1,2 @@
|
|||||||
|
bin/
|
||||||
|
obj/
|
||||||
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"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -0,0 +1,63 @@
|
|||||||
|
using System;
|
||||||
|
using example_csharp_app.Interfaces;
|
||||||
|
using example_csharp_app.Models.Mapper;
|
||||||
|
using example_csharp_app.Models.Request;
|
||||||
|
using example_csharp_app.Models.Response;
|
||||||
|
|
||||||
|
namespace example_csharp_app.Aggregators;
|
||||||
|
|
||||||
|
public class BookAggregator : IBookAggregator
|
||||||
|
{
|
||||||
|
private readonly IBookService _bookService;
|
||||||
|
|
||||||
|
public BookAggregator(IBookService bookService)
|
||||||
|
{
|
||||||
|
_bookService = bookService;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<BookOperationResponse> AddBookAsync(BookCreateRequest request)
|
||||||
|
{
|
||||||
|
var result = await _bookService.AddBookAsync(request.MapToModel());
|
||||||
|
return new BookOperationResponse()
|
||||||
|
{
|
||||||
|
IsSuccess = result > 0
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<BookSingleResponse> GetBookAsync(Guid bookId)
|
||||||
|
{
|
||||||
|
var book = await _bookService.GetBookAsync(bookId);
|
||||||
|
return new BookSingleResponse()
|
||||||
|
{
|
||||||
|
Book = book
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<BookListResponse> GetBookListAsync(BookFilterRequest request)
|
||||||
|
{
|
||||||
|
// should make separate filter object, but feel lazy
|
||||||
|
var bookList = await _bookService.GetBooksAsync(request);
|
||||||
|
return new BookListResponse()
|
||||||
|
{
|
||||||
|
Books = bookList
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<BookOperationResponse> RemoveBookAsync(Guid bookId)
|
||||||
|
{
|
||||||
|
var result = await _bookService.RemoveBookAsync(bookId);
|
||||||
|
return new BookOperationResponse()
|
||||||
|
{
|
||||||
|
IsSuccess = result > 0
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<BookOperationResponse> UpdateBookAsync(BookUpdateRequest request)
|
||||||
|
{
|
||||||
|
var result = await _bookService.UpdateBookAsync(request.Id, request.MapToModel());
|
||||||
|
return new BookOperationResponse()
|
||||||
|
{
|
||||||
|
IsSuccess = result > 0
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,60 @@
|
|||||||
|
using example_csharp_app.Interfaces;
|
||||||
|
using example_csharp_app.Models.Request;
|
||||||
|
using example_csharp_app.Models.Response;
|
||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using Swashbuckle.AspNetCore.Annotations;
|
||||||
|
|
||||||
|
namespace example_csharp_app.Controllers
|
||||||
|
{
|
||||||
|
[Route("api/[controller]")]
|
||||||
|
[ApiController]
|
||||||
|
public class BookController : ControllerBase
|
||||||
|
{
|
||||||
|
private readonly IBookAggregator _bookAggregator;
|
||||||
|
|
||||||
|
public BookController(IBookAggregator bookAggregator)
|
||||||
|
{
|
||||||
|
_bookAggregator = bookAggregator;
|
||||||
|
}
|
||||||
|
|
||||||
|
[SwaggerResponse(200, "Получить список книг", typeof(BookListResponse))]
|
||||||
|
[HttpGet()]
|
||||||
|
public async Task<IActionResult> GetList([FromQuery]BookFilterRequest request)
|
||||||
|
{
|
||||||
|
var bookList = await _bookAggregator.GetBookListAsync(request);
|
||||||
|
return Ok(bookList);
|
||||||
|
}
|
||||||
|
|
||||||
|
[SwaggerResponse(200, "Получить список книг", typeof(BookSingleResponse))]
|
||||||
|
[HttpGet("{bookId}")]
|
||||||
|
public async Task<IActionResult> GetBook([FromRoute] Guid bookId)
|
||||||
|
{
|
||||||
|
var result = await _bookAggregator.GetBookAsync(bookId);
|
||||||
|
return Ok(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
[SwaggerResponse(200, "Добавить книгу", typeof(BookOperationResponse))]
|
||||||
|
[HttpPost()]
|
||||||
|
public async Task<IActionResult> AddBook([FromBody]BookCreateRequest request)
|
||||||
|
{
|
||||||
|
var result = await _bookAggregator.AddBookAsync(request);
|
||||||
|
return Ok(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
[SwaggerResponse(200, "Обновить книгу", typeof(BookOperationResponse))]
|
||||||
|
[HttpPut()]
|
||||||
|
public async Task<IActionResult> UpdateBook([FromBody]BookUpdateRequest request)
|
||||||
|
{
|
||||||
|
var result = await _bookAggregator.UpdateBookAsync(request);
|
||||||
|
return Ok(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
[SwaggerResponse(200, "Удалить книгу", typeof(BookOperationResponse))]
|
||||||
|
[HttpDelete("{bookId}")]
|
||||||
|
public async Task<IActionResult> RemoveBook([FromRoute]Guid bookId)
|
||||||
|
{
|
||||||
|
var result = await _bookAggregator.RemoveBookAsync(bookId);
|
||||||
|
return Ok(result);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
using example_csharp_app.Dto;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace example_app.Database.Connector;
|
||||||
|
|
||||||
|
public class AppDbContext(DbContextOptions<AppDbContext> options) : DbContext(options)
|
||||||
|
{
|
||||||
|
public DbSet<BookDto> Books { set; get; }
|
||||||
|
|
||||||
|
protected override void OnModelCreating(ModelBuilder builder)
|
||||||
|
{
|
||||||
|
base.OnModelCreating(builder);
|
||||||
|
|
||||||
|
builder.Entity<BookDto>()
|
||||||
|
.ToTable("Books")
|
||||||
|
.HasKey(x => x.Id);
|
||||||
|
|
||||||
|
builder.Entity<BookDto>()
|
||||||
|
.Property(x => x.Author)
|
||||||
|
.IsRequired();
|
||||||
|
builder.Entity<BookDto>()
|
||||||
|
.Property(x => x.Title)
|
||||||
|
.IsRequired();
|
||||||
|
builder.Entity<BookDto>()
|
||||||
|
.Property(x => x.YearPublished)
|
||||||
|
.IsRequired();
|
||||||
|
builder.Entity<BookDto>()
|
||||||
|
.Property(x => x.BookIndex)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
builder.Entity<BookDto>()
|
||||||
|
.Property(x => x.BookIndex)
|
||||||
|
.HasColumnType("xml");
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
using System;
|
||||||
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
|
||||||
|
namespace example_csharp_app.Dto;
|
||||||
|
|
||||||
|
public class BookDto
|
||||||
|
{
|
||||||
|
// Для описания модели использовал fluent api в AppDbContext
|
||||||
|
public Guid Id { set; get; }
|
||||||
|
public string Title { set; get; } = string.Empty;
|
||||||
|
public string Author { set; get; } = string.Empty;
|
||||||
|
public DateTime YearPublished { set; get; }
|
||||||
|
public string BookIndex { set; get; } = string.Empty;
|
||||||
|
}
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
namespace example_csharp_app;
|
||||||
|
|
||||||
|
public class ErrorLogMiddleware
|
||||||
|
{
|
||||||
|
private readonly RequestDelegate _next;
|
||||||
|
|
||||||
|
public ErrorLogMiddleware(RequestDelegate next)
|
||||||
|
{
|
||||||
|
_next = next;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task Invoke(HttpContext httpContext, IConfiguration configuration)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await _next(httpContext);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
// todo: logme
|
||||||
|
Console.WriteLine(ex.Message);
|
||||||
|
httpContext.Response.StatusCode = 500;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,49 @@
|
|||||||
|
using System;
|
||||||
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
using System.Windows.Markup;
|
||||||
|
using System.Xml;
|
||||||
|
|
||||||
|
namespace example_csharp_app.Filters;
|
||||||
|
|
||||||
|
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Parameter, AllowMultiple = false)]
|
||||||
|
public class XmlAttribute : ValidationAttribute
|
||||||
|
{
|
||||||
|
|
||||||
|
public XmlAttribute(): base("Passed string is not XML")
|
||||||
|
{}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Override of <see cref="ValidationAttribute.IsValid(object)" />
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="value">The value to test</param>
|
||||||
|
/// <returns>
|
||||||
|
/// Returns <see langword="false" /> if the <paramref name="value" /> is null or an empty string.
|
||||||
|
/// If <see cref="AllowEmptyStrings" /> then <see langword="true" /> is returned for empty strings.
|
||||||
|
/// </returns>
|
||||||
|
public override bool IsValid(object? value)
|
||||||
|
{
|
||||||
|
if (value is null || value is not string)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
string possiblyXml = ((string)value).Trim();
|
||||||
|
if (!possiblyXml.StartsWith('<') && !possiblyXml.EndsWith('>'))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
XmlDocument xml = new XmlDocument();
|
||||||
|
try
|
||||||
|
{
|
||||||
|
xml.LoadXml(possiblyXml);
|
||||||
|
}
|
||||||
|
catch (XmlException e)
|
||||||
|
{
|
||||||
|
// logme?
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
using System;
|
||||||
|
using example_csharp_app.Models.Request;
|
||||||
|
using example_csharp_app.Models.Response;
|
||||||
|
|
||||||
|
namespace example_csharp_app.Interfaces;
|
||||||
|
|
||||||
|
public interface IBookAggregator
|
||||||
|
{
|
||||||
|
Task<BookListResponse> GetBookListAsync(BookFilterRequest request);
|
||||||
|
Task<BookSingleResponse> GetBookAsync(Guid bookId);
|
||||||
|
Task<BookOperationResponse> UpdateBookAsync(BookUpdateRequest request);
|
||||||
|
Task<BookOperationResponse> AddBookAsync(BookCreateRequest request);
|
||||||
|
Task<BookOperationResponse> RemoveBookAsync(Guid bookId);
|
||||||
|
}
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
using System;
|
||||||
|
using example_csharp_app.Dto;
|
||||||
|
using example_csharp_app.Models.Request;
|
||||||
|
|
||||||
|
namespace example_csharp_app.Interfaces;
|
||||||
|
|
||||||
|
public interface IBookRepository
|
||||||
|
{
|
||||||
|
Task<int> AddBookAsync(BookDto dto);
|
||||||
|
Task<int> RemoveBookAsync(BookDto dto);
|
||||||
|
Task<BookDto?> GetBookAsync(Guid bookId, bool enableEdit = true);
|
||||||
|
Task<IReadOnlyCollection<BookDto>> GetBookListAsync(BookFilterRequest request);
|
||||||
|
Task<int> UpdateBookAsync(BookDto dto);
|
||||||
|
}
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
using System;
|
||||||
|
using example_csharp_app.Models.Model;
|
||||||
|
using example_csharp_app.Models.Request;
|
||||||
|
|
||||||
|
namespace example_csharp_app.Interfaces;
|
||||||
|
|
||||||
|
public interface IBookService
|
||||||
|
{
|
||||||
|
Task<IReadOnlyCollection<BookModel>> GetBooksAsync(BookFilterRequest request);
|
||||||
|
Task<BookModel?> GetBookAsync(Guid bookId);
|
||||||
|
Task<int> UpdateBookAsync(Guid bookId, BookUpdateModel model);
|
||||||
|
Task<int> AddBookAsync(BookCreateModel model);
|
||||||
|
Task<int> RemoveBookAsync(Guid bookId);
|
||||||
|
}
|
||||||
+56
@@ -0,0 +1,56 @@
|
|||||||
|
// <auto-generated />
|
||||||
|
using System;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||||
|
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
|
||||||
|
using example_app.Database.Connector;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace example_csharp_app.Migrations
|
||||||
|
{
|
||||||
|
[DbContext(typeof(AppDbContext))]
|
||||||
|
[Migration("20260925040202_init")]
|
||||||
|
partial class init
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||||
|
{
|
||||||
|
#pragma warning disable 612, 618
|
||||||
|
modelBuilder
|
||||||
|
.HasAnnotation("ProductVersion", "10.0.12")
|
||||||
|
.HasAnnotation("Relational:MaxIdentifierLength", 63);
|
||||||
|
|
||||||
|
NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
|
||||||
|
|
||||||
|
modelBuilder.Entity("example_csharp_app.Dto.BookDto", b =>
|
||||||
|
{
|
||||||
|
b.Property<Guid>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("uuid");
|
||||||
|
|
||||||
|
b.Property<string>("Author")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.Property<string>("BookIndex")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("xml");
|
||||||
|
|
||||||
|
b.Property<string>("Title")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.Property<DateTime>("YearPublished")
|
||||||
|
.HasColumnType("timestamp with time zone");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.ToTable("Books", (string)null);
|
||||||
|
});
|
||||||
|
#pragma warning restore 612, 618
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
using System;
|
||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace example_csharp_app.Migrations
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
public partial class init : Migration
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Up(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "Books",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
Id = table.Column<Guid>(type: "uuid", nullable: false),
|
||||||
|
Title = table.Column<string>(type: "text", nullable: false),
|
||||||
|
Author = table.Column<string>(type: "text", nullable: false),
|
||||||
|
YearPublished = table.Column<DateTime>(type: "timestamp with time zone", nullable: false),
|
||||||
|
BookIndex = table.Column<string>(type: "xml", nullable: false)
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("PK_Books", x => x.Id);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Down(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "Books");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,53 @@
|
|||||||
|
// <auto-generated />
|
||||||
|
using System;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||||
|
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||||
|
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
|
||||||
|
using example_app.Database.Connector;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace example_csharp_app.Migrations
|
||||||
|
{
|
||||||
|
[DbContext(typeof(AppDbContext))]
|
||||||
|
partial class AppDbContextModelSnapshot : ModelSnapshot
|
||||||
|
{
|
||||||
|
protected override void BuildModel(ModelBuilder modelBuilder)
|
||||||
|
{
|
||||||
|
#pragma warning disable 612, 618
|
||||||
|
modelBuilder
|
||||||
|
.HasAnnotation("ProductVersion", "10.0.12")
|
||||||
|
.HasAnnotation("Relational:MaxIdentifierLength", 63);
|
||||||
|
|
||||||
|
NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
|
||||||
|
|
||||||
|
modelBuilder.Entity("example_csharp_app.Dto.BookDto", b =>
|
||||||
|
{
|
||||||
|
b.Property<Guid>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("uuid");
|
||||||
|
|
||||||
|
b.Property<string>("Author")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.Property<string>("BookIndex")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("xml");
|
||||||
|
|
||||||
|
b.Property<string>("Title")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.Property<DateTime>("YearPublished")
|
||||||
|
.HasColumnType("timestamp with time zone");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.ToTable("Books", (string)null);
|
||||||
|
});
|
||||||
|
#pragma warning restore 612, 618
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
using System;
|
||||||
|
using example_csharp_app.Dto;
|
||||||
|
using example_csharp_app.Models.Model;
|
||||||
|
|
||||||
|
namespace example_csharp_app.Models.Mapper;
|
||||||
|
|
||||||
|
public static class BookMapper
|
||||||
|
{
|
||||||
|
public static BookModel MapToModel(this BookDto dto)
|
||||||
|
{
|
||||||
|
return new BookModel()
|
||||||
|
{
|
||||||
|
Author = dto.Author,
|
||||||
|
BookIndex = dto.BookIndex,
|
||||||
|
Id = dto.Id,
|
||||||
|
Title = dto.Title,
|
||||||
|
YearPublished = dto.YearPublished
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public static BookDto MapToDto(this BookCreateModel model)
|
||||||
|
{
|
||||||
|
return new BookDto()
|
||||||
|
{
|
||||||
|
Id = Guid.NewGuid(),
|
||||||
|
Author = model.Author,
|
||||||
|
BookIndex = model.BookIndex,
|
||||||
|
Title = model.Title,
|
||||||
|
YearPublished = model.YearPublished
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
using System;
|
||||||
|
using example_csharp_app.Models.Model;
|
||||||
|
using example_csharp_app.Models.Request;
|
||||||
|
|
||||||
|
namespace example_csharp_app.Models.Mapper;
|
||||||
|
|
||||||
|
public static class RequestMapper
|
||||||
|
{
|
||||||
|
public static BookCreateModel MapToModel(this BookCreateRequest request)
|
||||||
|
{
|
||||||
|
return new BookCreateModel()
|
||||||
|
{
|
||||||
|
Author = request.Author,
|
||||||
|
BookIndex = request.BookIndex.Trim(),
|
||||||
|
Title = request.Title,
|
||||||
|
YearPublished = request.YearPublished.Date.ToUniversalTime()
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public static BookUpdateModel MapToModel(this BookUpdateRequest request)
|
||||||
|
{
|
||||||
|
return new BookUpdateModel()
|
||||||
|
{
|
||||||
|
Author = request.Author,
|
||||||
|
BookIndex = request.BookIndex.Trim(),
|
||||||
|
Title = request.Title,
|
||||||
|
YearPublished = request.YearPublished
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
namespace example_csharp_app.Models.Model;
|
||||||
|
|
||||||
|
public class BookCreateModel
|
||||||
|
{
|
||||||
|
public string Title { set; get; } = string.Empty;
|
||||||
|
public string Author { set; get; } = string.Empty;
|
||||||
|
public DateTime YearPublished { set; get; }
|
||||||
|
public string BookIndex { set; get; } = string.Empty;
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
namespace example_csharp_app.Models.Model;
|
||||||
|
|
||||||
|
public class BookModel
|
||||||
|
{
|
||||||
|
public Guid Id { set; get; }
|
||||||
|
public string Title { set; get; } = string.Empty;
|
||||||
|
public string Author { set; get; } = string.Empty;
|
||||||
|
public DateTime YearPublished { set; get; }
|
||||||
|
public string BookIndex { set; get; } = string.Empty;
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
namespace example_csharp_app.Models.Model;
|
||||||
|
|
||||||
|
public class BookUpdateModel
|
||||||
|
{
|
||||||
|
public string Title { set; get; } = string.Empty;
|
||||||
|
public string Author { set; get; } = string.Empty;
|
||||||
|
public DateTime YearPublished { set; get; }
|
||||||
|
public string BookIndex { set; get; } = string.Empty;
|
||||||
|
}
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
using System;
|
||||||
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
using example_csharp_app.Filters;
|
||||||
|
|
||||||
|
namespace example_csharp_app.Models.Request;
|
||||||
|
|
||||||
|
public class BookCreateRequest
|
||||||
|
{
|
||||||
|
[Required]
|
||||||
|
public string Title { set; get; } = string.Empty;
|
||||||
|
[Required]
|
||||||
|
public string Author { set; get; } = string.Empty;
|
||||||
|
[Required]
|
||||||
|
public DateTime YearPublished { set; get; }
|
||||||
|
[Required, Xml]
|
||||||
|
public string BookIndex { set; get; } = string.Empty;
|
||||||
|
}
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
namespace example_csharp_app.Models.Request;
|
||||||
|
|
||||||
|
public class BookFilterRequest
|
||||||
|
{
|
||||||
|
public string? Author { set; get; }
|
||||||
|
public string? Index { set; get; }
|
||||||
|
public string? Title { set; get; }
|
||||||
|
}
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
using System;
|
||||||
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
using example_csharp_app.Filters;
|
||||||
|
|
||||||
|
namespace example_csharp_app.Models.Request;
|
||||||
|
|
||||||
|
public class BookUpdateRequest
|
||||||
|
{
|
||||||
|
[Required]
|
||||||
|
public Guid Id { set; get; } = Guid.Empty;
|
||||||
|
[Required]
|
||||||
|
public string Title { set; get; } = string.Empty;
|
||||||
|
[Required]
|
||||||
|
public string Author { set; get; } = string.Empty;
|
||||||
|
[Required]
|
||||||
|
public DateTime YearPublished { set; get; }
|
||||||
|
[Required, Xml]
|
||||||
|
public string BookIndex { set; get; } = string.Empty;
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
using System;
|
||||||
|
using example_csharp_app.Models.Model;
|
||||||
|
|
||||||
|
namespace example_csharp_app.Models.Response;
|
||||||
|
|
||||||
|
public class BookListResponse
|
||||||
|
{
|
||||||
|
public IReadOnlyCollection<BookModel> Books { set; get; } = [];
|
||||||
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
namespace example_csharp_app.Models.Response;
|
||||||
|
|
||||||
|
public class BookOperationResponse
|
||||||
|
{
|
||||||
|
public bool IsSuccess { set; get; }
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
using System;
|
||||||
|
using example_csharp_app.Models.Model;
|
||||||
|
|
||||||
|
namespace example_csharp_app.Models.Response;
|
||||||
|
|
||||||
|
public class BookSingleResponse
|
||||||
|
{
|
||||||
|
public BookModel? Book { set; get; }
|
||||||
|
}
|
||||||
+91
@@ -0,0 +1,91 @@
|
|||||||
|
|
||||||
|
using example_app.Database.Connector;
|
||||||
|
using example_csharp_app;
|
||||||
|
using example_csharp_app.Aggregators;
|
||||||
|
using example_csharp_app.Interfaces;
|
||||||
|
using example_csharp_app.Repositories;
|
||||||
|
using example_csharp_app.Services;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using Microsoft.Extensions.Configuration;
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
|
||||||
|
|
||||||
|
namespace Program
|
||||||
|
{
|
||||||
|
public static class Program
|
||||||
|
{
|
||||||
|
private static void SetupDi(IServiceCollection services, IConfiguration configuration)
|
||||||
|
{
|
||||||
|
services.AddScoped<IBookAggregator, BookAggregator>();
|
||||||
|
services.AddScoped<IBookService, BookService>();
|
||||||
|
services.AddScoped<IBookRepository, BookRepository>();
|
||||||
|
services.AddDbContext<AppDbContext>(
|
||||||
|
(provider, builder) =>
|
||||||
|
{
|
||||||
|
//var settings = provider.GetRequiredService<IOptions<DbSettings>>(); // эта версия лучше
|
||||||
|
var connectionString = configuration.GetValue<string>("ConnectionStrings:DefaultConnection");
|
||||||
|
builder.UseNpgsql(connectionString);
|
||||||
|
},
|
||||||
|
ServiceLifetime.Transient
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int Main(string[] args)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var configurationBuilder = (new ConfigurationBuilder())
|
||||||
|
.SetBasePath(Environment.CurrentDirectory)
|
||||||
|
.AddJsonFile("appsettings.json", false, true)
|
||||||
|
.AddJsonFile("appsettings.{environmentName}", true, true)
|
||||||
|
.AddEnvironmentVariables()
|
||||||
|
.AddCommandLine(args);
|
||||||
|
|
||||||
|
var builder = WebApplication.CreateBuilder(args);
|
||||||
|
var configuration = configurationBuilder.Build();
|
||||||
|
|
||||||
|
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();
|
||||||
|
app.UseSwaggerUI();
|
||||||
|
//}
|
||||||
|
app.MapControllers();
|
||||||
|
app.UseMiddleware<ErrorLogMiddleware>();
|
||||||
|
//app.UseHttpsRedirection();
|
||||||
|
|
||||||
|
app.Run();
|
||||||
|
|
||||||
|
} catch (Exception)
|
||||||
|
{
|
||||||
|
// log
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
// flush log
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
{
|
||||||
|
"$schema": "https://json.schemastore.org/launchsettings.json",
|
||||||
|
"profiles": {
|
||||||
|
"http": {
|
||||||
|
"commandName": "Project",
|
||||||
|
"dotnetRunMessages": true,
|
||||||
|
"launchBrowser": false,
|
||||||
|
"applicationUrl": "http://localhost:5222",
|
||||||
|
"environmentVariables": {
|
||||||
|
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"https": {
|
||||||
|
"commandName": "Project",
|
||||||
|
"dotnetRunMessages": true,
|
||||||
|
"launchBrowser": false,
|
||||||
|
"applicationUrl": "https://localhost:7189;http://localhost:5222",
|
||||||
|
"environmentVariables": {
|
||||||
|
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,2 +1,95 @@
|
|||||||
# example-csharp-app
|
# 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
|
||||||
|
$$
|
||||||
@@ -0,0 +1,100 @@
|
|||||||
|
using System;
|
||||||
|
using example_app.Database.Connector;
|
||||||
|
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;
|
||||||
|
|
||||||
|
public class BookRepository : IBookRepository
|
||||||
|
{
|
||||||
|
private readonly AppDbContext _dbContext;
|
||||||
|
|
||||||
|
public BookRepository(AppDbContext dbContext)
|
||||||
|
{
|
||||||
|
_dbContext = dbContext;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<int> AddBookAsync(BookDto dto)
|
||||||
|
{
|
||||||
|
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.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)
|
||||||
|
{
|
||||||
|
|
||||||
|
var books = await _dbContext.Books.FromSqlInterpolated($"SELECT * FROM \"sp_SearchBooks\" ({request.Title}, {request.Author}, {request.Index})").ToListAsync();
|
||||||
|
return books;
|
||||||
|
|
||||||
|
// 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)
|
||||||
|
{
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,61 @@
|
|||||||
|
using System;
|
||||||
|
using example_csharp_app.Dto;
|
||||||
|
using example_csharp_app.Interfaces;
|
||||||
|
using example_csharp_app.Models.Mapper;
|
||||||
|
using example_csharp_app.Models.Model;
|
||||||
|
using example_csharp_app.Models.Request;
|
||||||
|
|
||||||
|
namespace example_csharp_app.Services;
|
||||||
|
|
||||||
|
public class BookService : IBookService
|
||||||
|
{
|
||||||
|
private readonly IBookRepository _bookRepository;
|
||||||
|
|
||||||
|
public BookService(IBookRepository bookRepository)
|
||||||
|
{
|
||||||
|
_bookRepository = bookRepository;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<int> AddBookAsync(BookCreateModel model)
|
||||||
|
{
|
||||||
|
var dto = model.MapToDto();
|
||||||
|
var result = await _bookRepository.AddBookAsync(dto);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<BookModel?> GetBookAsync(Guid bookId)
|
||||||
|
{
|
||||||
|
var result = await _bookRepository.GetBookAsync(bookId, enableEdit: false);
|
||||||
|
return result?.MapToModel();
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<IReadOnlyCollection<BookModel>> GetBooksAsync(BookFilterRequest request)
|
||||||
|
{
|
||||||
|
var result = await _bookRepository.GetBookListAsync(request);
|
||||||
|
return result.Select(x => x.MapToModel()).ToList();
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<int> RemoveBookAsync(Guid bookId)
|
||||||
|
{
|
||||||
|
var book = await GetBookForEdit(bookId);
|
||||||
|
return await _bookRepository.RemoveBookAsync(book);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<int> UpdateBookAsync(Guid bookId, BookUpdateModel model)
|
||||||
|
{
|
||||||
|
var book = await GetBookForEdit(bookId);
|
||||||
|
|
||||||
|
book.Author = model.Author;
|
||||||
|
book.BookIndex = model.BookIndex;
|
||||||
|
book.Title = model.Title;
|
||||||
|
book.YearPublished = model.YearPublished;
|
||||||
|
|
||||||
|
return await _bookRepository.UpdateBookAsync(book);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task<BookDto> GetBookForEdit(Guid bookId)
|
||||||
|
{
|
||||||
|
var book = await _bookRepository.GetBookAsync(bookId);
|
||||||
|
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}"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk.Web">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFramework>net10.0</TargetFramework>
|
||||||
|
<Nullable>enable</Nullable>
|
||||||
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
|
<RootNamespace>example_csharp_app</RootNamespace>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="10.0.12" />
|
||||||
|
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="10.0.12" />
|
||||||
|
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="10.0.3" />
|
||||||
|
<PackageReference Include="Swashbuckle.AspNetCore" Version="10.2.3" />
|
||||||
|
<PackageReference Include="Swashbuckle.AspNetCore.SwaggerUI" Version="10.2.3" />
|
||||||
|
<PackageReference Include="Microsoft.Extensions.Configuration" Version="10.0.12" />
|
||||||
|
<PackageReference Include="Microsoft.Extensions.Configuration.Binder" Version="10.0.12" />
|
||||||
|
<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