├── .vs
├── Demo
│ ├── FileContentIndex
│ │ └── read.lock
│ ├── v17
│ │ ├── .suo
│ │ └── .futdcache.v1
│ └── DesignTimeBuild
│ │ └── .dtbcache.v2
└── ProjectEvaluation
│ ├── demo.metadata.v1
│ └── demo.projects.v1
├── Program.cs
├── README.md
├── Startup.cs
├── settings.xml
├── settings.json
├── settings.yml
├── .gitignore
├── appsettings.Development.json
├── Models
├── PublishersAuthors.cs
├── Mapper.cs
├── AuthorDTO.cs
├── Publisher.cs
├── Book.cs
├── Author.cs
└── BookDetails.cs
├── appsettings.Production.json
├── .dockerignore
├── GraphQl
├── Subscription.cs
├── Mutation.cs
└── Query.cs
├── Configurations
├── DB
│ ├── DBConfigurationSource.cs
│ ├── DBConfigurationExtantions.cs
│ ├── DBConfigurationProvider.cs
│ └── DBConfigurationContext.cs
└── Dynamic
│ └── DynamicValueConfiguration.cs
├── Mappers
├── Register.cs
├── IAuthorMapper.cs
└── AuthorMapper.g.cs
├── appsettings.json
├── Demo.csproj.user
├── Properties
└── launchSettings.json
├── Dockerfile
├── Health
└── CustumHealth.cs
├── Filters
└── ExceptionFilter.cs
├── GlobalSuppressions.cs
├── LICENSE.md
├── HttpHandler
├── HttpTrackerHandler.cs
└── HttpRequestInterceptor.cs
├── DBContext
├── DemoRepository.cs
└── DemoContext.cs
├── BackgroundService
├── BookAVG.cs
└── TCPServer.cs
├── Controllers
├── DTOController.cs
├── PublisherController.cs
└── AuthorsController.cs
├── LongPollingQuery.cs
├── Demo.csproj
├── nlog.config
├── Migrations
├── 20211106063701_start.cs
├── DemoContextModelSnapshot.cs
└── 20211106063701_start.Designer.cs
├── Demo.xml
└── Client
└── DemoClient.cs
/.vs/Demo/FileContentIndex/read.lock:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/Program.cs:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AlexanderZhelnin/DEMO/HEAD/Program.cs
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AlexanderZhelnin/DEMO/HEAD/README.md
--------------------------------------------------------------------------------
/Startup.cs:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AlexanderZhelnin/DEMO/HEAD/Startup.cs
--------------------------------------------------------------------------------
/settings.xml:
--------------------------------------------------------------------------------
1 |
2 | Ключ XML1
3 |
--------------------------------------------------------------------------------
/settings.json:
--------------------------------------------------------------------------------
1 | {
2 | "settings": {
3 | "JSONKey1": "Значение JSON1"
4 | }
5 | }
--------------------------------------------------------------------------------
/.vs/Demo/v17/.suo:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AlexanderZhelnin/DEMO/HEAD/.vs/Demo/v17/.suo
--------------------------------------------------------------------------------
/settings.yml:
--------------------------------------------------------------------------------
1 | yamlkey1: Значение yaml1
2 | yamlkey2: Значение yaml2
3 | yamlkey3:
4 | v1: Значение yaml3
--------------------------------------------------------------------------------
/.vs/Demo/v17/.futdcache.v1:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AlexanderZhelnin/DEMO/HEAD/.vs/Demo/v17/.futdcache.v1
--------------------------------------------------------------------------------
/.vs/Demo/DesignTimeBuild/.dtbcache.v2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AlexanderZhelnin/DEMO/HEAD/.vs/Demo/DesignTimeBuild/.dtbcache.v2
--------------------------------------------------------------------------------
/.vs/ProjectEvaluation/demo.metadata.v1:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AlexanderZhelnin/DEMO/HEAD/.vs/ProjectEvaluation/demo.metadata.v1
--------------------------------------------------------------------------------
/.vs/ProjectEvaluation/demo.projects.v1:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AlexanderZhelnin/DEMO/HEAD/.vs/ProjectEvaluation/demo.projects.v1
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # See http://help.github.com/ignore-files/ for more about ignoring files.
2 |
3 | # Compiled output
4 | /bin
5 | /obj
6 |
7 |
8 | # Visual Studio
9 | .vs/*
--------------------------------------------------------------------------------
/appsettings.Development.json:
--------------------------------------------------------------------------------
1 | {
2 | "Logging": {
3 | "LogLevel": {
4 | "Default": "Information",
5 | "Microsoft": "Warning",
6 | "Microsoft.Hosting.Lifetime": "Information"
7 | }
8 | },
9 | "key1": "value5"
10 | }
11 |
--------------------------------------------------------------------------------
/Models/PublishersAuthors.cs:
--------------------------------------------------------------------------------
1 | namespace Demo.Models;
2 |
3 | public class PublishersAuthors
4 | {
5 | public int AuthorId { get; set; }
6 | public Author Author { get; set; }
7 | public int PublisherId { get; set; }
8 | public Publisher Publisher { get; set; }
9 | }
10 |
--------------------------------------------------------------------------------
/appsettings.Production.json:
--------------------------------------------------------------------------------
1 | {
2 | "Logging": {
3 | "LogLevel": {
4 | "Default": "Information",
5 | "Microsoft": "Warning",
6 | "Microsoft.Hosting.Lifetime": "Information"
7 | }
8 | },
9 | "ConnectionStrings": {
10 | "DefaultConnection": "Server=127.0.0.1;Port=5432;Database=authors;User Id=test;Password=test;"
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/Models/Mapper.cs:
--------------------------------------------------------------------------------
1 | namespace Demo.Models
2 | {
3 | public static class Mapper
4 | {
5 | ///
6 | /// Ручное сопоставление
7 | ///
8 | //public static AuthorDTO MapTo(this Author author) => new()
9 | //{
10 | // Id = author.Id,
11 | // Name = author.Name
12 | //};
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/Models/AuthorDTO.cs:
--------------------------------------------------------------------------------
1 | using GreenDonut;
2 |
3 | namespace Demo.Models;
4 |
5 | ///
6 | /// Автор для передачи данных
7 | ///
8 | public partial class AuthorDTO
9 | {
10 | /** Уникальный идентификатор */
11 | public int Id { get; set; }
12 |
13 | /** Имя автора */
14 | public string Name { get; set; }
15 |
16 | public string Name1 { get; set; }
17 | }
--------------------------------------------------------------------------------
/.dockerignore:
--------------------------------------------------------------------------------
1 | **/.classpath
2 | **/.dockerignore
3 | **/.env
4 | **/.git
5 | **/.gitignore
6 | **/.project
7 | **/.settings
8 | **/.toolstarget
9 | **/.vs
10 | **/.vscode
11 | **/*.*proj.user
12 | **/*.dbmdl
13 | **/*.jfm
14 | **/azds.yaml
15 | **/bin
16 | **/charts
17 | **/docker-compose*
18 | **/Dockerfile*
19 | **/node_modules
20 | **/npm-debug.log
21 | **/obj
22 | **/secrets.dev.yaml
23 | **/values.dev.yaml
24 | LICENSE
25 | README.md
--------------------------------------------------------------------------------
/GraphQl/Subscription.cs:
--------------------------------------------------------------------------------
1 | using Demo.Models;
2 | using HotChocolate;
3 | using HotChocolate.Types;
4 |
5 | namespace Demo.GraphQl;
6 |
7 | ///
8 | /// Подписки
9 | ///
10 | public class Subscription
11 | {
12 | ///
13 | /// Добавлен новый автор
14 | ///
15 | ///
16 | /// Автор
17 | [Subscribe]
18 | public Author OnAuthorChanged([EventMessage] Author author)
19 | => author;
20 | }
21 |
--------------------------------------------------------------------------------
/Configurations/DB/DBConfigurationSource.cs:
--------------------------------------------------------------------------------
1 | namespace Demo.Configurations.DB;
2 |
3 |
4 | public class DBConfigurationSource : IConfigurationSource
5 | {
6 | private readonly string _connectionString;
7 |
8 |
9 | /** Строка подключения к базе данных */
10 | public DBConfigurationSource(string connectionString)
11 | {
12 | _connectionString = connectionString;
13 | }
14 |
15 | public IConfigurationProvider Build(IConfigurationBuilder builder) =>
16 | new DBConfigurationProvider(_connectionString);
17 | }
--------------------------------------------------------------------------------
/Mappers/Register.cs:
--------------------------------------------------------------------------------
1 | using Demo.Models;
2 | using Mapster;
3 |
4 | namespace Demo.Mappers
5 | {
6 | ///
7 | /// Регистрация сопоставления
8 | ///
9 | public class RegisterMapper : IRegister
10 | {
11 | /** */
12 | public void Register(TypeAdapterConfig config)
13 | {
14 | config.NewConfig()
15 | .Map(adto => adto.Name1, a => a.Name + "1")
16 | .RequireDestinationMemberSource(true);
17 | }
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/appsettings.json:
--------------------------------------------------------------------------------
1 | {
2 | "Logging": {
3 | "LogLevel": {
4 | "Default": "Information",
5 | "Microsoft": "Warning",
6 | "Microsoft.Hosting.Lifetime": "Information"
7 | }
8 | },
9 | "ConnectionStrings": {
10 | "DefaultConnection": "Server=127.0.0.1;Port=5432;Database=authors;User Id=test;Password=test;"
11 | },
12 | "AllowedHosts": "*",
13 | "Authentication": {
14 | "OAUTH_PATH": "http://localhost:8080/auth/realms/SAT/",
15 | "OAUTH_CLIENT_ID": "DEMO"
16 | },
17 | "key1": "value4"
18 | }
19 |
--------------------------------------------------------------------------------
/Mappers/IAuthorMapper.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Linq.Expressions;
3 | using Demo.Models;
4 | using Mapster;
5 |
6 | namespace Demo;
7 |
8 | ///
9 | /// Интерфейс для генерации сопоставления
10 | ///
11 | [Mapper]
12 | public interface IAuthorMapper
13 | {
14 | Expression> AuthorProjection { get; }
15 | AuthorDTO MapTo(Author author);
16 | AuthorDTO MapTo(Author author, AuthorDTO authordto);
17 |
18 | //Author MapTo(AuthorDTO author);
19 | //Author MapTo(AuthorDTO authordto, Author author);
20 |
21 | }
22 |
--------------------------------------------------------------------------------
/Configurations/DB/DBConfigurationExtantions.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.Data.Sqlite;
2 |
3 | namespace Demo.Configurations.DB;
4 |
5 | /** Для удобной регистрации */
6 | public static class DBConfigurationExtantions
7 | {
8 | public static IConfigurationBuilder AddDB(this IConfigurationBuilder builder)
9 | {
10 |
11 | return builder.Add(
12 | new DBConfigurationSource(
13 | new SqliteConnectionStringBuilder
14 | {
15 | DataSource = System.IO.Path.Combine(AppContext.BaseDirectory, "config.db")
16 | }.ToString()));
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/Demo.csproj.user:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | MvcControllerEmptyScaffolder
5 | root/Common/MVC/Controller
6 | Demo
7 |
8 |
9 | ProjectDebugger
10 |
11 |
--------------------------------------------------------------------------------
/Properties/launchSettings.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "http://json.schemastore.org/launchsettings.json",
3 | "profiles": {
4 | "Demo": {
5 | "commandName": "Project",
6 | "dotnetRunMessages": "true",
7 | "launchBrowser": true,
8 | "launchUrl": "swagger",
9 | "applicationUrl": "http://localhost:5000",
10 | "environmentVariables": {
11 | "ASPNETCORE_ENVIRONMENT": "Development"
12 | }
13 | },
14 | "Docker": {
15 | "commandName": "Docker",
16 | "launchBrowser": true,
17 | "launchUrl": "{Scheme}://{ServiceHost}:{ServicePort}/graphql",
18 | "publishAllPorts": true,
19 | "useSSL": true
20 | }
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/Dockerfile:
--------------------------------------------------------------------------------
1 | #See https://aka.ms/containerfastmode to understand how Visual Studio uses this Dockerfile to build your images for faster debugging.
2 |
3 | FROM mcr.microsoft.com/dotnet/aspnet:6.0.4-alpine3.15-amd64 AS base
4 | WORKDIR /app
5 | EXPOSE 80
6 | EXPOSE 443
7 |
8 | FROM mcr.microsoft.com/dotnet/sdk:6.0 AS build
9 | WORKDIR /src
10 | COPY "Demo.csproj" "Demo.csproj"
11 | RUN dotnet restore "Demo.csproj"
12 | COPY . .
13 | WORKDIR "/src"
14 | RUN dotnet build "Demo.csproj" -c Release -o /app/build
15 |
16 | FROM build AS publish
17 | RUN dotnet publish "Demo.csproj" -c Release -o /app/publish
18 |
19 | FROM base AS final
20 | WORKDIR /app
21 | COPY --from=publish /app/publish .
22 | ENTRYPOINT ["dotnet", "Demo.dll"]
--------------------------------------------------------------------------------
/Configurations/Dynamic/DynamicValueConfiguration.cs:
--------------------------------------------------------------------------------
1 | namespace Demo.Configurations.Dynamic;
2 |
3 | class DynamicValueConfigurationSource : IConfigurationSource
4 | {
5 | public IConfigurationProvider Build(IConfigurationBuilder builder) =>
6 | new DynamicValueConfigurationProvider();
7 | }
8 |
9 | class DynamicValueConfigurationProvider : ConfigurationProvider
10 | {
11 | private int _count = 0;
12 |
13 | public override void Load()
14 | {
15 | Data = new Dictionary { { "DynamicKey1", "" } };
16 | }
17 |
18 | public override bool TryGet(string key, out string value)
19 | {
20 | if (!Data.TryGetValue(key, out value)) return false;
21 |
22 | value = $"динамическая конфигурация {++_count}";
23 | return true;
24 | }
25 |
26 | }
27 |
--------------------------------------------------------------------------------
/Models/Publisher.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 |
3 | namespace Demo.Models;
4 |
5 | ///
6 | /// Издатель
7 | ///
8 | public class Publisher
9 | {
10 | public Publisher()
11 | {
12 |
13 | }
14 | /** Уникальный идентификатор */
15 | public int Id { get; set; }
16 |
17 | /** Название издателя */
18 | public string Name { get; set; }
19 |
20 | /** Авторы */
21 | [Newtonsoft.Json.JsonIgnore]
22 | [System.Text.Json.Serialization.JsonIgnore]
23 | public ICollection Authors { get; set; } = new List();
24 |
25 | /** Связь многие ко многим Издатели/Авторы */
26 | [Newtonsoft.Json.JsonIgnore]
27 | [System.Text.Json.Serialization.JsonIgnore]
28 | public List PublishersAuthors { get; set; } = new List();
29 |
30 | }
31 |
--------------------------------------------------------------------------------
/Health/CustumHealth.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.Extensions.Diagnostics.HealthChecks;
2 | using System.Threading;
3 | using System.Threading.Tasks;
4 |
5 | namespace Demo.Health;
6 |
7 | ///
8 | /// Кастомная провека работоспособности
9 | ///
10 | public class CustumHealthCheck : IHealthCheck
11 | {
12 | ///
13 | /// Фукнция проверки
14 | ///
15 | ///
16 | ///
17 | ///
18 | public Task CheckHealthAsync(
19 | HealthCheckContext context,
20 | CancellationToken cancellationToken = new CancellationToken())
21 | {
22 |
23 | return Task.FromResult(HealthCheckResult.Healthy("Моя проверка работает"));
24 |
25 | // return Task.FromResult(HealthCheckResult.Unhealthy("Моя проверка не работает"));
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/Filters/ExceptionFilter.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.AspNetCore.Mvc;
2 | using Microsoft.AspNetCore.Mvc.Filters;
3 | using System;
4 | using System.Collections.Generic;
5 | using System.Linq;
6 | using System.Threading.Tasks;
7 |
8 | namespace Demo.Filters;
9 |
10 | ///
11 | /// Фильтр ошибок
12 | ///
13 | public class ExceptionFilter : IExceptionFilter
14 | {
15 | ///
16 | /// Обработчик фильтра ошибок
17 | ///
18 | ///
19 | public void OnException(ExceptionContext context)
20 | {
21 | switch (context.Exception)
22 | {
23 | case ArgumentException e:
24 | context.Result = new NotFoundObjectResult(e.Message);
25 | break;
26 | case InvalidOperationException e:
27 | context.Result = new BadRequestObjectResult(e.Message);
28 | break;
29 | }
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/Configurations/DB/DBConfigurationProvider.cs:
--------------------------------------------------------------------------------
1 |
2 | using Microsoft.Data.Sqlite;
3 |
4 | namespace Demo.Configurations.DB
5 | {
6 | public class DBConfigurationProvider : ConfigurationProvider
7 | {
8 | /** Строка подключения к базе данных */
9 | private readonly string _connectionString;
10 |
11 | /** Конструктор */
12 | public DBConfigurationProvider(string connectionString)
13 | {
14 | _connectionString = connectionString;
15 | }
16 |
17 | /** Загрузка конфигурации из базы данных */
18 | public override void Load()
19 | {
20 |
21 | using var dbContext = new DBConfigurationContext(_connectionString);
22 | dbContext.Database.EnsureCreated();
23 |
24 | Data = dbContext.Settings.ToDictionary(
25 | s => $"DBConfiguration:{s.Key}",
26 | s => s.Value,
27 | StringComparer.OrdinalIgnoreCase);
28 | }
29 | }
30 | }
--------------------------------------------------------------------------------
/GlobalSuppressions.cs:
--------------------------------------------------------------------------------
1 | // This file is used by Code Analysis to maintain SuppressMessage
2 | // attributes that are applied to this project.
3 | // Project-level suppressions either have no target or are given
4 | // a specific target and scoped to a namespace, type, member, etc.
5 |
6 | using System.Diagnostics.CodeAnalysis;
7 |
8 | [assembly: SuppressMessage("Usage", "CA2254:Шаблон должен быть статическим выражением", Justification = "<Ожидание>", Scope = "member", Target = "~M:Demo.TCPServer.ExecuteAsync(System.Threading.CancellationToken)~System.Threading.Tasks.Task")]
9 | [assembly: SuppressMessage("Reliability", "CA2016:Перенаправьте параметр \"CancellationToken\" в методы", Justification = "<Ожидание>", Scope = "member", Target = "~M:Demo.TCPServer.ExecuteAsync(System.Threading.CancellationToken)~System.Threading.Tasks.Task")]
10 | [assembly: SuppressMessage("Usage", "CA2254:Шаблон должен быть статическим выражением", Justification = "<Ожидание>", Scope = "member", Target = "~M:Demo.BookAVG.ExecuteAsync(System.Threading.CancellationToken)~System.Threading.Tasks.Task")]
11 |
--------------------------------------------------------------------------------
/Models/Book.cs:
--------------------------------------------------------------------------------
1 | using Newtonsoft.Json;
2 | using System;
3 | using System.Collections.Generic;
4 | using System.Linq;
5 | using System.Threading.Tasks;
6 |
7 | namespace Demo.Models;
8 |
9 | /** Книга */
10 | public class Book
11 | {
12 | /** Уникальный идентификатор */
13 | public int Id { get; set; }
14 |
15 | /** Название книги */
16 | public string Title { get; set; }
17 |
18 | /** Описание книги */
19 | public string Description { get; set; }
20 |
21 | /** Обложка */
22 | public string ImageUrl { get; set; }
23 |
24 | /** Уникльный идентификатор автора */
25 | public int AuthorId { get; set; }
26 | /** Автор */
27 | [Newtonsoft.Json.JsonIgnore]
28 | [System.Text.Json.Serialization.JsonIgnore]
29 | public Author Author { get; set; }
30 |
31 | /** Идентификатор ISBN10 */
32 | public string ISBN_10 { get; set; }
33 | /** Идентификатор ISBN13 */
34 | public string ISBN_13 { get; set; }
35 |
36 | /** Дополнительная информация по книге */
37 | public BookDetails Details { get; set; }
38 |
39 | }
40 |
--------------------------------------------------------------------------------
/LICENSE.md:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2022 AlexanderZhelnin
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/Mappers/AuthorMapper.g.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Linq.Expressions;
3 | using Demo;
4 | using Demo.Models;
5 |
6 | namespace Demo
7 | {
8 | public partial class AuthorMapper : IAuthorMapper
9 | {
10 | public Expression> AuthorProjection => p1 => new AuthorDTO()
11 | {
12 | Id = p1.Id,
13 | Name = p1.Name,
14 | Name1 = p1.Name + "1"
15 | };
16 | public AuthorDTO MapTo(Author p2)
17 | {
18 | return p2 == null ? null : new AuthorDTO()
19 | {
20 | Id = p2.Id,
21 | Name = p2.Name,
22 | Name1 = p2.Name + "1"
23 | };
24 | }
25 | public AuthorDTO MapTo(Author p3, AuthorDTO p4)
26 | {
27 | if (p3 == null)
28 | {
29 | return null;
30 | }
31 | AuthorDTO result = p4 ?? new AuthorDTO();
32 |
33 | result.Id = p3.Id;
34 | result.Name = p3.Name;
35 | result.Name1 = p3.Name + "1";
36 | return result;
37 |
38 | }
39 | }
40 | }
--------------------------------------------------------------------------------
/HttpHandler/HttpTrackerHandler.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.AspNetCore.Http;
2 | using System.Linq;
3 | using System.Net.Http;
4 | using System.Threading;
5 | using System.Threading.Tasks;
6 |
7 | namespace Demo.HttpHandler;
8 |
9 | ///
10 | /// Передача аргументов запроса в вызов
11 | ///
12 | public class HttpTrackerHandler : DelegatingHandler
13 | {
14 | private readonly IHttpContextAccessor _context;
15 |
16 | /** Конструктор */
17 | public HttpTrackerHandler(IHttpContextAccessor context)
18 | {
19 | _context = context;
20 | }
21 |
22 | /** Асинхронный метод вызова */
23 | protected override Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
24 | {
25 | if (_context.HttpContext.Request.Headers.TryGetValue("Authorization", out var jwt))
26 | request.Headers.Add("Authorization", jwt.FirstOrDefault());
27 |
28 | if (_context.HttpContext.Request.Headers.TryGetValue("TraceId", out var traceid))
29 | request.Headers.Add("TraceId", traceid.FirstOrDefault());
30 |
31 | return base.SendAsync(request, cancellationToken);
32 | }
33 |
34 | }
35 |
--------------------------------------------------------------------------------
/Models/Author.cs:
--------------------------------------------------------------------------------
1 | using HotChocolate;
2 | using HotChocolate.AspNetCore.Authorization;
3 | using Mapster;
4 | using System;
5 | using System.Collections.Generic;
6 | using System.ComponentModel;
7 | using System.Linq;
8 | using System.Runtime.Serialization;
9 | using System.Threading.Tasks;
10 | using DefaultValueAttribute = HotChocolate.Types.DefaultValueAttribute;
11 |
12 | namespace Demo.Models;
13 |
14 | ///
15 | /// Автор
16 | ///
17 | public class Author
18 | {
19 | /** Уникальный идентификатор */
20 | public int Id { get; set; }
21 |
22 | /** Имя автора */
23 | [DefaultValue("Вася")]
24 | public string Name { get; set; }
25 |
26 | /** Книги автора */
27 | //[Authorize(Roles = new[] { "admin" })]
28 | public ICollection Books { get; set; } = new List();
29 |
30 | /** Издательства */
31 | public List Publishers { get; set; } = new List();
32 |
33 | /** Связь многие ко многим Издатели/Авторы */
34 | [Newtonsoft.Json.JsonIgnore]
35 | [System.Text.Json.Serialization.JsonIgnore]
36 | [GraphQLIgnore]
37 | public List PublishersAuthors { get; set; } = new List();
38 |
39 | public override string ToString() => $"{Id}:{Name}";
40 | }
41 |
--------------------------------------------------------------------------------
/Configurations/DB/DBConfigurationContext.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.EntityFrameworkCore;
2 |
3 | namespace Demo.Configurations.DB;
4 |
5 | public class Settings
6 | {
7 | public string Key { get; set; }
8 | public string Value { get; set; }
9 | }
10 |
11 | /** Контекст базы данных для конфигурации */
12 | public class DBConfigurationContext: DbContext
13 | {
14 | private readonly string _connectionString;
15 |
16 | public DbSet Settings { get; set; }
17 |
18 | public DBConfigurationContext(string connectionString)
19 | {
20 | _connectionString = connectionString;
21 | }
22 |
23 | protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
24 | {
25 |
26 | optionsBuilder.UseSqlite(_connectionString);
27 | }
28 |
29 | protected override void OnModelCreating(ModelBuilder modelBuilder)
30 | {
31 | modelBuilder.Entity(b =>
32 | {
33 | b.HasKey(a => a.Key);
34 |
35 | b.HasData(
36 | new() { Key = "DBKey1", Value = "DB Значение 1" },
37 | new() { Key = "DBKey2", Value = "DB Значение 2" },
38 | new() { Key = "DBKey3", Value = "DB Значение 3" }
39 |
40 |
41 | );
42 | });
43 | }
44 |
45 | }
46 |
47 |
--------------------------------------------------------------------------------
/DBContext/DemoRepository.cs:
--------------------------------------------------------------------------------
1 | using Demo.Models;
2 | using System;
3 | using System.Linq;
4 |
5 | namespace Demo.DB;
6 |
7 | /** Демо репозиторий */
8 | public class DemoRepository
9 | {
10 | private readonly DemoContext _ctx;
11 |
12 | /** Конструктор */
13 | public DemoRepository(DemoContext ctx)
14 | {
15 | _ctx = ctx;
16 | }
17 |
18 | ///
19 | /// Получить автора по иникальному идентификатору
20 | ///
21 | /// Уникальный идентификатор
22 | ///
23 | public Author Get(int id)
24 | {
25 | var author = _ctx.Authors.FirstOrDefault(x => x.Id == id);
26 | if (author == null) throw new ArgumentException($"Автора с заданным Id: {id} не существует");
27 | return author;
28 | }
29 | ///
30 | /// Обновить автора
31 | ///
32 | /// автор для обновления
33 | public void Update(Author author)
34 | {
35 | var a = Get(author.Id);
36 |
37 | try
38 | {
39 | _ctx.Update(author);
40 | _ctx.SaveChanges();
41 | }
42 | catch
43 | {
44 | throw new InvalidOperationException($"Не удалось обновить автора {author.Id}");
45 | }
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/HttpHandler/HttpRequestInterceptor.cs:
--------------------------------------------------------------------------------
1 | using HotChocolate.AspNetCore;
2 | using HotChocolate.Execution;
3 | using Microsoft.AspNetCore.Http;
4 | using System;
5 | using System.Collections.Generic;
6 | using System.Linq;
7 | using System.Security.Claims;
8 | using System.Threading;
9 | using System.Threading.Tasks;
10 |
11 | namespace Demo.HttpHandler;
12 |
13 | public class HttpRequestInterceptor : DefaultHttpRequestInterceptor
14 | {
15 | public override ValueTask OnCreateAsync(HttpContext context,
16 | IRequestExecutor requestExecutor, IQueryRequestBuilder requestBuilder,
17 | CancellationToken cancellationToken)
18 | {
19 | var identity = new ClaimsIdentity();
20 | var rolesv = context.User.FindFirstValue("realm_access");
21 | if (rolesv != null)
22 | {
23 | dynamic roles = Newtonsoft.Json.Linq.JObject.Parse(rolesv);
24 | foreach (var r in roles.roles)
25 | identity.AddClaim(new Claim(ClaimTypes.Role, r.Value));
26 | }
27 |
28 | var namev = context.User.FindFirstValue("preferred_username");
29 | if (namev != null)
30 | identity.AddClaim(new Claim(ClaimTypes.Name, namev));
31 |
32 | context.User.AddIdentity(identity);
33 |
34 | return base.OnCreateAsync(context, requestExecutor, requestBuilder,
35 | cancellationToken);
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/BackgroundService/BookAVG.cs:
--------------------------------------------------------------------------------
1 | using Demo.DB;
2 | using Demo.Models;
3 | using Microsoft.Extensions.DependencyInjection;
4 | using Microsoft.Extensions.Hosting;
5 | using Microsoft.Extensions.Logging;
6 | using System;
7 | using System.Diagnostics;
8 | using System.Linq;
9 | using System.Threading;
10 | using System.Threading.Tasks;
11 |
12 | namespace Demo;
13 |
14 | /** Фоновый процесс расчёта среднего колличества отзывов */
15 | public class BookAVG : BackgroundService
16 | {
17 | private readonly ILogger _logger;
18 | private readonly DemoContext _ctx;
19 | private readonly TimeSpan _interval = TimeSpan.FromMinutes(5);
20 |
21 | ///
22 | /// Конструктор
23 | ///
24 | ///
25 | /// контекст базы данных
26 | public BookAVG(ILogger logger, DemoContext ctx)
27 | {
28 | _logger = logger;
29 | _ctx = ctx;
30 | }
31 |
32 | ///
33 | /// Выполнение фонового процесса
34 | ///
35 | /// токен завершения
36 | ///
37 | protected override async Task ExecuteAsync(CancellationToken stoppingToken)
38 | {
39 | while (!stoppingToken.IsCancellationRequested)
40 | {
41 | _logger.LogDebug(_ctx.BookDetails.Average(db => db.Reviews).ToString());
42 |
43 | await Task.Delay(_interval, stoppingToken);
44 | }
45 |
46 | }
47 |
48 | }
49 |
50 |
--------------------------------------------------------------------------------
/Controllers/DTOController.cs:
--------------------------------------------------------------------------------
1 | using Demo.DB;
2 | using Demo.Models;
3 | using Mapster;
4 | using MapsterMapper;
5 | using Microsoft.AspNetCore.Http;
6 | using Microsoft.AspNetCore.Mvc;
7 | using Microsoft.EntityFrameworkCore;
8 |
9 | namespace Demo.Controllers;
10 |
11 | /** Пример контроллера для показа работы Mapster */
12 | [Route("api/[controller]")]
13 | [ApiController]
14 | public class DTOController : ControllerBase
15 | {
16 | private readonly IMapper _mapper;
17 | private readonly DemoContext _ctx;
18 |
19 | ///
20 | /// Конструктор
21 | ///
22 | /// мапинг
23 | /// контекст базы данных
24 | public DTOController(
25 | IMapper mapper,
26 | DemoContext ctx
27 | )
28 | {
29 | _mapper = mapper;
30 | _ctx = ctx;
31 | }
32 |
33 | ///
34 | /// Получить авторов DTO
35 | ///
36 | ///
37 | [HttpGet("", Name = nameof(GetDTOAuthors))]
38 | public IQueryable GetDTOAuthors()
39 | {
40 | return _mapper
41 | .From(_ctx.Authors)
42 | .ProjectToType();
43 | }
44 |
45 |
46 | ///
47 | /// Получить автора по уникальному идентификатору
48 | ///
49 | ///
50 | ///
51 | /// GET api/Authors/1
52 | [HttpGet("{id}", Name = nameof(GetAuthorDTOById))]
53 | [ProducesResponseType(StatusCodes.Status200OK)]
54 | [ProducesDefaultResponseType]
55 | public ActionResult GetAuthorDTOById(int id)
56 | {
57 | var result = _ctx.Authors.FirstOrDefault(a => a.Id == id);
58 |
59 | if (result == null) return NotFound();
60 |
61 | return result.Adapt();
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/Models/BookDetails.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace Demo.Models;
4 |
5 | /** Статус книги */
6 | public enum StatusEnum
7 | {
8 | /** Книга завершена */
9 | complite = 0,
10 | /** Книга пишется */
11 | write = 1,
12 | }
13 |
14 | /** Язык */
15 | public enum LanguageEnum
16 | {
17 | /** Русский */
18 | ru = 0,
19 | /** Английский */
20 | en = 1,
21 | }
22 |
23 | /** Жанр книги */
24 | public enum GenreEnum
25 | {
26 | /** Фантастика */
27 | fantastic,
28 | /** Фантази */
29 | fantasy,
30 | /** litRPG */
31 | litrpg,
32 | /** Научная литература */
33 | scientific,
34 | /** Историческая литература */
35 | historical
36 | }
37 |
38 |
39 | /** Дополнительная информация по книге */
40 | public class BookDetails
41 | {
42 |
43 | /** Уникальный идентификатор */
44 | public int BookId { get; set; }
45 | /** Книга */
46 | public Book Book { get; set; }
47 |
48 | /** Ранк книги */
49 | public double Rank { get; set; }
50 | /** Статус книги */
51 | public StatusEnum Status { get; set; } = StatusEnum.complite;
52 |
53 | /** Стоимость книги в мягком переплёте */
54 | public decimal PeperbackCost { get; set; }
55 |
56 | /** Стоимость книги в твёрдом переплёте */
57 | public decimal HardcoverCost { get; set; }
58 | /** Дата выпуска */
59 | public DateTime Year { get; set; }
60 |
61 | /** Редакция */
62 | public string Editor { get; set; }
63 |
64 | /** иллюстратор */
65 | public string Illustrator { get; set; }
66 |
67 | /** Колличество страниц */
68 | public int PageCount { get; set; }
69 |
70 | /** Язык */
71 | public LanguageEnum Language { get; set; }
72 |
73 | /** Возростные ограничения */
74 | public byte ReadingAge { get; set; }
75 |
76 | /** Жанр */
77 | public GenreEnum Genre { get; set; }
78 | /** Отзывы */
79 | public int Reviews { get; set; }
80 |
81 | }
82 |
--------------------------------------------------------------------------------
/BackgroundService/TCPServer.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.Extensions.Hosting;
2 | using Microsoft.Extensions.Logging;
3 | using System;
4 | using System.Net;
5 | using System.Net.Sockets;
6 | using System.Text;
7 | using System.Threading;
8 | using System.Threading.Tasks;
9 |
10 | namespace Demo;
11 |
12 | /** Сервер TCP */
13 | public class TCPServer : BackgroundService
14 | {
15 | private readonly ILogger _logger;
16 | private const int port = 8888;
17 |
18 | ///
19 | /// Конструктор
20 | ///
21 | ///
22 | public TCPServer(ILogger logger)
23 | {
24 | _logger = logger;
25 |
26 | }
27 |
28 | ///
29 | /// Выполнение фонового процесса
30 | ///
31 | ///
32 | protected override async Task ExecuteAsync(CancellationToken stoppingToken)
33 | {
34 | TcpListener server = null;
35 | try
36 | {
37 | server = new TcpListener(IPAddress.Any, port);
38 |
39 | // запуск слушателя
40 | server.Start();
41 |
42 | while (!stoppingToken.IsCancellationRequested)
43 | {
44 | _logger.LogTrace("Ожидание подключений... ");
45 |
46 | // получаем входящее подключение
47 | using var client = server.AcceptTcpClient();
48 | _logger.LogTrace("Подключен клиент. Выполнение запроса...");
49 |
50 | // получаем сетевой поток для чтения и записи
51 | using var stream = client.GetStream();
52 |
53 | // сообщение для отправки клиенту
54 | var response = "Привет мир";
55 | // преобразуем сообщение в массив байтов
56 | var data = Encoding.UTF8.GetBytes(response);
57 |
58 | // отправка сообщения
59 | await stream.WriteAsync(new ReadOnlyMemory(data), stoppingToken);
60 | _logger.LogTrace($"Отправлено сообщение: {response}");
61 |
62 | }
63 | }
64 | catch (Exception e)
65 | {
66 | _logger.LogError(e.Message);
67 | }
68 | finally
69 | {
70 | server?.Stop();
71 | }
72 | }
73 |
74 | }
75 |
76 |
77 |
--------------------------------------------------------------------------------
/LongPollingQuery.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Reactive.Linq;
5 | using System.Text.Json.Serialization;
6 | using System.Threading.Tasks;
7 |
8 | namespace Demo;
9 |
10 | #region LongPollingValue
11 | ///
12 | /// Значение очереди
13 | ///
14 | ///
15 | public class LongPollingValue
16 | {
17 | ///
18 | /// Текущие занчение
19 | ///
20 | public K Value { get; set; }
21 | ///
22 | /// Маркер по котором считываем новые значения
23 | ///
24 | public DateTime Marker { get; set; }
25 |
26 | ///
27 | /// Следующий элемент в связанном списке
28 | ///
29 | [JsonIgnore]
30 | public LongPollingValue Next { get; set; }
31 | }
32 | #endregion
33 |
34 | ///
35 | /// Очередь реализующаю паттерн LongPolling ( "Длительный Опрос" )
36 | ///
37 | /// generetic тип очереди
38 | public class LongPollingQuery
39 | {
40 | #region Свойства
41 | private LongPollingValue _first { get; set; }
42 | private LongPollingValue _last { get; set; }
43 |
44 | ///
45 | /// Максимальное время удержания запроса, когда нет подходящих данных
46 | ///
47 | public TimeSpan TimeOut { get; set; } = TimeSpan.FromSeconds(10);
48 |
49 | ///
50 | /// Время устаревания данных в очереди
51 | ///
52 | public TimeSpan WatchDogTimeOut { get; set; } = TimeSpan.FromMinutes(5);
53 | #endregion
54 |
55 | #region События
56 | private event Action Added;
57 | #endregion
58 |
59 | #region Конструктор
60 | ///
61 | /// Конструктор
62 | ///
63 | public LongPollingQuery()
64 | {
65 | var clearwatchdog = Observable.FromEvent(h => Added += h, h => Added -= h);
66 |
67 | clearwatchdog
68 | .Sample(TimeSpan.FromMinutes(1))
69 | .Subscribe(_ =>
70 | {
71 | var dt = DateTime.Now - WatchDogTimeOut;
72 | while (_first != null && _first.Marker < dt) _first = _first.Next;
73 | });
74 | }
75 | #endregion
76 |
77 | private readonly object _lock = new();
78 | #region Add
79 | ///
80 | /// Добавление в очередь
81 | ///
82 | public void Add(T value)
83 | {
84 | lock (_lock)
85 | {
86 | var added = new LongPollingValue { Value = value, Marker = DateTime.Now };
87 | if (_first == null)
88 | _first = _last = added;
89 | else
90 | _last = _last.Next = added;
91 | }
92 | Added();
93 | }
94 | #endregion
95 |
96 | #region Read
97 | ///
98 | /// Чтение из очереди
99 | ///
100 | /// маркер после которого происходит чтение данных из очереди
101 | ///
102 | public async IAsyncEnumerable> Read(DateTime marker)
103 | {
104 | await Task.Delay(500);
105 | var dtstart = DateTime.Now;
106 | var fined = false;
107 | do
108 | {
109 | var item = _first;
110 | while (item != null)
111 | {
112 | fined = item.Marker > marker;
113 | if (fined) yield return item;
114 |
115 | item = item.Next;
116 | }
117 | if (!fined) await Task.Delay(500);
118 | }
119 | while (!fined && (DateTime.Now - dtstart) < TimeOut);
120 | }
121 | #endregion
122 | }
123 |
--------------------------------------------------------------------------------
/GraphQl/Mutation.cs:
--------------------------------------------------------------------------------
1 | using Demo.DB;
2 | using Demo.Models;
3 | using HotChocolate;
4 | using HotChocolate.Subscriptions;
5 | using System;
6 | using System.Linq;
7 | using System.Threading.Tasks;
8 |
9 | namespace Demo.GraphQl;
10 |
11 | ///
12 | /// Изменения GraphQL
13 | /// Главное отличие в том что подзапросы выполняются последовательно, в том же порядке что указано в запросе
14 | ///
15 | public class Mutation
16 | {
17 | #region Create
18 | ///
19 | /// Создание автора
20 | ///
21 | /// Создаваемый автор
22 | /// Контекст базы данных Entity
23 | /// Автор
24 | public Author Create(Author author, [Service] DemoContext ctx)
25 | {
26 | ctx.Add(author);
27 | ctx.SaveChanges();
28 | return author;
29 | }
30 | #endregion
31 |
32 | #region Update
33 | ///
34 | /// Обновление автора
35 | ///
36 | /// Обновляемый автор
37 | /// Контекст базы данных Entity
38 | /// Автор
39 | public Author Update(Author author, [Service] DemoContext ctx)
40 | {
41 | ctx.Update(author);
42 | ctx.SaveChanges();
43 | return author;
44 | }
45 | #endregion
46 |
47 | #region Delete
48 | ///
49 | /// Удаление автора
50 | ///
51 | /// Уникальный идентификатор автора
52 | /// Контекст базы данных Entity
53 | /// Автор
54 | public Author Delete(int id, [Service] DemoContext ctx)
55 | {
56 | var author = ctx.Authors.FirstOrDefault(a => a.Id == id);
57 | if (author == null)
58 | throw new ArgumentException("Автор не найден");
59 | ctx.Remove(author);
60 | ctx.SaveChanges();
61 | return author;
62 | }
63 | #endregion
64 |
65 | #region CreateOrUpdate
66 | ///
67 | /// Создать или обновить автора тут логика такая, если Id равен 0, то это однозначно новый автор
68 | ///
69 | /// Контекст базы данных Entity
70 | /// Создать или обновить автора
71 | ///
72 | /// Автор
73 | public async Task CreateOrUpdate(Author author, [Service] DemoContext ctx, [Service] ITopicEventSender sender)
74 | {
75 | if (author.Id == 0 || !ctx.Authors.Any(a => a.Id == author.Id))
76 | ctx.Add(author);
77 | else
78 | ctx.Update(author);
79 |
80 | ctx.SaveChanges();
81 |
82 | // по инициативе сервера отправляем клиентам данные
83 | await sender.SendAsync(nameof(Subscription.OnAuthorChanged), author);
84 | return author;
85 | }
86 | #endregion
87 |
88 | #region ThrowError
89 | ///
90 | /// Генерация ошибки для проверки транзакции
91 | ///
92 | /// Автор
93 | public Author ThrowError()
94 | {
95 | throw new Exception("Специально сгенерированная ошибка");
96 | }
97 | #endregion
98 |
99 | #region TestDateTime
100 | ///
101 | /// Проверка даты
102 | ///
103 | ///
104 | ///
105 | public DateTime TestDateTime(DateTime dt)
106 | {
107 | return dt;
108 | }
109 | #endregion
110 |
111 | public int[] Test1()
112 | {
113 | return new int[] { 1, 2, 3 };
114 | }
115 |
116 |
117 | public string Test2(int[] ids)
118 | {
119 | return string.Join(',', ids);
120 | }
121 |
122 | }
123 |
--------------------------------------------------------------------------------
/Demo.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net6.0
5 | enable
6 | true
7 | b7a1d7c8-f8b1-4ec6-a192-45531ffa65c8
8 | Linux
9 | c:\Project\DEMO\DEMO\Demo.xml
10 | https://github.com/AlexanderZhelnin/DEMO
11 |
12 |
13 |
14 | $(DefineConstants)TRACE SQLITE
15 |
16 |
17 |
18 | $(DefineConstants)TRACE SQLITE
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 | Always
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 | all
56 | runtime; build; native; contentfiles; analyzers; buildtransitive
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 | Always
71 |
72 |
73 | Always
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
--------------------------------------------------------------------------------
/Controllers/PublisherController.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using Microsoft.AspNetCore.Mvc;
5 | using Demo.Models;
6 | using Microsoft.AspNetCore.Http;
7 | using System.Net.Http;
8 | using Microsoft.AspNetCore.Authorization;
9 | using System.Threading.Tasks;
10 | using Microsoft.EntityFrameworkCore;
11 | using Demo.DB;
12 |
13 | namespace Demo.Controllers;
14 |
15 | ///
16 | /// Контроллер издателей
17 | ///
18 | [Route("api/[controller]")]
19 | [ApiController]
20 | public class PublisherController : ControllerBase
21 | {
22 |
23 | #region Переменные
24 | internal static int _id = 20;
25 | private readonly DemoContext _ctx;
26 | private readonly IHttpClientFactory _httpClientFactory;
27 | #endregion
28 |
29 | #region Конструктор
30 | ///
31 | /// Конструктор
32 | ///
33 | public PublisherController(DemoContext ctx, IHttpClientFactory httpClientFactory)
34 | {
35 | _ctx = ctx;
36 | _httpClientFactory = httpClientFactory;
37 | }
38 | #endregion
39 |
40 | ///
41 | /// Получение всех издателей
42 | ///
43 | [HttpGet("", Name = nameof(GetAllPublishers))]
44 | public IQueryable GetAllPublishers() =>
45 | _ctx.Publishers
46 | .Include(a => a.Authors);
47 |
48 | ///
49 | /// Получить издателя по уникальному идентификатору
50 | ///
51 | ///
52 | ///
53 | /// GET api/Authors/1
54 | [HttpGet("{id}", Name = nameof(GetPublisherById))]
55 | [ProducesResponseType(StatusCodes.Status200OK)]
56 | [ProducesDefaultResponseType]
57 | [Authorize]
58 | public ActionResult GetPublisherById(int id)
59 | {
60 | var result = _ctx.Publishers.FirstOrDefault(a => a.Id == id);
61 |
62 | if (result == null) return NotFound();
63 |
64 | return result;
65 | }
66 |
67 | ///
68 | /// Получение авторов по уникальному идентификатору издателя
69 | ///
70 | ///
71 | ///
72 | /// GET api/Publisher/1/Author
73 | [HttpGet("{id}/Author", Name = nameof(GetAuthor))]
74 | [ProducesResponseType(StatusCodes.Status200OK)]
75 | [ProducesResponseType(typeof(string), StatusCodes.Status400BadRequest)]
76 | public ActionResult> GetAuthor(int id)
77 | {
78 | var result = _ctx.Publishers.FirstOrDefault(a => a.Id == id);
79 |
80 | if (result == null)
81 | return BadRequest($"Издательства с заданным Id: {id} не существует");
82 |
83 | return result.Authors.ToArray();
84 | }
85 |
86 | ///
87 | /// Создание нового издательства
88 | ///
89 | /// новый издательство
90 | /// POST api/Authors
91 | [HttpPost(Name = nameof(CreatePublisher))]
92 | public void CreatePublisher([FromBody] Publisher publisher)
93 | {
94 | publisher.Id = ++_id;
95 | _ctx.Publishers.Add(publisher);
96 | }
97 |
98 | ///
99 | /// Добавление Автора к издателю
100 | ///
101 | /// Уникальный идентификатор издателя
102 | /// Уникальный идентификатор автора
103 |
104 | /// POST api/Authors
105 | [HttpPost("{id}/AddAuthor/{authorId}", Name = nameof(AddAuthor))]
106 | [ProducesResponseType(StatusCodes.Status200OK)]
107 | [ProducesResponseType(typeof(string), StatusCodes.Status400BadRequest)]
108 | [ProducesDefaultResponseType]
109 | public ActionResult AddAuthor(int id, int authorId)
110 | {
111 | #region Валидация
112 | var author = _ctx.Authors.FirstOrDefault(a => a.Id == authorId);
113 | if (author == null)
114 | return BadRequest($"Автора с заданным Id: {authorId} не существует");
115 |
116 | var publisher = _ctx.Publishers
117 | // .Include(p => p.Authors)
118 | .FirstOrDefault(a => a.Id == id);
119 | if (publisher == null)
120 | return BadRequest($"Издателя с заданным Id: {id} не существует");
121 |
122 | var relation = _ctx.PublishersAuthors.FirstOrDefault(a => a.PublisherId == id && a.AuthorId == authorId);
123 | if (relation != null)
124 | return BadRequest($"Автор с заданным Id: {authorId} уже добавлен в издалеля {id}");
125 | #endregion
126 |
127 | //publisher.Authors.Add(author);
128 | _ctx.PublishersAuthors.Add(new() { AuthorId = authorId, PublisherId = id });
129 | _ctx.SaveChangesAsync();
130 | return Ok(true);
131 | }
132 |
133 | ///
134 | /// Изменение автора
135 | ///
136 | /// издатель
137 | /// PUT api/Publisher
138 | [HttpPut("", Name = nameof(UpdatePublisher))]
139 | public ActionResult UpdatePublisher([FromBody] Publisher publisher)
140 | {
141 | _ctx.Update(publisher);
142 | _ctx.SaveChanges();
143 |
144 | return publisher;
145 | }
146 |
147 | ///
148 | /// Удаление издателя
149 | ///
150 | ///
151 | /// DELETE api/Publisher/1
152 | [HttpDelete("{id}", Name = nameof(DeletePublisher))]
153 | public void DeletePublisher(int id)
154 | {
155 | }
156 | }
157 |
--------------------------------------------------------------------------------
/nlog.config:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
28 |
29 |
30 |
32 |
33 | Server=127.0.0.1;Port=5432;Database=logs;User Id=test;Password=test;
34 |
35 |
36 | insert into "logs".logging(log_date,log_level,log_logger,log_message, stacktrace) values(@time_stamp, @level, @logger, @message, @stacktrace);
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
48 |
49 | Data Source=${basedir}\logs.db
50 |
51 |
52 | insert into logging(log_date,log_level,log_logger,log_message,stacktrace) values(@time_stamp, @level, @logger, @message, @stacktrace);
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
76 |
77 |
78 |
86 |
87 |
88 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
122 |
123 |
124 |
125 |
126 |
127 |
--------------------------------------------------------------------------------
/Controllers/AuthorsController.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using Microsoft.AspNetCore.Mvc;
5 | using Demo.Models;
6 | using Microsoft.AspNetCore.Http;
7 | using System.Net.Http;
8 | using Microsoft.AspNetCore.Authorization;
9 | using System.Threading.Tasks;
10 | using Microsoft.EntityFrameworkCore;
11 | using Demo.DB;
12 | using Mapster;
13 |
14 | namespace Demo.Controllers;
15 |
16 | ///
17 | /// Контроллер авторов
18 | ///
19 | [Route("api/[controller]")]
20 | [ApiController]
21 | public partial class AuthorsController : ControllerBase
22 | {
23 | #region Переменные
24 | internal static int _id = 20;
25 | private readonly LongPollingQuery _longpolling;
26 | private readonly DemoContext _ctx;
27 | private readonly DemoRepository _repository;
28 | private readonly IHttpClientFactory _httpClientFactory;
29 | private readonly ILogger _logger;
30 | #endregion
31 |
32 | #region Конструктор
33 | ///
34 | /// Конструктор
35 | ///
36 | public AuthorsController(
37 |
38 | LongPollingQuery longpolling,
39 | DemoContext ctx,
40 | DemoRepository repository,
41 | IHttpClientFactory httpClientFactory,
42 | ILogger logger)
43 | {
44 | _longpolling = longpolling;
45 | _ctx = ctx;
46 | _repository = repository;
47 | _httpClientFactory = httpClientFactory;
48 | _logger = logger;
49 | }
50 | #endregion
51 |
52 | #region TestTrace
53 | ///
54 | /// Тестовая функция с получением данных из "другого" микросервиса, с авторизацией
55 | ///
56 | [HttpGet("TestTrace", Name = nameof(TestTrace))]
57 | public async Task> TestTrace()
58 | {
59 | var demoClient = new Client.DemoClient("http://localhost:5004", _httpClientFactory.CreateClient("auth"));
60 |
61 | var bs = await demoClient.GetByIdAsync(1);
62 |
63 | //var responce = await _httpClientFactory.CreateClient("auth").GetAsync("http://localhost:5004/api/authors/1");
64 | //var result = await responce.Content.ReadAsStringAsync();
65 | return _ctx.Authors;
66 | }
67 | #endregion
68 |
69 | ///
70 | /// Получение всех авторов
71 | ///
72 | [HttpGet("", Name = nameof(GetAllAuthors))]
73 | public IQueryable GetAllAuthors()
74 | {
75 | return _ctx.Authors
76 | .AsNoTracking()
77 | .Include(a => a.Books)
78 | .Include(a => a.Publishers);
79 | }
80 |
81 |
82 | ///
83 | /// Получить автора по уникальному идентификатору
84 | ///
85 | ///
86 | ///
87 | /// GET api/Authors/1
88 | [HttpGet("{id}", Name = nameof(GetAuthorById))]
89 | [ProducesResponseType(StatusCodes.Status200OK)]
90 | [ProducesDefaultResponseType]
91 | [Authorize]
92 | public ActionResult GetAuthorById(int id)
93 | {
94 | var result = _ctx.Authors.FirstOrDefault(a => a.Id == id);
95 |
96 | if (result == null) return NotFound();
97 |
98 | return result;
99 | }
100 |
101 | ///
102 | /// Получение книг по уникальному идентификатору и названию
103 | ///
104 | ///
105 | ///
106 | ///
107 | /// GET api/Authors/1/Book/Первая книга Васи
108 | [HttpGet("{id}/Book/{title?}", Name = nameof(GetBook))]
109 | [ProducesResponseType(StatusCodes.Status200OK)]
110 | [ProducesResponseType(typeof(string), StatusCodes.Status400BadRequest)]
111 | public ActionResult> GetBook(int id, string title)
112 | {
113 | var result = _ctx.Authors.FirstOrDefault(a => a.Id == id);
114 |
115 | if (result == null)
116 | return BadRequest($"Автора с заданным Id: {id} не существует");
117 |
118 | return title == null
119 | ? result.Books.ToArray()
120 | : result.Books.Where(b => string.Equals(b.Title, title, StringComparison.OrdinalIgnoreCase)).ToArray();
121 | }
122 |
123 | ///
124 | /// Метод чтения реализующий паттерн LongPolling
125 | ///
126 | /// маркер после которого происходит чтение данных из очереди
127 | /// коллекция результат
128 | /// GET api/Authors/GetChanges/
129 | [HttpGet("getchages/{marker}", Name = nameof(GetChanges))]
130 | public IAsyncEnumerable> GetChanges(string marker)
131 | {
132 | if (marker == "0")
133 | marker = DateTime.MinValue.ToString("o");
134 |
135 | if (!DateTime.TryParse(marker, out var dt))
136 | return AsyncEnumerable.Empty>(); ;
137 |
138 | return _longpolling.Read(dt);
139 | }
140 |
141 | ///
142 | /// Создание нового автора
143 | ///
144 | /// новый автор
145 | /// POST api/Authors
146 | [HttpPost(Name = nameof(CreateAuthor))]
147 | public void CreateAuthor([FromBody] Author author)
148 | {
149 | author.Id = ++_id;
150 |
151 | _ctx.Authors.Add(author);
152 | _longpolling.Add(author);
153 | }
154 |
155 | ///
156 | /// Изменение автора
157 | ///
158 | ///
159 | /// PUT api/Authors
160 | [HttpPut("", Name = nameof(UpdateAuthor))]
161 | public ActionResult UpdateAuthor([FromBody] Author author)
162 | {
163 | //var existAuthor = _ctx.Authors.FirstOrDefault(a => a.Id == author.Id);
164 | //if (existAuthor == null)
165 | // return NotFound($"Не удалось найти автора для обновления {author.Id}");
166 |
167 | //try
168 | //{
169 | // _ctx.Update(author);
170 | // _ctx.SaveChanges();
171 | //}
172 | //catch (Exception exception)
173 | //{
174 | // //
175 | // // тут будет запись ошибки в лог
176 | // //
177 |
178 | // return BadRequest($"Не удалось обновить автора {author.Id}");
179 | //}
180 |
181 | //return author;
182 |
183 | _repository.Update(author);
184 | return author;
185 | }
186 |
187 | ///
188 | /// Удаление автора
189 | ///
190 | ///
191 | /// DELETE api/Authors/1
192 | [HttpDelete("{id}", Name = nameof(DeleteAuthor))]
193 | public void DeleteAuthor(int id)
194 | {
195 | }
196 |
197 | ///
198 | /// Создание нового автора
199 | ///
200 | /// новый автор
201 | /// POST api/Authors
202 | [HttpPost("upsert", Name = nameof(UpsertAuthor))]
203 | public void UpsertAuthor([FromBody] Author author)
204 | {
205 | //author.Id = ++_id;
206 | author.Name ??= "Вася";
207 |
208 | _ctx.Upsert(author).Run();
209 | //_ctx.Set(author).addor
210 | //_ctx.Authors.Add(author);
211 | //_longpolling.Add(author);
212 | //_ctx.SaveChanges();
213 | }
214 |
215 | ///
216 | /// Тестовая функция с получением данных из "другого" микросервиса, с авторизацией
217 | ///
218 | [HttpGet("TestDate", Name = nameof(TestDate))]
219 | public DateTime TestDate(DateTime dt)
220 | {
221 | return dt;
222 | }
223 |
224 |
225 | }
226 |
--------------------------------------------------------------------------------
/GraphQl/Query.cs:
--------------------------------------------------------------------------------
1 | using Demo.Models;
2 | using HotChocolate;
3 | using HotChocolate.AspNetCore.Authorization;
4 | using HotChocolate.Data;
5 | using HotChocolate.Types;
6 | using Microsoft.AspNetCore.Http;
7 | using Microsoft.AspNetCore.Mvc;
8 | using System;
9 | using System.Collections.Generic;
10 | using System.Linq;
11 | using System.Security.Claims;
12 | using Microsoft.Extensions.Logging;
13 | using HotChocolate.Types.Pagination;
14 | using System.Threading.Tasks;
15 | using Microsoft.EntityFrameworkCore;
16 | using Microsoft.Extensions.Options;
17 | using Demo.DB;
18 |
19 | namespace Demo.GraphQl;
20 |
21 | ///
22 | /// Запросы GraphQl
23 | /// Главное отличик запроса в том что его подзапросы выполняются параллельно
24 | ///
25 | public partial class Query
26 | {
27 | [LoggerMessage(0, LogLevel.Information, "Чтение {Author}")]
28 | partial void LogRead(Author author);
29 |
30 | private readonly ILogger _logger;
31 | private readonly IConfiguration _configuration;
32 | //private readonly IOptionsMonitor _settings;
33 |
34 | //private static Dictionary Permissions = new Dictionary
35 | //{
36 | // { "admin", new int[] { 1 } },
37 | // { "Вася", new int[] { 2 } }
38 | //};
39 |
40 | ///
41 | /// Конструктор
42 | ///
43 | public Query(ILogger logger, IConfiguration configuration)//, IOptionsMonitor settings)
44 | {
45 | (_logger, _configuration) = (logger, configuration);
46 |
47 | logger.LogInformation("Инициализация");
48 | //_settings = settings;
49 | }
50 |
51 | ///
52 | /// Возвращает первую версию
53 | ///
54 | ///
55 | public Api1 V1() =>
56 | new Api1();
57 |
58 |
59 | #region Authors
60 | ///
61 | ///
62 | ///
63 | ///
64 | ///
65 | ///
66 | ///
67 | [UseDbContext(typeof(DemoContext))]
68 | [UsePaging(IncludeTotalCount = true)]
69 | [UseProjection]
70 | public Connection MyLogicPaginAuthors(string? after, int? first, string sortBy, [ScopedService] DemoContext ctx)
71 | {
72 |
73 | var pageSize = first ?? 10;
74 | var authors = ((IQueryable)ctx.Authors);
75 | var count = authors.Count();
76 | var hasNextPage = false;
77 |
78 | if (after != null)
79 | {
80 | var split = after.IndexOf('_');
81 | var id = Convert.ToInt32(after[..split]);
82 | var name = after[(split + 1)..];
83 |
84 | authors =
85 | authors
86 | .Where(a => string.Compare(a.Name, name) == 1 || (a.Name == name && a.Id > id));
87 | }
88 |
89 | var edges =
90 | authors
91 | .OrderBy(a => a.Name)
92 | .ThenBy(a => a.Id)
93 | .Select(a => new Edge(a, $"{a.Id}_{a.Name}"))
94 | .Take(pageSize + 1)
95 | .ToList();
96 |
97 |
98 | hasNextPage = edges.Count > pageSize;
99 |
100 | edges = edges.SkipLast(1).ToList();
101 |
102 | var pageInfo =
103 | new ConnectionPageInfo(
104 | hasNextPage,
105 | false,
106 | edges.FirstOrDefault()?.Cursor,
107 | edges.LastOrDefault()?.Cursor);
108 |
109 | var connection =
110 | new Connection(
111 | edges,
112 | pageInfo,
113 | ct => ValueTask.FromResult(count));
114 |
115 | return connection;
116 | }
117 |
118 | ///
119 | /// Запрос чтения
120 | ///
121 | /// Контекст базы данных Entity
122 | /// Авторы
123 | [UseDbContext(typeof(DemoContext))]
124 | //[UsePaging(IncludeTotalCount = true)]
125 | //[UseOffsetPaging]
126 | [UseProjection]
127 | [UseFiltering()]
128 | //[UseFiltering(typeof(AuthorFilterType))]
129 | [UseSorting()]
130 | public IQueryable Authors([ScopedService] DemoContext ctx)//, ClaimsPrincipal claimsPrincipal)
131 | {
132 | #region MyRegion
133 | //var roles = claimsPrincipal.FindAll(ClaimTypes.Role).ToArray();
134 | //var accessIds = new List();
135 | //foreach (var r in roles)
136 | // if (Permissions.TryGetValue(r.Value, out var ids)) accessIds.AddRange(ids);
137 | //return ((IQueryable)ctx.Authors).Where(a => accessIds.Contains(a.Id));
138 | #endregion
139 |
140 | //var resutl = ((IQueryable)ctx.Authors).Where(a => EF.Functions.ILike(a.Name, "автор 4")).ToList();
141 | var resutl = ((IQueryable)ctx.Authors.AsNoTracking()).Where(a => a.Name.ToLower() == "автор 4".ToLower()).ToList();
142 |
143 | return ctx.Authors;
144 | }
145 |
146 | #endregion
147 | #region AuthorsById
148 | ///
149 | /// Чтение по уникальным идентификаторам, это функция по факту не нужна, легко заменяется функцией Authors с фильтром
150 | ///
151 | ///
152 | ///
153 | /// Авторы
154 | [UseProjection]
155 | public IQueryable AuthorsByIds([Service] DemoContext ctx, IEnumerable ids) =>
156 | ((IQueryable)ctx.Authors).Where(a => ids.Contains(a.Id));
157 | #endregion
158 |
159 | #region AuthorById
160 | ///
161 | /// Получить автора по иникальному идентификатору
162 | ///
163 | /// Контекст базы данных
164 | /// Уникальный идентификатор книги
165 | /// Автор книги
166 | ///
167 | public Author AuthorById([Service] DemoContext ctx, int id)
168 | {
169 | var author = ((IQueryable)ctx.Authors).FirstOrDefault(a => a.Id == id);
170 |
171 | if (author == null) throw new ArgumentException($"Автор с заданным id: {id} не существует");
172 |
173 | return author;
174 | }
175 | #endregion
176 |
177 | #region Book
178 | ///
179 | /// Запрос получения книг
180 | ///
181 | /// Контекст базы данных Entity
182 | /// Книги
183 | [UseProjection]
184 | [UseFiltering()]
185 | [UseSorting()]
186 | public IQueryable Books([Service] DemoContext ctx) => ctx.Books;
187 | #endregion
188 |
189 | #region AuthorizeQuery
190 | ///
191 | /// Тестовая функция с авторизацией
192 | ///
193 | ///
194 | ///
195 | [Authorize(Roles = new[] { "admin" })]
196 | public bool AuthorizeQuery([Service] IHttpContextAccessor context, ClaimsPrincipal claimsPrincipal)
197 | {
198 | var user = context.HttpContext.User;
199 | var username = user.FindFirstValue("preferred_username");
200 | return true;
201 |
202 | }
203 | #endregion
204 |
205 |
206 | /** Проверяем конфигурацию на прямую */
207 | public string TestConfigurationDirectly()
208 | {
209 | return _configuration["key1"];
210 | }
211 |
212 | /** Проверяем конфигурацию из JSON */
213 | public string TestConfigurationJSON()
214 | {
215 | return _configuration["settings:JSONKey1"];
216 | }
217 |
218 | /** Проверяем работу значений из YAML файла */
219 | public string TestConfigurationYaml()
220 | {
221 | return _configuration["yamlkey3:v1"];
222 | }
223 |
224 | /** Проверяем конфигурацию из xml */
225 | public string TestConfigurationXML()
226 | {
227 | return _configuration["XMLKey1"];
228 | }
229 |
230 | /** Проверяем простейшие опции */
231 | public string TestOptions([Service] IOptions settings)
232 | {
233 | return settings.Value.OAUTH_PATH;
234 | }
235 |
236 | /** Проверяем простейшие опции снимка */
237 | public string TestOptionsSnapshot([Service] IOptionsSnapshot settings)
238 | {
239 | return settings.Value.OAUTH_PATH;
240 | }
241 |
242 | /** Проверяем работу опций типа монитор */
243 | public string TestOptionsMonitor([Service] IOptionsMonitor settings)
244 | {
245 | return settings.CurrentValue.OAUTH_PATH;
246 | }
247 |
248 | /** Проверяем работу динамических значений */
249 | public string TestConfigurationDynamic()
250 | {
251 | return _configuration["DynamicKey1"];
252 | }
253 |
254 | /** Проверяем работу значений из базы данных */
255 | public string TestConfigurationDB()
256 | {
257 | return _configuration["DBConfiguration:DBKey1"];
258 | }
259 |
260 | public int[] Test1()
261 | {
262 | return new int[] { 1, 2, 3 };
263 | }
264 |
265 |
266 | public string Test2(int[] ids)
267 | {
268 | return string.Join(',', ids);
269 | }
270 |
271 | }
272 |
273 | public class Api1
274 | {
275 | ///
276 | /// Запрос чтения
277 | ///
278 | /// Контекст базы данных Entity
279 | /// Авторы
280 | [UseDbContext(typeof(DemoContext))]
281 | [UseProjection]
282 | [UseFiltering()]
283 | [UseSorting()]
284 | public IQueryable Authors([ScopedService] DemoContext ctx)
285 | {
286 | return ctx.Authors;
287 | }
288 | }
--------------------------------------------------------------------------------
/DBContext/DemoContext.cs:
--------------------------------------------------------------------------------
1 | using Demo.Models;
2 | using Microsoft.EntityFrameworkCore;
3 | using System;
4 | using System.Diagnostics;
5 | using System.Linq;
6 |
7 | namespace Demo.DB;
8 |
9 | /** */
10 | public class DemoContext : DbContext
11 | {
12 | ///
13 | /// Констрктор DemoContext
14 | ///
15 | /// свойства контекста
16 | public DemoContext(DbContextOptions o) : base(o)
17 | {
18 | #if SQLITE
19 | try
20 | {
21 | Database.EnsureCreated();
22 | }
23 | catch (Exception e)
24 | {
25 | Debug.Print(e.ToString());
26 | }
27 | #endif
28 | }
29 |
30 | /** Авторы */
31 | public DbSet Authors { get; set; }
32 | /** Книги */
33 | public DbSet Books { get; set; }
34 |
35 | /** Издательства */
36 | public DbSet Publishers { get; set; }
37 |
38 | /** Дополнительная информация по книге */
39 | public DbSet BookDetails { get; set; }
40 |
41 | /** Связь многие ко многим между Издателями и Авторами */
42 | public DbSet PublishersAuthors { get; set; }
43 |
44 | ///
45 | /// Настройка свойств модели
46 | ///
47 | protected override void OnModelCreating(ModelBuilder modelBuilder)
48 | {
49 | //modelBuilder.UseCollation("SQL_Latin1_General_CP1_CS_AS");
50 | modelBuilder.HasDefaultSchema("authors");
51 | #region Данные по умолчанию
52 | //var authors = new Author[]
53 | // {
54 | // new() { Id = 1, Name = "Вася", },
55 | // new() { Id = 2, Name = "Петя", }
56 | // };
57 |
58 | var authors = Enumerable.Range(1, 500).Select(i => new Author { Id = i, Name = "Автор " + i });
59 | var publishers = new Publisher[]
60 | {
61 | new() { Id = 1, Name = "Издательство ООО \"Сервер\"" },
62 | new() { Id = 2, Name = "Издательство ООО \"Восток\"" }
63 | };
64 | #endregion
65 |
66 | modelBuilder.Entity(b =>
67 | {
68 | //b.Property(a => a.Id).ValueGeneratedNever();
69 | b.HasKey(a => a.Id);
70 | b.HasIndex(b => b.Name).IsUnique();
71 |
72 | //b.Property(c => c.Name)
73 | // .UseCollation("SQL_Latin1_General_CP1_CI_AS");
74 |
75 | #region Данные по умолчанию
76 | b.HasData(authors);
77 | #endregion
78 |
79 | // Один ко многим
80 | b
81 | .HasMany(a => a.Books)
82 | .WithOne(b => b.Author);
83 | });
84 |
85 | modelBuilder.Entity(b =>
86 | {
87 | #region Данные по умолчанию
88 | b.HasData(
89 | new()
90 | {
91 | Id = 3,
92 | AuthorId = 1,
93 | Title = "Первая книга Васи",
94 | Description = "Биографическое описание жизни",
95 |
96 | },
97 | new()
98 | {
99 | Id = 4,
100 | AuthorId = 1,
101 | Title = "Вторая книга Васи",
102 | Description = "Фантастическая книга о приключениях",
103 | },
104 | new()
105 | {
106 | Id = 5,
107 | AuthorId = 1,
108 | Title = "Третья книга Васи",
109 | Description = "Историческая книга",
110 |
111 | },
112 | new()
113 | {
114 | Id = 6,
115 | AuthorId = 2,
116 | Title = "Первая книга Пети",
117 | Description = "Фентази об эльфах",
118 | },
119 | new()
120 | {
121 | Id = 7,
122 | AuthorId = 2,
123 | Title = "Вторая книга Пети",
124 | Description = "Научная литература, докозательство 3-й теорему Фихтенгольца",
125 | },
126 | new()
127 | {
128 | Id = 8,
129 | AuthorId = 2,
130 | Title = "Третья книга Пети",
131 | Description = "Научная фантастика и приключение героя в далёком космосе",
132 |
133 | });
134 | #endregion
135 | });
136 |
137 | modelBuilder.Entity(b =>
138 | {
139 | b.HasKey(d => d.BookId);
140 | // Один к одному
141 | b.HasOne(d => d.Book);
142 |
143 | #region Данные по умолчанию
144 | b.HasData(
145 | new()
146 | {
147 | BookId = 3,
148 | Year = new DateTime(2000, 1, 1),
149 | Genre = GenreEnum.historical,
150 | HardcoverCost = 100,
151 | Illustrator = "МИКЕЛАНДЖЕЛО",
152 | Editor = "Не известен",
153 | Language = LanguageEnum.ru,
154 | PageCount = 100,
155 | ReadingAge = 18,
156 | Rank = 7.112
157 | },
158 | new()
159 | {
160 | BookId = 4,
161 | Year = new DateTime(2001, 1, 1),
162 | Genre = GenreEnum.fantastic,
163 | HardcoverCost = 110,
164 | Illustrator = "ЙОХАННЕС ВЕРМЕЕР",
165 | Editor = "Не известен",
166 | Language = LanguageEnum.ru,
167 | PageCount = 200,
168 | ReadingAge = 12,
169 | Rank = 4.343
170 | },
171 | new()
172 | {
173 | BookId = 5,
174 | Year = new DateTime(2002, 1, 1),
175 | Genre = GenreEnum.historical,
176 | HardcoverCost = 120,
177 | Illustrator = "ПАБЛО ПИКАССО",
178 | Editor = "Не известен",
179 | Language = LanguageEnum.ru,
180 | PageCount = 300,
181 | ReadingAge = 10,
182 | Rank = 9.2
183 | },
184 | new()
185 | {
186 | BookId = 6,
187 | Year = new DateTime(2003, 1, 1),
188 | Genre = GenreEnum.fantasy,
189 | HardcoverCost = 130,
190 | Illustrator = "ВИНСЕНТ ВАН ГОГ",
191 | Editor = "Не известен",
192 | Language = LanguageEnum.ru,
193 | PageCount = 400,
194 | ReadingAge = 5,
195 | Rank = 8.345
196 | },
197 | new()
198 | {
199 | BookId = 7,
200 | Year = new DateTime(2004, 1, 1),
201 | Genre = GenreEnum.scientific,
202 | HardcoverCost = 140,
203 | Illustrator = "РЕМБРАНДТ ВАН РЕЙН",
204 | Editor = "Не известен",
205 | Language = LanguageEnum.en,
206 | PageCount = 500,
207 | ReadingAge = 10,
208 | Rank = 1
209 | },
210 | new()
211 | {
212 | BookId = 8,
213 | Year = new DateTime(2005, 1, 1),
214 | Genre = GenreEnum.fantastic,
215 | HardcoverCost = 150,
216 | Illustrator = "ЛЕОНАРДО ДА ВИНЧИ",
217 | Editor = "Не известен",
218 | Language = LanguageEnum.ru,
219 | PageCount = 600,
220 | ReadingAge = 18,
221 | Rank = 5
222 | }
223 | );
224 | #endregion
225 | });
226 |
227 | modelBuilder.Entity(b =>
228 | {
229 | // Многие ко многим
230 | b
231 | .HasMany(p => p.Authors)
232 | .WithMany(a => a.Publishers)
233 | .UsingEntity(
234 | author => author
235 | .HasOne(pa => pa.Author)
236 | .WithMany(a => a.PublishersAuthors)
237 | .HasForeignKey(pa => pa.AuthorId),
238 | publisher => publisher
239 | .HasOne(pa => pa.Publisher)
240 | .WithMany(p => p.PublishersAuthors)
241 | .HasForeignKey(pa => pa.PublisherId)
242 | );
243 |
244 | b.HasData(publishers);
245 | });
246 |
247 | modelBuilder.Entity(b =>
248 | {
249 | #region Данные по умолчанию
250 | b.HasData(
251 | new() { PublisherId = 1, AuthorId = 1 },
252 | new() { PublisherId = 1, AuthorId = 2 },
253 | new() { PublisherId = 2, AuthorId = 1 }
254 | //new() { PublisherId = 2, AuthorId = 2 }
255 | );
256 | #endregion
257 | });
258 | }
259 | }
260 |
--------------------------------------------------------------------------------
/Migrations/20211106063701_start.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using Microsoft.EntityFrameworkCore.Migrations;
3 | using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
4 |
5 | namespace Demo.Migrations;
6 |
7 | public partial class start : Migration
8 | {
9 | protected override void Up(MigrationBuilder migrationBuilder)
10 | {
11 | migrationBuilder.EnsureSchema(
12 | name: "authors");
13 |
14 | migrationBuilder.CreateTable(
15 | name: "authors",
16 | schema: "authors",
17 | columns: table => new
18 | {
19 | id = table.Column(type: "integer", nullable: false)
20 | .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
21 | name = table.Column(type: "text", nullable: true)
22 | },
23 | constraints: table =>
24 | {
25 | table.PrimaryKey("pk_authors", x => x.id);
26 | });
27 |
28 | migrationBuilder.CreateTable(
29 | name: "publishers",
30 | schema: "authors",
31 | columns: table => new
32 | {
33 | id = table.Column(type: "integer", nullable: false)
34 | .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
35 | name = table.Column(type: "text", nullable: true)
36 | },
37 | constraints: table =>
38 | {
39 | table.PrimaryKey("pk_publishers", x => x.id);
40 | });
41 |
42 | migrationBuilder.CreateTable(
43 | name: "books",
44 | schema: "authors",
45 | columns: table => new
46 | {
47 | id = table.Column(type: "integer", nullable: false)
48 | .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
49 | title = table.Column(type: "text", nullable: true),
50 | description = table.Column(type: "text", nullable: true),
51 | image_url = table.Column(type: "text", nullable: true),
52 | author_id = table.Column(type: "integer", nullable: false),
53 | isbn_10 = table.Column(type: "text", nullable: true),
54 | isbn_13 = table.Column(type: "text", nullable: true)
55 | },
56 | constraints: table =>
57 | {
58 | table.PrimaryKey("pk_books", x => x.id);
59 | table.ForeignKey(
60 | name: "fk_books_authors_author_id",
61 | column: x => x.author_id,
62 | principalSchema: "authors",
63 | principalTable: "authors",
64 | principalColumn: "id",
65 | onDelete: ReferentialAction.Cascade);
66 | });
67 |
68 | migrationBuilder.CreateTable(
69 | name: "publishers_authors",
70 | schema: "authors",
71 | columns: table => new
72 | {
73 | author_id = table.Column(type: "integer", nullable: false),
74 | publisher_id = table.Column(type: "integer", nullable: false)
75 | },
76 | constraints: table =>
77 | {
78 | table.PrimaryKey("pk_publishers_authors", x => new { x.author_id, x.publisher_id });
79 | table.ForeignKey(
80 | name: "fk_publishers_authors_authors_author_id",
81 | column: x => x.author_id,
82 | principalSchema: "authors",
83 | principalTable: "authors",
84 | principalColumn: "id",
85 | onDelete: ReferentialAction.Cascade);
86 | table.ForeignKey(
87 | name: "fk_publishers_authors_publishers_publisher_id",
88 | column: x => x.publisher_id,
89 | principalSchema: "authors",
90 | principalTable: "publishers",
91 | principalColumn: "id",
92 | onDelete: ReferentialAction.Cascade);
93 | });
94 |
95 | migrationBuilder.CreateTable(
96 | name: "book_details",
97 | schema: "authors",
98 | columns: table => new
99 | {
100 | book_id = table.Column(type: "integer", nullable: false),
101 | rank = table.Column(type: "double precision", nullable: false),
102 | status = table.Column(type: "integer", nullable: false),
103 | peperback_cost = table.Column(type: "numeric", nullable: false),
104 | hardcover_cost = table.Column(type: "numeric", nullable: false),
105 | year = table.Column(type: "timestamp without time zone", nullable: false),
106 | editor = table.Column(type: "text", nullable: true),
107 | illustrator = table.Column(type: "text", nullable: true),
108 | page_count = table.Column(type: "integer", nullable: false),
109 | language = table.Column(type: "integer", nullable: false),
110 | reading_age = table.Column(type: "smallint", nullable: false),
111 | genre = table.Column(type: "integer", nullable: false),
112 | reviews = table.Column(type: "integer", nullable: false)
113 | },
114 | constraints: table =>
115 | {
116 | table.PrimaryKey("pk_book_details", x => x.book_id);
117 | table.ForeignKey(
118 | name: "fk_book_details_books_book_id",
119 | column: x => x.book_id,
120 | principalSchema: "authors",
121 | principalTable: "books",
122 | principalColumn: "id",
123 | onDelete: ReferentialAction.Cascade);
124 | });
125 |
126 | migrationBuilder.InsertData(
127 | schema: "authors",
128 | table: "authors",
129 | columns: new[] { "id", "name" },
130 | values: new object[,]
131 | {
132 | { 1, "Вася" },
133 | { 2, "Петя" }
134 | });
135 |
136 | migrationBuilder.InsertData(
137 | schema: "authors",
138 | table: "publishers",
139 | columns: new[] { "id", "name" },
140 | values: new object[,]
141 | {
142 | { 1, "Издательство ООО \"Сервер\"" },
143 | { 2, "Издательство ООО \"Восток\"" }
144 | });
145 |
146 | migrationBuilder.InsertData(
147 | schema: "authors",
148 | table: "books",
149 | columns: new[] { "id", "author_id", "description", "isbn_10", "isbn_13", "image_url", "title" },
150 | values: new object[,]
151 | {
152 | { 3, 1, "Биографическое описание жизни", null, null, null, "Первая книга Васи" },
153 | { 4, 1, "Фантастическая книга о приключениях", null, null, null, "Вторая книга Васи" },
154 | { 5, 1, "Историческая книга", null, null, null, "Третья книга Васи" },
155 | { 6, 2, "Фентази об эльфах", null, null, null, "Первая книга Пети" },
156 | { 7, 2, "Научная литература, докозательство 3-й теорему Фихтенгольца", null, null, null, "Вторая книга Пети" },
157 | { 8, 2, "Научная фантастика и приключение героя в далёком космосе", null, null, null, "Третья книга Пети" }
158 | });
159 |
160 | migrationBuilder.InsertData(
161 | schema: "authors",
162 | table: "publishers_authors",
163 | columns: new[] { "author_id", "publisher_id" },
164 | values: new object[,]
165 | {
166 | { 1, 1 },
167 | { 2, 1 },
168 | { 1, 2 }
169 | });
170 |
171 | migrationBuilder.InsertData(
172 | schema: "authors",
173 | table: "book_details",
174 | columns: new[] { "book_id", "editor", "genre", "hardcover_cost", "illustrator", "language", "page_count", "peperback_cost", "rank", "reading_age", "reviews", "status", "year" },
175 | values: new object[,]
176 | {
177 | { 3, "Не известен", 4, 100m, "МИКЕЛАНДЖЕЛО", 0, 100, 0m, 7.1120000000000001, (byte)18, 0, 0, new DateTime(2000, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified) },
178 | { 4, "Не известен", 0, 110m, "ЙОХАННЕС ВЕРМЕЕР", 0, 200, 0m, 4.343, (byte)12, 0, 0, new DateTime(2001, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified) },
179 | { 5, "Не известен", 4, 120m, "ПАБЛО ПИКАССО", 0, 300, 0m, 9.1999999999999993, (byte)10, 0, 0, new DateTime(2002, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified) },
180 | { 6, "Не известен", 1, 130m, "ВИНСЕНТ ВАН ГОГ", 0, 400, 0m, 8.3450000000000006, (byte)5, 0, 0, new DateTime(2003, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified) },
181 | { 7, "Не известен", 3, 140m, "РЕМБРАНДТ ВАН РЕЙН", 1, 500, 0m, 1.0, (byte)10, 0, 0, new DateTime(2004, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified) },
182 | { 8, "Не известен", 0, 150m, "ЛЕОНАРДО ДА ВИНЧИ", 0, 600, 0m, 5.0, (byte)18, 0, 0, new DateTime(2005, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified) }
183 | });
184 |
185 | migrationBuilder.CreateIndex(
186 | name: "ix_authors_name",
187 | schema: "authors",
188 | table: "authors",
189 | column: "name",
190 | unique: true);
191 |
192 | migrationBuilder.CreateIndex(
193 | name: "ix_books_author_id",
194 | schema: "authors",
195 | table: "books",
196 | column: "author_id");
197 |
198 | migrationBuilder.CreateIndex(
199 | name: "ix_publishers_authors_publisher_id",
200 | schema: "authors",
201 | table: "publishers_authors",
202 | column: "publisher_id");
203 | }
204 |
205 | protected override void Down(MigrationBuilder migrationBuilder)
206 | {
207 | migrationBuilder.DropTable(
208 | name: "book_details",
209 | schema: "authors");
210 |
211 | migrationBuilder.DropTable(
212 | name: "publishers_authors",
213 | schema: "authors");
214 |
215 | migrationBuilder.DropTable(
216 | name: "books",
217 | schema: "authors");
218 |
219 | migrationBuilder.DropTable(
220 | name: "publishers",
221 | schema: "authors");
222 |
223 | migrationBuilder.DropTable(
224 | name: "authors",
225 | schema: "authors");
226 | }
227 | }
228 |
--------------------------------------------------------------------------------
/Migrations/DemoContextModelSnapshot.cs:
--------------------------------------------------------------------------------
1 | //
2 | using System;
3 | using Demo.DB;
4 | using Demo.Models;
5 | using Microsoft.EntityFrameworkCore;
6 | using Microsoft.EntityFrameworkCore.Infrastructure;
7 | using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
8 | using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
9 |
10 | namespace Demo.Migrations;
11 |
12 | [DbContext(typeof(DemoContext))]
13 | partial class DemoContextModelSnapshot : ModelSnapshot
14 | {
15 | protected override void BuildModel(ModelBuilder modelBuilder)
16 | {
17 | #pragma warning disable 612, 618
18 | modelBuilder
19 | .HasDefaultSchema("authors")
20 | .HasAnnotation("Relational:MaxIdentifierLength", 63)
21 | .HasAnnotation("ProductVersion", "5.0.11")
22 | .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn);
23 |
24 | modelBuilder.Entity("Demo.Model.Author", b =>
25 | {
26 | b.Property("Id")
27 | .ValueGeneratedOnAdd()
28 | .HasColumnType("integer")
29 | .HasColumnName("id")
30 | .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn);
31 |
32 | b.Property("Name")
33 | .HasColumnType("text")
34 | .HasColumnName("name");
35 |
36 | b.HasKey("Id")
37 | .HasName("pk_authors");
38 |
39 | b.HasIndex("Name")
40 | .IsUnique()
41 | .HasDatabaseName("ix_authors_name");
42 |
43 | b.ToTable("authors");
44 |
45 | b.HasData(
46 | new
47 | {
48 | Id = 1,
49 | Name = "Вася"
50 | },
51 | new
52 | {
53 | Id = 2,
54 | Name = "Петя"
55 | });
56 | });
57 |
58 | modelBuilder.Entity("Demo.Model.Book", b =>
59 | {
60 | b.Property("Id")
61 | .ValueGeneratedOnAdd()
62 | .HasColumnType("integer")
63 | .HasColumnName("id")
64 | .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn);
65 |
66 | b.Property("AuthorId")
67 | .HasColumnType("integer")
68 | .HasColumnName("author_id");
69 |
70 | b.Property("Description")
71 | .HasColumnType("text")
72 | .HasColumnName("description");
73 |
74 | b.Property("ISBN_10")
75 | .HasColumnType("text")
76 | .HasColumnName("isbn_10");
77 |
78 | b.Property("ISBN_13")
79 | .HasColumnType("text")
80 | .HasColumnName("isbn_13");
81 |
82 | b.Property("ImageUrl")
83 | .HasColumnType("text")
84 | .HasColumnName("image_url");
85 |
86 | b.Property("Title")
87 | .HasColumnType("text")
88 | .HasColumnName("title");
89 |
90 | b.HasKey("Id")
91 | .HasName("pk_books");
92 |
93 | b.HasIndex("AuthorId")
94 | .HasDatabaseName("ix_books_author_id");
95 |
96 | b.ToTable("books");
97 |
98 | b.HasData(
99 | new
100 | {
101 | Id = 3,
102 | AuthorId = 1,
103 | Description = "Биографическое описание жизни",
104 | Title = "Первая книга Васи"
105 | },
106 | new
107 | {
108 | Id = 4,
109 | AuthorId = 1,
110 | Description = "Фантастическая книга о приключениях",
111 | Title = "Вторая книга Васи"
112 | },
113 | new
114 | {
115 | Id = 5,
116 | AuthorId = 1,
117 | Description = "Историческая книга",
118 | Title = "Третья книга Васи"
119 | },
120 | new
121 | {
122 | Id = 6,
123 | AuthorId = 2,
124 | Description = "Фентази об эльфах",
125 | Title = "Первая книга Пети"
126 | },
127 | new
128 | {
129 | Id = 7,
130 | AuthorId = 2,
131 | Description = "Научная литература, докозательство 3-й теорему Фихтенгольца",
132 | Title = "Вторая книга Пети"
133 | },
134 | new
135 | {
136 | Id = 8,
137 | AuthorId = 2,
138 | Description = "Научная фантастика и приключение героя в далёком космосе",
139 | Title = "Третья книга Пети"
140 | });
141 | });
142 |
143 | modelBuilder.Entity("Demo.Model.BookDetails", b =>
144 | {
145 | b.Property("BookId")
146 | .HasColumnType("integer")
147 | .HasColumnName("book_id");
148 |
149 | b.Property("Editor")
150 | .HasColumnType("text")
151 | .HasColumnName("editor");
152 |
153 | b.Property("Genre")
154 | .HasColumnType("integer")
155 | .HasColumnName("genre");
156 |
157 | b.Property("HardcoverCost")
158 | .HasColumnType("numeric")
159 | .HasColumnName("hardcover_cost");
160 |
161 | b.Property("Illustrator")
162 | .HasColumnType("text")
163 | .HasColumnName("illustrator");
164 |
165 | b.Property("Language")
166 | .HasColumnType("integer")
167 | .HasColumnName("language");
168 |
169 | b.Property("PageCount")
170 | .HasColumnType("integer")
171 | .HasColumnName("page_count");
172 |
173 | b.Property("PeperbackCost")
174 | .HasColumnType("numeric")
175 | .HasColumnName("peperback_cost");
176 |
177 | b.Property("Rank")
178 | .HasColumnType("double precision")
179 | .HasColumnName("rank");
180 |
181 | b.Property("ReadingAge")
182 | .HasColumnType("smallint")
183 | .HasColumnName("reading_age");
184 |
185 | b.Property("Reviews")
186 | .HasColumnType("integer")
187 | .HasColumnName("reviews");
188 |
189 | b.Property("Status")
190 | .HasColumnType("integer")
191 | .HasColumnName("status");
192 |
193 | b.Property("Year")
194 | .HasColumnType("timestamp without time zone")
195 | .HasColumnName("year");
196 |
197 | b.HasKey("BookId")
198 | .HasName("pk_book_details");
199 |
200 | b.ToTable("book_details");
201 |
202 | b.HasData(
203 | new
204 | {
205 | BookId = 3,
206 | Editor = "Не известен",
207 | Genre = 4,
208 | HardcoverCost = 100m,
209 | Illustrator = "МИКЕЛАНДЖЕЛО",
210 | Language = 0,
211 | PageCount = 100,
212 | PeperbackCost = 0m,
213 | Rank = 7.1120000000000001,
214 | ReadingAge = (byte)18,
215 | Reviews = 0,
216 | Status = 0,
217 | Year = new DateTime(2000, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified)
218 | },
219 | new
220 | {
221 | BookId = 4,
222 | Editor = "Не известен",
223 | Genre = 0,
224 | HardcoverCost = 110m,
225 | Illustrator = "ЙОХАННЕС ВЕРМЕЕР",
226 | Language = 0,
227 | PageCount = 200,
228 | PeperbackCost = 0m,
229 | Rank = 4.343,
230 | ReadingAge = (byte)12,
231 | Reviews = 0,
232 | Status = 0,
233 | Year = new DateTime(2001, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified)
234 | },
235 | new
236 | {
237 | BookId = 5,
238 | Editor = "Не известен",
239 | Genre = 4,
240 | HardcoverCost = 120m,
241 | Illustrator = "ПАБЛО ПИКАССО",
242 | Language = 0,
243 | PageCount = 300,
244 | PeperbackCost = 0m,
245 | Rank = 9.1999999999999993,
246 | ReadingAge = (byte)10,
247 | Reviews = 0,
248 | Status = 0,
249 | Year = new DateTime(2002, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified)
250 | },
251 | new
252 | {
253 | BookId = 6,
254 | Editor = "Не известен",
255 | Genre = 1,
256 | HardcoverCost = 130m,
257 | Illustrator = "ВИНСЕНТ ВАН ГОГ",
258 | Language = 0,
259 | PageCount = 400,
260 | PeperbackCost = 0m,
261 | Rank = 8.3450000000000006,
262 | ReadingAge = (byte)5,
263 | Reviews = 0,
264 | Status = 0,
265 | Year = new DateTime(2003, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified)
266 | },
267 | new
268 | {
269 | BookId = 7,
270 | Editor = "Не известен",
271 | Genre = 3,
272 | HardcoverCost = 140m,
273 | Illustrator = "РЕМБРАНДТ ВАН РЕЙН",
274 | Language = 1,
275 | PageCount = 500,
276 | PeperbackCost = 0m,
277 | Rank = 1.0,
278 | ReadingAge = (byte)10,
279 | Reviews = 0,
280 | Status = 0,
281 | Year = new DateTime(2004, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified)
282 | },
283 | new
284 | {
285 | BookId = 8,
286 | Editor = "Не известен",
287 | Genre = 0,
288 | HardcoverCost = 150m,
289 | Illustrator = "ЛЕОНАРДО ДА ВИНЧИ",
290 | Language = 0,
291 | PageCount = 600,
292 | PeperbackCost = 0m,
293 | Rank = 5.0,
294 | ReadingAge = (byte)18,
295 | Reviews = 0,
296 | Status = 0,
297 | Year = new DateTime(2005, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified)
298 | });
299 | });
300 |
301 | modelBuilder.Entity("Demo.Model.Publisher", b =>
302 | {
303 | b.Property("Id")
304 | .ValueGeneratedOnAdd()
305 | .HasColumnType("integer")
306 | .HasColumnName("id")
307 | .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn);
308 |
309 | b.Property("Name")
310 | .HasColumnType("text")
311 | .HasColumnName("name");
312 |
313 | b.HasKey("Id")
314 | .HasName("pk_publishers");
315 |
316 | b.ToTable("publishers");
317 |
318 | b.HasData(
319 | new
320 | {
321 | Id = 1,
322 | Name = "Издательство ООО \"Сервер\""
323 | },
324 | new
325 | {
326 | Id = 2,
327 | Name = "Издательство ООО \"Восток\""
328 | });
329 | });
330 |
331 | modelBuilder.Entity("Demo.Model.PublishersAuthors", b =>
332 | {
333 | b.Property("AuthorId")
334 | .HasColumnType("integer")
335 | .HasColumnName("author_id");
336 |
337 | b.Property("PublisherId")
338 | .HasColumnType("integer")
339 | .HasColumnName("publisher_id");
340 |
341 | b.HasKey("AuthorId", "PublisherId")
342 | .HasName("pk_publishers_authors");
343 |
344 | b.HasIndex("PublisherId")
345 | .HasDatabaseName("ix_publishers_authors_publisher_id");
346 |
347 | b.ToTable("publishers_authors");
348 |
349 | b.HasData(
350 | new
351 | {
352 | AuthorId = 1,
353 | PublisherId = 1
354 | },
355 | new
356 | {
357 | AuthorId = 2,
358 | PublisherId = 1
359 | },
360 | new
361 | {
362 | AuthorId = 1,
363 | PublisherId = 2
364 | });
365 | });
366 |
367 | modelBuilder.Entity("Demo.Model.Book", b =>
368 | {
369 | b.HasOne("Demo.Model.Author", "Author")
370 | .WithMany("Books")
371 | .HasForeignKey("AuthorId")
372 | .HasConstraintName("fk_books_authors_author_id")
373 | .OnDelete(DeleteBehavior.Cascade)
374 | .IsRequired();
375 |
376 | b.Navigation("Author");
377 | });
378 |
379 | modelBuilder.Entity("Demo.Model.BookDetails", b =>
380 | {
381 | b.HasOne("Demo.Model.Book", "Book")
382 | .WithOne("Detatils")
383 | .HasForeignKey("Demo.Model.BookDetails", "BookId")
384 | .HasConstraintName("fk_book_details_books_book_id")
385 | .OnDelete(DeleteBehavior.Cascade)
386 | .IsRequired();
387 |
388 | b.Navigation("Book");
389 | });
390 |
391 | modelBuilder.Entity("Demo.Model.PublishersAuthors", b =>
392 | {
393 | b.HasOne("Demo.Model.Author", "Author")
394 | .WithMany("PublishersAuthors")
395 | .HasForeignKey("AuthorId")
396 | .HasConstraintName("fk_publishers_authors_authors_author_id")
397 | .OnDelete(DeleteBehavior.Cascade)
398 | .IsRequired();
399 |
400 | b.HasOne("Demo.Model.Publisher", "Publisher")
401 | .WithMany("PublishersAuthors")
402 | .HasForeignKey("PublisherId")
403 | .HasConstraintName("fk_publishers_authors_publishers_publisher_id")
404 | .OnDelete(DeleteBehavior.Cascade)
405 | .IsRequired();
406 |
407 | b.Navigation("Author");
408 |
409 | b.Navigation("Publisher");
410 | });
411 |
412 | modelBuilder.Entity("Demo.Model.Author", b =>
413 | {
414 | b.Navigation("Books");
415 |
416 | b.Navigation("PublishersAuthors");
417 | });
418 |
419 | modelBuilder.Entity("Demo.Model.Book", b =>
420 | {
421 | b.Navigation("Detatils");
422 | });
423 |
424 | modelBuilder.Entity("Demo.Model.Publisher", b =>
425 | {
426 | b.Navigation("PublishersAuthors");
427 | });
428 | #pragma warning restore 612, 618
429 | }
430 | }
431 |
--------------------------------------------------------------------------------
/Migrations/20211106063701_start.Designer.cs:
--------------------------------------------------------------------------------
1 | //
2 | using System;
3 | using Demo.DB;
4 | using Demo.Models;
5 | using Microsoft.EntityFrameworkCore;
6 | using Microsoft.EntityFrameworkCore.Infrastructure;
7 | using Microsoft.EntityFrameworkCore.Migrations;
8 | using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
9 | using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
10 |
11 | namespace Demo.Migrations;
12 |
13 | [DbContext(typeof(DemoContext))]
14 | [Migration("20211106063701_start")]
15 | partial class start
16 | {
17 | protected override void BuildTargetModel(ModelBuilder modelBuilder)
18 | {
19 | #pragma warning disable 612, 618
20 | modelBuilder
21 | .HasDefaultSchema("authors")
22 | .HasAnnotation("Relational:MaxIdentifierLength", 63)
23 | .HasAnnotation("ProductVersion", "5.0.11")
24 | .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn);
25 |
26 | modelBuilder.Entity("Demo.Model.Author", b =>
27 | {
28 | b.Property("Id")
29 | .ValueGeneratedOnAdd()
30 | .HasColumnType("integer")
31 | .HasColumnName("id")
32 | .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn);
33 |
34 | b.Property("Name")
35 | .HasColumnType("text")
36 | .HasColumnName("name");
37 |
38 | b.HasKey("Id")
39 | .HasName("pk_authors");
40 |
41 | b.HasIndex("Name")
42 | .IsUnique()
43 | .HasDatabaseName("ix_authors_name");
44 |
45 | b.ToTable("authors");
46 |
47 | b.HasData(
48 | new
49 | {
50 | Id = 1,
51 | Name = "Вася"
52 | },
53 | new
54 | {
55 | Id = 2,
56 | Name = "Петя"
57 | });
58 | });
59 |
60 | modelBuilder.Entity("Demo.Model.Book", b =>
61 | {
62 | b.Property("Id")
63 | .ValueGeneratedOnAdd()
64 | .HasColumnType("integer")
65 | .HasColumnName("id")
66 | .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn);
67 |
68 | b.Property("AuthorId")
69 | .HasColumnType("integer")
70 | .HasColumnName("author_id");
71 |
72 | b.Property("Description")
73 | .HasColumnType("text")
74 | .HasColumnName("description");
75 |
76 | b.Property("ISBN_10")
77 | .HasColumnType("text")
78 | .HasColumnName("isbn_10");
79 |
80 | b.Property("ISBN_13")
81 | .HasColumnType("text")
82 | .HasColumnName("isbn_13");
83 |
84 | b.Property("ImageUrl")
85 | .HasColumnType("text")
86 | .HasColumnName("image_url");
87 |
88 | b.Property("Title")
89 | .HasColumnType("text")
90 | .HasColumnName("title");
91 |
92 | b.HasKey("Id")
93 | .HasName("pk_books");
94 |
95 | b.HasIndex("AuthorId")
96 | .HasDatabaseName("ix_books_author_id");
97 |
98 | b.ToTable("books");
99 |
100 | b.HasData(
101 | new
102 | {
103 | Id = 3,
104 | AuthorId = 1,
105 | Description = "Биографическое описание жизни",
106 | Title = "Первая книга Васи"
107 | },
108 | new
109 | {
110 | Id = 4,
111 | AuthorId = 1,
112 | Description = "Фантастическая книга о приключениях",
113 | Title = "Вторая книга Васи"
114 | },
115 | new
116 | {
117 | Id = 5,
118 | AuthorId = 1,
119 | Description = "Историческая книга",
120 | Title = "Третья книга Васи"
121 | },
122 | new
123 | {
124 | Id = 6,
125 | AuthorId = 2,
126 | Description = "Фентази об эльфах",
127 | Title = "Первая книга Пети"
128 | },
129 | new
130 | {
131 | Id = 7,
132 | AuthorId = 2,
133 | Description = "Научная литература, докозательство 3-й теорему Фихтенгольца",
134 | Title = "Вторая книга Пети"
135 | },
136 | new
137 | {
138 | Id = 8,
139 | AuthorId = 2,
140 | Description = "Научная фантастика и приключение героя в далёком космосе",
141 | Title = "Третья книга Пети"
142 | });
143 | });
144 |
145 | modelBuilder.Entity("Demo.Model.BookDetails", b =>
146 | {
147 | b.Property("BookId")
148 | .HasColumnType("integer")
149 | .HasColumnName("book_id");
150 |
151 | b.Property("Editor")
152 | .HasColumnType("text")
153 | .HasColumnName("editor");
154 |
155 | b.Property("Genre")
156 | .HasColumnType("integer")
157 | .HasColumnName("genre");
158 |
159 | b.Property("HardcoverCost")
160 | .HasColumnType("numeric")
161 | .HasColumnName("hardcover_cost");
162 |
163 | b.Property("Illustrator")
164 | .HasColumnType("text")
165 | .HasColumnName("illustrator");
166 |
167 | b.Property("Language")
168 | .HasColumnType("integer")
169 | .HasColumnName("language");
170 |
171 | b.Property("PageCount")
172 | .HasColumnType("integer")
173 | .HasColumnName("page_count");
174 |
175 | b.Property("PeperbackCost")
176 | .HasColumnType("numeric")
177 | .HasColumnName("peperback_cost");
178 |
179 | b.Property("Rank")
180 | .HasColumnType("double precision")
181 | .HasColumnName("rank");
182 |
183 | b.Property("ReadingAge")
184 | .HasColumnType("smallint")
185 | .HasColumnName("reading_age");
186 |
187 | b.Property("Reviews")
188 | .HasColumnType("integer")
189 | .HasColumnName("reviews");
190 |
191 | b.Property("Status")
192 | .HasColumnType("integer")
193 | .HasColumnName("status");
194 |
195 | b.Property("Year")
196 | .HasColumnType("timestamp without time zone")
197 | .HasColumnName("year");
198 |
199 | b.HasKey("BookId")
200 | .HasName("pk_book_details");
201 |
202 | b.ToTable("book_details");
203 |
204 | b.HasData(
205 | new
206 | {
207 | BookId = 3,
208 | Editor = "Не известен",
209 | Genre = 4,
210 | HardcoverCost = 100m,
211 | Illustrator = "МИКЕЛАНДЖЕЛО",
212 | Language = 0,
213 | PageCount = 100,
214 | PeperbackCost = 0m,
215 | Rank = 7.1120000000000001,
216 | ReadingAge = (byte)18,
217 | Reviews = 0,
218 | Status = 0,
219 | Year = new DateTime(2000, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified)
220 | },
221 | new
222 | {
223 | BookId = 4,
224 | Editor = "Не известен",
225 | Genre = 0,
226 | HardcoverCost = 110m,
227 | Illustrator = "ЙОХАННЕС ВЕРМЕЕР",
228 | Language = 0,
229 | PageCount = 200,
230 | PeperbackCost = 0m,
231 | Rank = 4.343,
232 | ReadingAge = (byte)12,
233 | Reviews = 0,
234 | Status = 0,
235 | Year = new DateTime(2001, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified)
236 | },
237 | new
238 | {
239 | BookId = 5,
240 | Editor = "Не известен",
241 | Genre = 4,
242 | HardcoverCost = 120m,
243 | Illustrator = "ПАБЛО ПИКАССО",
244 | Language = 0,
245 | PageCount = 300,
246 | PeperbackCost = 0m,
247 | Rank = 9.1999999999999993,
248 | ReadingAge = (byte)10,
249 | Reviews = 0,
250 | Status = 0,
251 | Year = new DateTime(2002, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified)
252 | },
253 | new
254 | {
255 | BookId = 6,
256 | Editor = "Не известен",
257 | Genre = 1,
258 | HardcoverCost = 130m,
259 | Illustrator = "ВИНСЕНТ ВАН ГОГ",
260 | Language = 0,
261 | PageCount = 400,
262 | PeperbackCost = 0m,
263 | Rank = 8.3450000000000006,
264 | ReadingAge = (byte)5,
265 | Reviews = 0,
266 | Status = 0,
267 | Year = new DateTime(2003, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified)
268 | },
269 | new
270 | {
271 | BookId = 7,
272 | Editor = "Не известен",
273 | Genre = 3,
274 | HardcoverCost = 140m,
275 | Illustrator = "РЕМБРАНДТ ВАН РЕЙН",
276 | Language = 1,
277 | PageCount = 500,
278 | PeperbackCost = 0m,
279 | Rank = 1.0,
280 | ReadingAge = (byte)10,
281 | Reviews = 0,
282 | Status = 0,
283 | Year = new DateTime(2004, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified)
284 | },
285 | new
286 | {
287 | BookId = 8,
288 | Editor = "Не известен",
289 | Genre = 0,
290 | HardcoverCost = 150m,
291 | Illustrator = "ЛЕОНАРДО ДА ВИНЧИ",
292 | Language = 0,
293 | PageCount = 600,
294 | PeperbackCost = 0m,
295 | Rank = 5.0,
296 | ReadingAge = (byte)18,
297 | Reviews = 0,
298 | Status = 0,
299 | Year = new DateTime(2005, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified)
300 | });
301 | });
302 |
303 | modelBuilder.Entity("Demo.Model.Publisher", b =>
304 | {
305 | b.Property("Id")
306 | .ValueGeneratedOnAdd()
307 | .HasColumnType("integer")
308 | .HasColumnName("id")
309 | .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn);
310 |
311 | b.Property("Name")
312 | .HasColumnType("text")
313 | .HasColumnName("name");
314 |
315 | b.HasKey("Id")
316 | .HasName("pk_publishers");
317 |
318 | b.ToTable("publishers");
319 |
320 | b.HasData(
321 | new
322 | {
323 | Id = 1,
324 | Name = "Издательство ООО \"Сервер\""
325 | },
326 | new
327 | {
328 | Id = 2,
329 | Name = "Издательство ООО \"Восток\""
330 | });
331 | });
332 |
333 | modelBuilder.Entity("Demo.Model.PublishersAuthors", b =>
334 | {
335 | b.Property("AuthorId")
336 | .HasColumnType("integer")
337 | .HasColumnName("author_id");
338 |
339 | b.Property("PublisherId")
340 | .HasColumnType("integer")
341 | .HasColumnName("publisher_id");
342 |
343 | b.HasKey("AuthorId", "PublisherId")
344 | .HasName("pk_publishers_authors");
345 |
346 | b.HasIndex("PublisherId")
347 | .HasDatabaseName("ix_publishers_authors_publisher_id");
348 |
349 | b.ToTable("publishers_authors");
350 |
351 | b.HasData(
352 | new
353 | {
354 | AuthorId = 1,
355 | PublisherId = 1
356 | },
357 | new
358 | {
359 | AuthorId = 2,
360 | PublisherId = 1
361 | },
362 | new
363 | {
364 | AuthorId = 1,
365 | PublisherId = 2
366 | });
367 | });
368 |
369 | modelBuilder.Entity("Demo.Model.Book", b =>
370 | {
371 | b.HasOne("Demo.Model.Author", "Author")
372 | .WithMany("Books")
373 | .HasForeignKey("AuthorId")
374 | .HasConstraintName("fk_books_authors_author_id")
375 | .OnDelete(DeleteBehavior.Cascade)
376 | .IsRequired();
377 |
378 | b.Navigation("Author");
379 | });
380 |
381 | modelBuilder.Entity("Demo.Model.BookDetails", b =>
382 | {
383 | b.HasOne("Demo.Model.Book", "Book")
384 | .WithOne("Detatils")
385 | .HasForeignKey("Demo.Model.BookDetails", "BookId")
386 | .HasConstraintName("fk_book_details_books_book_id")
387 | .OnDelete(DeleteBehavior.Cascade)
388 | .IsRequired();
389 |
390 | b.Navigation("Book");
391 | });
392 |
393 | modelBuilder.Entity("Demo.Model.PublishersAuthors", b =>
394 | {
395 | b.HasOne("Demo.Model.Author", "Author")
396 | .WithMany("PublishersAuthors")
397 | .HasForeignKey("AuthorId")
398 | .HasConstraintName("fk_publishers_authors_authors_author_id")
399 | .OnDelete(DeleteBehavior.Cascade)
400 | .IsRequired();
401 |
402 | b.HasOne("Demo.Model.Publisher", "Publisher")
403 | .WithMany("PublishersAuthors")
404 | .HasForeignKey("PublisherId")
405 | .HasConstraintName("fk_publishers_authors_publishers_publisher_id")
406 | .OnDelete(DeleteBehavior.Cascade)
407 | .IsRequired();
408 |
409 | b.Navigation("Author");
410 |
411 | b.Navigation("Publisher");
412 | });
413 |
414 | modelBuilder.Entity("Demo.Model.Author", b =>
415 | {
416 | b.Navigation("Books");
417 |
418 | b.Navigation("PublishersAuthors");
419 | });
420 |
421 | modelBuilder.Entity("Demo.Model.Book", b =>
422 | {
423 | b.Navigation("Detatils");
424 | });
425 |
426 | modelBuilder.Entity("Demo.Model.Publisher", b =>
427 | {
428 | b.Navigation("PublishersAuthors");
429 | });
430 | #pragma warning restore 612, 618
431 | }
432 | }
433 |
--------------------------------------------------------------------------------
/Demo.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Demo
5 |
6 |
7 |
8 | Фоновый процесс расчёта среднего колличества отзывов
9 |
10 |
11 |
12 | Конструктор
13 |
14 |
15 | контекст базы данных
16 |
17 |
18 |
19 | Выполнение фонового процесса
20 |
21 | токен завершения
22 |
23 |
24 |
25 | Сервер TCP
26 |
27 |
28 |
29 | Конструктор
30 |
31 |
32 |
33 |
34 |
35 | Выполнение фонового процесса
36 |
37 |
38 |
39 |
40 | Получение всех авторов
41 | Success
42 | A server side error occurred.
43 |
44 |
45 | A cancellation token that can be used by other objects or threads to receive notice of cancellation.
46 | Получение всех авторов
47 | Success
48 | A server side error occurred.
49 |
50 |
51 | Создание нового автора
52 | новый автор
53 | Success
54 | A server side error occurred.
55 |
56 |
57 | A cancellation token that can be used by other objects or threads to receive notice of cancellation.
58 | Создание нового автора
59 | новый автор
60 | Success
61 | A server side error occurred.
62 |
63 |
64 | Изменение автора
65 | Success
66 | A server side error occurred.
67 |
68 |
69 | A cancellation token that can be used by other objects or threads to receive notice of cancellation.
70 | Изменение автора
71 | Success
72 | A server side error occurred.
73 |
74 |
75 | Получить автора по уникальному идентификатору
76 | Success
77 | A server side error occurred.
78 |
79 |
80 | A cancellation token that can be used by other objects or threads to receive notice of cancellation.
81 | Получить автора по уникальному идентификатору
82 | Success
83 | A server side error occurred.
84 |
85 |
86 | Удаление автора
87 | Success
88 | A server side error occurred.
89 |
90 |
91 | A cancellation token that can be used by other objects or threads to receive notice of cancellation.
92 | Удаление автора
93 | Success
94 | A server side error occurred.
95 |
96 |
97 | Получение книг по уникальному идентификатору и названию
98 | Success
99 | A server side error occurred.
100 |
101 |
102 | A cancellation token that can be used by other objects or threads to receive notice of cancellation.
103 | Получение книг по уникальному идентификатору и названию
104 | Success
105 | A server side error occurred.
106 |
107 |
108 | Метод чтения реализующий паттерн LongPolling
109 | маркер после которого происходит чтение данных из очереди
110 | Success
111 | A server side error occurred.
112 |
113 |
114 | A cancellation token that can be used by other objects or threads to receive notice of cancellation.
115 | Метод чтения реализующий паттерн LongPolling
116 | маркер после которого происходит чтение данных из очереди
117 | Success
118 | A server side error occurred.
119 |
120 |
121 | Автор
122 |
123 |
124 | Уникальный идентификатор
125 |
126 |
127 | Имя автора
128 |
129 |
130 | Книги автора
131 |
132 |
133 | Значение очереди
134 |
135 |
136 | Маркер по котором считываем новые значения
137 |
138 |
139 | Статус книги
140 |
141 |
142 | Контекст базы данных для конфигурации
143 |
144 |
145 | Для удобной регистрации
146 |
147 |
148 | Строка подключения к базе данных
149 |
150 |
151 | Конструктор
152 |
153 |
154 | Загрузка конфигурации из базы данных
155 |
156 |
157 | Строка подключения к базе данных
158 |
159 |
160 |
161 | Контроллер авторов
162 |
163 |
164 |
165 |
166 | Конструктор
167 |
168 |
169 |
170 |
171 | Тестовая функция с получением данных из "другого" микросервиса, с авторизацией
172 |
173 |
174 |
175 |
176 | Получение всех авторов
177 |
178 |
179 |
180 |
181 | Получить автора по уникальному идентификатору
182 |
183 |
184 |
185 | GET api/Authors/1
186 |
187 |
188 |
189 | Получение книг по уникальному идентификатору и названию
190 |
191 |
192 |
193 |
194 | GET api/Authors/1/Book/Первая книга Васи
195 |
196 |
197 |
198 | Метод чтения реализующий паттерн LongPolling
199 |
200 | маркер после которого происходит чтение данных из очереди
201 | коллекция результат
202 | GET api/Authors/GetChanges/
203 |
204 |
205 |
206 | Создание нового автора
207 |
208 | новый автор
209 | POST api/Authors
210 |
211 |
212 |
213 | Изменение автора
214 |
215 |
216 | PUT api/Authors
217 |
218 |
219 |
220 | Удаление автора
221 |
222 |
223 | DELETE api/Authors/1
224 |
225 |
226 |
227 | Создание нового автора
228 |
229 | новый автор
230 | POST api/Authors
231 |
232 |
233 |
234 | Тестовая функция с получением данных из "другого" микросервиса, с авторизацией
235 |
236 |
237 |
238 | Пример контроллера для показа работы Mapster
239 |
240 |
241 |
242 | Конструктор
243 |
244 | мапинг
245 | контекст базы данных
246 |
247 |
248 |
249 | Получить авторов DTO
250 |
251 |
252 |
253 |
254 |
255 | Получить автора по уникальному идентификатору
256 |
257 |
258 |
259 | GET api/Authors/1
260 |
261 |
262 |
263 | Контроллер издателей
264 |
265 |
266 |
267 |
268 | Конструктор
269 |
270 |
271 |
272 |
273 | Получение всех издателей
274 |
275 |
276 |
277 |
278 | Получить издателя по уникальному идентификатору
279 |
280 |
281 |
282 | GET api/Authors/1
283 |
284 |
285 |
286 | Получение авторов по уникальному идентификатору издателя
287 |
288 |
289 |
290 | GET api/Publisher/1/Author
291 |
292 |
293 |
294 | Создание нового издательства
295 |
296 | новый издательство
297 | POST api/Authors
298 |
299 |
300 |
301 | Добавление Автора к издателю
302 |
303 | Уникальный идентификатор издателя
304 | Уникальный идентификатор автора
305 | POST api/Authors
306 |
307 |
308 |
309 | Изменение автора
310 |
311 | издатель
312 | PUT api/Publisher
313 |
314 |
315 |
316 | Удаление издателя
317 |
318 |
319 | DELETE api/Publisher/1
320 |
321 |
322 |
323 |
324 |
325 |
326 | Констрктор DemoContext
327 |
328 | свойства контекста
329 |
330 |
331 | Авторы
332 |
333 |
334 | Книги
335 |
336 |
337 | Издательства
338 |
339 |
340 | Дополнительная информация по книге
341 |
342 |
343 | Связь многие ко многим между Издателями и Авторами
344 |
345 |
346 |
347 | Настройка свойств модели
348 |
349 |
350 |
351 | Демо репозиторий
352 |
353 |
354 | Конструктор
355 |
356 |
357 |
358 | Получить автора по иникальному идентификатору
359 |
360 | Уникальный идентификатор
361 |
362 |
363 |
364 |
365 | Обновить автора
366 |
367 | автор для обновления
368 |
369 |
370 |
371 | Фильтр ошибок
372 |
373 |
374 |
375 |
376 | Обработчик фильтра ошибок
377 |
378 |
379 |
380 |
381 |
382 | Изменения GraphQL
383 | Главное отличие в том что подзапросы выполняются последовательно, в том же порядке что указано в запросе
384 |
385 |
386 |
387 |
388 | Создание автора
389 |
390 | Создаваемый автор
391 | Контекст базы данных Entity
392 | Автор
393 |
394 |
395 |
396 | Обновление автора
397 |
398 | Обновляемый автор
399 | Контекст базы данных Entity
400 | Автор
401 |
402 |
403 |
404 | Удаление автора
405 |
406 | Уникальный идентификатор автора
407 | Контекст базы данных Entity
408 | Автор
409 |
410 |
411 |
412 | Создать или обновить автора тут логика такая, если Id равен 0, то это однозначно новый автор
413 |
414 | Контекст базы данных Entity
415 | Создать или обновить автора
416 |
417 | Автор
418 |
419 |
420 |
421 | Генерация ошибки для проверки транзакции
422 |
423 | Автор
424 |
425 |
426 |
427 | Проверка даты
428 |
429 |
430 |
431 |
432 |
433 |
434 | Запросы GraphQl
435 | Главное отличик запроса в том что его подзапросы выполняются параллельно
436 |
437 |
438 |
439 |
440 | Конструктор
441 |
442 |
443 |
444 |
445 | Возвращает первую версию
446 |
447 |
448 |
449 |
450 |
451 |
452 |
453 |
454 |
455 |
456 |
457 |
458 |
459 |
460 | Запрос чтения
461 |
462 | Контекст базы данных Entity
463 | Авторы
464 |
465 |
466 |
467 | Чтение по уникальным идентификаторам, это функция по факту не нужна, легко заменяется функцией Authors с фильтром
468 |
469 |
470 |
471 | Авторы
472 |
473 |
474 |
475 | Получить автора по иникальному идентификатору
476 |
477 | Контекст базы данных
478 | Уникальный идентификатор книги
479 | Автор книги
480 |
481 |
482 |
483 |
484 | Запрос получения книг
485 |
486 | Контекст базы данных Entity
487 | Книги
488 |
489 |
490 |
491 | Тестовая функция с авторизацией
492 |
493 |
494 |
495 |
496 |
497 | Проверяем конфигурацию на прямую
498 |
499 |
500 | Проверяем конфигурацию из JSON
501 |
502 |
503 | Проверяем работу значений из YAML файла
504 |
505 |
506 | Проверяем конфигурацию из xml
507 |
508 |
509 | Проверяем простейшие опции
510 |
511 |
512 | Проверяем простейшие опции снимка
513 |
514 |
515 | Проверяем работу опций типа монитор
516 |
517 |
518 | Проверяем работу динамических значений
519 |
520 |
521 | Проверяем работу значений из базы данных
522 |
523 |
524 |
525 | Запрос чтения
526 |
527 | Контекст базы данных Entity
528 | Авторы
529 |
530 |
531 |
532 | Подписки
533 |
534 |
535 |
536 |
537 | Добавлен новый автор
538 |
539 |
540 | Автор
541 |
542 |
543 |
544 | Кастомная провека работоспособности
545 |
546 |
547 |
548 |
549 | Фукнция проверки
550 |
551 |
552 |
553 |
554 |
555 |
556 |
557 | Передача аргументов запроса в вызов
558 |
559 |
560 |
561 | Конструктор
562 |
563 |
564 | Асинхронный метод вызова
565 |
566 |
567 |
568 | Значение очереди
569 |
570 |
571 |
572 |
573 |
574 | Текущие занчение
575 |
576 |
577 |
578 |
579 | Маркер по котором считываем новые значения
580 |
581 |
582 |
583 |
584 | Следующий элемент в связанном списке
585 |
586 |
587 |
588 |
589 | Очередь реализующаю паттерн LongPolling ( "Длительный Опрос" )
590 |
591 | generetic тип очереди
592 |
593 |
594 |
595 | Максимальное время удержания запроса, когда нет подходящих данных
596 |
597 |
598 |
599 |
600 | Время устаревания данных в очереди
601 |
602 |
603 |
604 |
605 | Конструктор
606 |
607 |
608 |
609 |
610 | Добавление в очередь
611 |
612 |
613 |
614 |
615 | Чтение из очереди
616 |
617 | маркер после которого происходит чтение данных из очереди
618 |
619 |
620 |
621 |
622 | Интерфейс для генерации сопоставления
623 |
624 |
625 |
626 |
627 | Регистрация сопоставления
628 |
629 |
630 |
631 |
632 |
633 |
634 |
635 | Автор
636 |
637 |
638 |
639 | Уникальный идентификатор
640 |
641 |
642 | Имя автора
643 |
644 |
645 | Книги автора
646 |
647 |
648 | Издательства
649 |
650 |
651 | Связь многие ко многим Издатели/Авторы
652 |
653 |
654 |
655 | Автор для передачи данных
656 |
657 |
658 |
659 | Уникальный идентификатор
660 |
661 |
662 | Имя автора
663 |
664 |
665 | Книга
666 |
667 |
668 | Уникальный идентификатор
669 |
670 |
671 | Название книги
672 |
673 |
674 | Описание книги
675 |
676 |
677 | Обложка
678 |
679 |
680 | Уникльный идентификатор автора
681 |
682 |
683 | Автор
684 |
685 |
686 | Идентификатор ISBN10
687 |
688 |
689 | Идентификатор ISBN13
690 |
691 |
692 | Дополнительная информация по книге
693 |
694 |
695 | Статус книги
696 |
697 |
698 | Книга завершена
699 |
700 |
701 | Книга пишется
702 |
703 |
704 | Язык
705 |
706 |
707 | Русский
708 |
709 |
710 | Английский
711 |
712 |
713 | Жанр книги
714 |
715 |
716 | Фантастика
717 |
718 |
719 | Фантази
720 |
721 |
722 | litRPG
723 |
724 |
725 | Научная литература
726 |
727 |
728 | Историческая литература
729 |
730 |
731 | Дополнительная информация по книге
732 |
733 |
734 | Уникальный идентификатор
735 |
736 |
737 | Книга
738 |
739 |
740 | Ранк книги
741 |
742 |
743 | Статус книги
744 |
745 |
746 | Стоимость книги в мягком переплёте
747 |
748 |
749 | Стоимость книги в твёрдом переплёте
750 |
751 |
752 | Дата выпуска
753 |
754 |
755 | Редакция
756 |
757 |
758 | иллюстратор
759 |
760 |
761 | Колличество страниц
762 |
763 |
764 | Язык
765 |
766 |
767 | Возростные ограничения
768 |
769 |
770 | Жанр
771 |
772 |
773 | Отзывы
774 |
775 |
776 |
777 | Издатель
778 |
779 |
780 |
781 | Уникальный идентификатор
782 |
783 |
784 | Название издателя
785 |
786 |
787 | Авторы
788 |
789 |
790 | Связь многие ко многим Издатели/Авторы
791 |
792 |
793 |
794 |
795 |
796 |
797 |
798 |
799 |
800 |
801 |
802 |
803 |
804 |
805 | Отслеживание изменения конфигурации
806 |
807 |
808 | *
809 |
810 |
811 | Конфигурация *
812 |
813 |
814 | Функция записи результата проверки работоспособности
815 |
816 |
817 | *
818 |
819 |
820 | Конфигурация сервисов *
821 |
822 |
823 | Конфигурация midlware *
824 |
825 |
826 |
827 |
--------------------------------------------------------------------------------
/Client/DemoClient.cs:
--------------------------------------------------------------------------------
1 | //----------------------
2 | //
3 | // Generated using the NSwag toolchain v13.13.2.0 (NJsonSchema v10.5.2.0 (Newtonsoft.Json v11.0.0.0)) (http://NSwag.org)
4 | //
5 | //----------------------
6 |
7 | #pragma warning disable 108 // Disable "CS0108 '{derivedDto}.ToJson()' hides inherited member '{dtoBase}.ToJson()'. Use the new keyword if hiding was intended."
8 | #pragma warning disable 114 // Disable "CS0114 '{derivedDto}.RaisePropertyChanged(String)' hides inherited member 'dtoBase.RaisePropertyChanged(String)'. To make the current member override that implementation, add the override keyword. Otherwise add the new keyword."
9 | #pragma warning disable 472 // Disable "CS0472 The result of the expression is always 'false' since a value of type 'Int32' is never equal to 'null' of type 'Int32?'
10 | #pragma warning disable 1573 // Disable "CS1573 Parameter '...' has no matching param tag in the XML comment for ...
11 | #pragma warning disable 1591 // Disable "CS1591 Missing XML comment for publicly visible type or member ..."
12 | #pragma warning disable 8073 // Disable "CS8073 The result of the expression is always 'false' since a value of type 'T' is never equal to 'null' of type 'T?'"
13 | #pragma warning disable 3016 // Disable "CS3016 Arrays as attribute arguments is not CLS-compliant"
14 |
15 | namespace Demo.Client
16 | {
17 | using System = global::System;
18 |
19 | [System.CodeDom.Compiler.GeneratedCode("NSwag", "13.13.2.0 (NJsonSchema v10.5.2.0 (Newtonsoft.Json v11.0.0.0))")]
20 | public partial class DemoClient
21 | {
22 | private string _baseUrl = "";
23 | private System.Net.Http.HttpClient _httpClient;
24 | private System.Lazy _settings;
25 |
26 | public DemoClient(string baseUrl, System.Net.Http.HttpClient httpClient)
27 | {
28 | BaseUrl = baseUrl;
29 | _httpClient = httpClient;
30 | _settings = new System.Lazy(CreateSerializerSettings);
31 | }
32 |
33 | private Newtonsoft.Json.JsonSerializerSettings CreateSerializerSettings()
34 | {
35 | var settings = new Newtonsoft.Json.JsonSerializerSettings();
36 | UpdateJsonSerializerSettings(settings);
37 | return settings;
38 | }
39 |
40 | public string BaseUrl
41 | {
42 | get { return _baseUrl; }
43 | set { _baseUrl = value; }
44 | }
45 |
46 | protected Newtonsoft.Json.JsonSerializerSettings JsonSerializerSettings { get { return _settings.Value; } }
47 |
48 | partial void UpdateJsonSerializerSettings(Newtonsoft.Json.JsonSerializerSettings settings);
49 |
50 |
51 | partial void PrepareRequest(System.Net.Http.HttpClient client, System.Net.Http.HttpRequestMessage request, string url);
52 | partial void PrepareRequest(System.Net.Http.HttpClient client, System.Net.Http.HttpRequestMessage request, System.Text.StringBuilder urlBuilder);
53 | partial void ProcessResponse(System.Net.Http.HttpClient client, System.Net.Http.HttpResponseMessage response);
54 | /// Получение всех авторов
55 | /// Success
56 | /// A server side error occurred.
57 | public System.Threading.Tasks.Task> GetAllAsync()
58 | {
59 | return GetAllAsync(System.Threading.CancellationToken.None);
60 | }
61 |
62 | /// A cancellation token that can be used by other objects or threads to receive notice of cancellation.
63 | /// Получение всех авторов
64 | /// Success
65 | /// A server side error occurred.
66 | public async System.Threading.Tasks.Task> GetAllAsync(System.Threading.CancellationToken cancellationToken)
67 | {
68 | var urlBuilder_ = new System.Text.StringBuilder();
69 | urlBuilder_.Append(BaseUrl != null ? BaseUrl.TrimEnd('/') : "").Append("/api/Authors");
70 |
71 | var client_ = _httpClient;
72 | var disposeClient_ = false;
73 | try
74 | {
75 | using (var request_ = new System.Net.Http.HttpRequestMessage())
76 | {
77 | request_.Method = new System.Net.Http.HttpMethod("GET");
78 | request_.Headers.Accept.Add(System.Net.Http.Headers.MediaTypeWithQualityHeaderValue.Parse("text/plain"));
79 |
80 | PrepareRequest(client_, request_, urlBuilder_);
81 |
82 | var url_ = urlBuilder_.ToString();
83 | request_.RequestUri = new System.Uri(url_, System.UriKind.RelativeOrAbsolute);
84 |
85 | PrepareRequest(client_, request_, url_);
86 |
87 | var response_ = await client_.SendAsync(request_, System.Net.Http.HttpCompletionOption.ResponseHeadersRead, cancellationToken).ConfigureAwait(false);
88 | var disposeResponse_ = true;
89 | try
90 | {
91 | var headers_ = System.Linq.Enumerable.ToDictionary(response_.Headers, h_ => h_.Key, h_ => h_.Value);
92 | if (response_.Content != null && response_.Content.Headers != null)
93 | {
94 | foreach (var item_ in response_.Content.Headers)
95 | headers_[item_.Key] = item_.Value;
96 | }
97 |
98 | ProcessResponse(client_, response_);
99 |
100 | var status_ = (int)response_.StatusCode;
101 | if (status_ == 200)
102 | {
103 | var objectResponse_ = await ReadObjectResponseAsync>(response_, headers_, cancellationToken).ConfigureAwait(false);
104 | if (objectResponse_.Object == null)
105 | {
106 | throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null);
107 | }
108 | return objectResponse_.Object;
109 | }
110 | else
111 | {
112 | var responseData_ = response_.Content == null ? null : await response_.Content.ReadAsStringAsync().ConfigureAwait(false);
113 | throw new ApiException("The HTTP status code of the response was not expected (" + status_ + ").", status_, responseData_, headers_, null);
114 | }
115 | }
116 | finally
117 | {
118 | if (disposeResponse_)
119 | response_.Dispose();
120 | }
121 | }
122 | }
123 | finally
124 | {
125 | if (disposeClient_)
126 | client_.Dispose();
127 | }
128 | }
129 |
130 | /// Создание нового автора
131 | /// новый автор
132 | /// Success
133 | /// A server side error occurred.
134 | public System.Threading.Tasks.Task CreateAsync(Author body)
135 | {
136 | return CreateAsync(body, System.Threading.CancellationToken.None);
137 | }
138 |
139 | /// A cancellation token that can be used by other objects or threads to receive notice of cancellation.
140 | /// Создание нового автора
141 | /// новый автор
142 | /// Success
143 | /// A server side error occurred.
144 | public async System.Threading.Tasks.Task CreateAsync(Author body, System.Threading.CancellationToken cancellationToken)
145 | {
146 | var urlBuilder_ = new System.Text.StringBuilder();
147 | urlBuilder_.Append(BaseUrl != null ? BaseUrl.TrimEnd('/') : "").Append("/api/Authors");
148 |
149 | var client_ = _httpClient;
150 | var disposeClient_ = false;
151 | try
152 | {
153 | using (var request_ = new System.Net.Http.HttpRequestMessage())
154 | {
155 | var content_ = new System.Net.Http.StringContent(Newtonsoft.Json.JsonConvert.SerializeObject(body, _settings.Value));
156 | content_.Headers.ContentType = System.Net.Http.Headers.MediaTypeHeaderValue.Parse("application/json-patch+json");
157 | request_.Content = content_;
158 | request_.Method = new System.Net.Http.HttpMethod("POST");
159 |
160 | PrepareRequest(client_, request_, urlBuilder_);
161 |
162 | var url_ = urlBuilder_.ToString();
163 | request_.RequestUri = new System.Uri(url_, System.UriKind.RelativeOrAbsolute);
164 |
165 | PrepareRequest(client_, request_, url_);
166 |
167 | var response_ = await client_.SendAsync(request_, System.Net.Http.HttpCompletionOption.ResponseHeadersRead, cancellationToken).ConfigureAwait(false);
168 | var disposeResponse_ = true;
169 | try
170 | {
171 | var headers_ = System.Linq.Enumerable.ToDictionary(response_.Headers, h_ => h_.Key, h_ => h_.Value);
172 | if (response_.Content != null && response_.Content.Headers != null)
173 | {
174 | foreach (var item_ in response_.Content.Headers)
175 | headers_[item_.Key] = item_.Value;
176 | }
177 |
178 | ProcessResponse(client_, response_);
179 |
180 | var status_ = (int)response_.StatusCode;
181 | if (status_ == 200)
182 | {
183 | return;
184 | }
185 | else
186 | {
187 | var responseData_ = response_.Content == null ? null : await response_.Content.ReadAsStringAsync().ConfigureAwait(false);
188 | throw new ApiException("The HTTP status code of the response was not expected (" + status_ + ").", status_, responseData_, headers_, null);
189 | }
190 | }
191 | finally
192 | {
193 | if (disposeResponse_)
194 | response_.Dispose();
195 | }
196 | }
197 | }
198 | finally
199 | {
200 | if (disposeClient_)
201 | client_.Dispose();
202 | }
203 | }
204 |
205 | /// Изменение автора
206 | /// Success
207 | /// A server side error occurred.
208 | public System.Threading.Tasks.Task UpdateAsync(Author body)
209 | {
210 | return UpdateAsync(body, System.Threading.CancellationToken.None);
211 | }
212 |
213 | /// A cancellation token that can be used by other objects or threads to receive notice of cancellation.
214 | /// Изменение автора
215 | /// Success
216 | /// A server side error occurred.
217 | public async System.Threading.Tasks.Task UpdateAsync(Author body, System.Threading.CancellationToken cancellationToken)
218 | {
219 | var urlBuilder_ = new System.Text.StringBuilder();
220 | urlBuilder_.Append(BaseUrl != null ? BaseUrl.TrimEnd('/') : "").Append("/api/Authors");
221 |
222 | var client_ = _httpClient;
223 | var disposeClient_ = false;
224 | try
225 | {
226 | using (var request_ = new System.Net.Http.HttpRequestMessage())
227 | {
228 | var content_ = new System.Net.Http.StringContent(Newtonsoft.Json.JsonConvert.SerializeObject(body, _settings.Value));
229 | content_.Headers.ContentType = System.Net.Http.Headers.MediaTypeHeaderValue.Parse("application/json-patch+json");
230 | request_.Content = content_;
231 | request_.Method = new System.Net.Http.HttpMethod("PUT");
232 | request_.Headers.Accept.Add(System.Net.Http.Headers.MediaTypeWithQualityHeaderValue.Parse("text/plain"));
233 |
234 | PrepareRequest(client_, request_, urlBuilder_);
235 |
236 | var url_ = urlBuilder_.ToString();
237 | request_.RequestUri = new System.Uri(url_, System.UriKind.RelativeOrAbsolute);
238 |
239 | PrepareRequest(client_, request_, url_);
240 |
241 | var response_ = await client_.SendAsync(request_, System.Net.Http.HttpCompletionOption.ResponseHeadersRead, cancellationToken).ConfigureAwait(false);
242 | var disposeResponse_ = true;
243 | try
244 | {
245 | var headers_ = System.Linq.Enumerable.ToDictionary(response_.Headers, h_ => h_.Key, h_ => h_.Value);
246 | if (response_.Content != null && response_.Content.Headers != null)
247 | {
248 | foreach (var item_ in response_.Content.Headers)
249 | headers_[item_.Key] = item_.Value;
250 | }
251 |
252 | ProcessResponse(client_, response_);
253 |
254 | var status_ = (int)response_.StatusCode;
255 | if (status_ == 200)
256 | {
257 | var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false);
258 | if (objectResponse_.Object == null)
259 | {
260 | throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null);
261 | }
262 | return objectResponse_.Object;
263 | }
264 | else
265 | {
266 | var responseData_ = response_.Content == null ? null : await response_.Content.ReadAsStringAsync().ConfigureAwait(false);
267 | throw new ApiException("The HTTP status code of the response was not expected (" + status_ + ").", status_, responseData_, headers_, null);
268 | }
269 | }
270 | finally
271 | {
272 | if (disposeResponse_)
273 | response_.Dispose();
274 | }
275 | }
276 | }
277 | finally
278 | {
279 | if (disposeClient_)
280 | client_.Dispose();
281 | }
282 | }
283 |
284 | /// Получить автора по уникальному идентификатору
285 | /// Success
286 | /// A server side error occurred.
287 | public System.Threading.Tasks.Task GetByIdAsync(int id)
288 | {
289 | return GetByIdAsync(id, System.Threading.CancellationToken.None);
290 | }
291 |
292 | /// A cancellation token that can be used by other objects or threads to receive notice of cancellation.
293 | /// Получить автора по уникальному идентификатору
294 | /// Success
295 | /// A server side error occurred.
296 | public async System.Threading.Tasks.Task GetByIdAsync(int id, System.Threading.CancellationToken cancellationToken)
297 | {
298 | if (id == null)
299 | throw new System.ArgumentNullException("id");
300 |
301 | var urlBuilder_ = new System.Text.StringBuilder();
302 | urlBuilder_.Append(BaseUrl != null ? BaseUrl.TrimEnd('/') : "").Append("/api/Authors/{id}");
303 | urlBuilder_.Replace("{id}", System.Uri.EscapeDataString(ConvertToString(id, System.Globalization.CultureInfo.InvariantCulture)));
304 |
305 | var client_ = _httpClient;
306 | var disposeClient_ = false;
307 | try
308 | {
309 | using (var request_ = new System.Net.Http.HttpRequestMessage())
310 | {
311 | request_.Method = new System.Net.Http.HttpMethod("GET");
312 | request_.Headers.Accept.Add(System.Net.Http.Headers.MediaTypeWithQualityHeaderValue.Parse("text/plain"));
313 |
314 | PrepareRequest(client_, request_, urlBuilder_);
315 |
316 | var url_ = urlBuilder_.ToString();
317 | request_.RequestUri = new System.Uri(url_, System.UriKind.RelativeOrAbsolute);
318 |
319 | PrepareRequest(client_, request_, url_);
320 |
321 | var response_ = await client_.SendAsync(request_, System.Net.Http.HttpCompletionOption.ResponseHeadersRead, cancellationToken).ConfigureAwait(false);
322 | var disposeResponse_ = true;
323 | try
324 | {
325 | var headers_ = System.Linq.Enumerable.ToDictionary(response_.Headers, h_ => h_.Key, h_ => h_.Value);
326 | if (response_.Content != null && response_.Content.Headers != null)
327 | {
328 | foreach (var item_ in response_.Content.Headers)
329 | headers_[item_.Key] = item_.Value;
330 | }
331 |
332 | ProcessResponse(client_, response_);
333 |
334 | var status_ = (int)response_.StatusCode;
335 | if (status_ == 200)
336 | {
337 | var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false);
338 | if (objectResponse_.Object == null)
339 | {
340 | throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null);
341 | }
342 | return objectResponse_.Object;
343 | }
344 | else
345 | {
346 | var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false);
347 | if (objectResponse_.Object == null)
348 | {
349 | throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null);
350 | }
351 | throw new ApiException("Error", status_, objectResponse_.Text, headers_, objectResponse_.Object, null);
352 | }
353 | }
354 | finally
355 | {
356 | if (disposeResponse_)
357 | response_.Dispose();
358 | }
359 | }
360 | }
361 | finally
362 | {
363 | if (disposeClient_)
364 | client_.Dispose();
365 | }
366 | }
367 |
368 | /// Удаление автора
369 | /// Success
370 | /// A server side error occurred.
371 | public System.Threading.Tasks.Task DeleteAsync(int id)
372 | {
373 | return DeleteAsync(id, System.Threading.CancellationToken.None);
374 | }
375 |
376 | /// A cancellation token that can be used by other objects or threads to receive notice of cancellation.
377 | /// Удаление автора
378 | /// Success
379 | /// A server side error occurred.
380 | public async System.Threading.Tasks.Task DeleteAsync(int id, System.Threading.CancellationToken cancellationToken)
381 | {
382 | if (id == null)
383 | throw new System.ArgumentNullException("id");
384 |
385 | var urlBuilder_ = new System.Text.StringBuilder();
386 | urlBuilder_.Append(BaseUrl != null ? BaseUrl.TrimEnd('/') : "").Append("/api/Authors/{id}");
387 | urlBuilder_.Replace("{id}", System.Uri.EscapeDataString(ConvertToString(id, System.Globalization.CultureInfo.InvariantCulture)));
388 |
389 | var client_ = _httpClient;
390 | var disposeClient_ = false;
391 | try
392 | {
393 | using (var request_ = new System.Net.Http.HttpRequestMessage())
394 | {
395 | request_.Method = new System.Net.Http.HttpMethod("DELETE");
396 |
397 | PrepareRequest(client_, request_, urlBuilder_);
398 |
399 | var url_ = urlBuilder_.ToString();
400 | request_.RequestUri = new System.Uri(url_, System.UriKind.RelativeOrAbsolute);
401 |
402 | PrepareRequest(client_, request_, url_);
403 |
404 | var response_ = await client_.SendAsync(request_, System.Net.Http.HttpCompletionOption.ResponseHeadersRead, cancellationToken).ConfigureAwait(false);
405 | var disposeResponse_ = true;
406 | try
407 | {
408 | var headers_ = System.Linq.Enumerable.ToDictionary(response_.Headers, h_ => h_.Key, h_ => h_.Value);
409 | if (response_.Content != null && response_.Content.Headers != null)
410 | {
411 | foreach (var item_ in response_.Content.Headers)
412 | headers_[item_.Key] = item_.Value;
413 | }
414 |
415 | ProcessResponse(client_, response_);
416 |
417 | var status_ = (int)response_.StatusCode;
418 | if (status_ == 200)
419 | {
420 | return;
421 | }
422 | else
423 | {
424 | var responseData_ = response_.Content == null ? null : await response_.Content.ReadAsStringAsync().ConfigureAwait(false);
425 | throw new ApiException("The HTTP status code of the response was not expected (" + status_ + ").", status_, responseData_, headers_, null);
426 | }
427 | }
428 | finally
429 | {
430 | if (disposeResponse_)
431 | response_.Dispose();
432 | }
433 | }
434 | }
435 | finally
436 | {
437 | if (disposeClient_)
438 | client_.Dispose();
439 | }
440 | }
441 |
442 | /// Получение книг по уникальному идентификатору и названию
443 | /// Success
444 | /// A server side error occurred.
445 | public System.Threading.Tasks.Task> GetBookAsync(int id, string title)
446 | {
447 | return GetBookAsync(id, title, System.Threading.CancellationToken.None);
448 | }
449 |
450 | /// A cancellation token that can be used by other objects or threads to receive notice of cancellation.
451 | /// Получение книг по уникальному идентификатору и названию
452 | /// Success
453 | /// A server side error occurred.
454 | public async System.Threading.Tasks.Task> GetBookAsync(int id, string title, System.Threading.CancellationToken cancellationToken)
455 | {
456 | if (id == null)
457 | throw new System.ArgumentNullException("id");
458 |
459 | if (title == null)
460 | throw new System.ArgumentNullException("title");
461 |
462 | var urlBuilder_ = new System.Text.StringBuilder();
463 | urlBuilder_.Append(BaseUrl != null ? BaseUrl.TrimEnd('/') : "").Append("/api/Authors/{id}/Book/{title}");
464 | urlBuilder_.Replace("{id}", System.Uri.EscapeDataString(ConvertToString(id, System.Globalization.CultureInfo.InvariantCulture)));
465 | urlBuilder_.Replace("{title}", System.Uri.EscapeDataString(ConvertToString(title, System.Globalization.CultureInfo.InvariantCulture)));
466 |
467 | var client_ = _httpClient;
468 | var disposeClient_ = false;
469 | try
470 | {
471 | using (var request_ = new System.Net.Http.HttpRequestMessage())
472 | {
473 | request_.Method = new System.Net.Http.HttpMethod("GET");
474 | request_.Headers.Accept.Add(System.Net.Http.Headers.MediaTypeWithQualityHeaderValue.Parse("text/plain"));
475 |
476 | PrepareRequest(client_, request_, urlBuilder_);
477 |
478 | var url_ = urlBuilder_.ToString();
479 | request_.RequestUri = new System.Uri(url_, System.UriKind.RelativeOrAbsolute);
480 |
481 | PrepareRequest(client_, request_, url_);
482 |
483 | var response_ = await client_.SendAsync(request_, System.Net.Http.HttpCompletionOption.ResponseHeadersRead, cancellationToken).ConfigureAwait(false);
484 | var disposeResponse_ = true;
485 | try
486 | {
487 | var headers_ = System.Linq.Enumerable.ToDictionary(response_.Headers, h_ => h_.Key, h_ => h_.Value);
488 | if (response_.Content != null && response_.Content.Headers != null)
489 | {
490 | foreach (var item_ in response_.Content.Headers)
491 | headers_[item_.Key] = item_.Value;
492 | }
493 |
494 | ProcessResponse(client_, response_);
495 |
496 | var status_ = (int)response_.StatusCode;
497 | if (status_ == 200)
498 | {
499 | var objectResponse_ = await ReadObjectResponseAsync>(response_, headers_, cancellationToken).ConfigureAwait(false);
500 | if (objectResponse_.Object == null)
501 | {
502 | throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null);
503 | }
504 | return objectResponse_.Object;
505 | }
506 | else
507 | if (status_ == 400)
508 | {
509 | var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false);
510 | if (objectResponse_.Object == null)
511 | {
512 | throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null);
513 | }
514 | throw new ApiException("Bad Request", status_, objectResponse_.Text, headers_, objectResponse_.Object, null);
515 | }
516 | else
517 | {
518 | var responseData_ = response_.Content == null ? null : await response_.Content.ReadAsStringAsync().ConfigureAwait(false);
519 | throw new ApiException("The HTTP status code of the response was not expected (" + status_ + ").", status_, responseData_, headers_, null);
520 | }
521 | }
522 | finally
523 | {
524 | if (disposeResponse_)
525 | response_.Dispose();
526 | }
527 | }
528 | }
529 | finally
530 | {
531 | if (disposeClient_)
532 | client_.Dispose();
533 | }
534 | }
535 |
536 | /// Метод чтения реализующий паттерн LongPolling
537 | /// маркер после которого происходит чтение данных из очереди
538 | /// Success
539 | /// A server side error occurred.
540 | public System.Threading.Tasks.Task> GetChangesAsync(string marker)
541 | {
542 | return GetChangesAsync(marker, System.Threading.CancellationToken.None);
543 | }
544 |
545 | /// A cancellation token that can be used by other objects or threads to receive notice of cancellation.
546 | /// Метод чтения реализующий паттерн LongPolling
547 | /// маркер после которого происходит чтение данных из очереди
548 | /// Success
549 | /// A server side error occurred.
550 | public async System.Threading.Tasks.Task> GetChangesAsync(string marker, System.Threading.CancellationToken cancellationToken)
551 | {
552 | if (marker == null)
553 | throw new System.ArgumentNullException("marker");
554 |
555 | var urlBuilder_ = new System.Text.StringBuilder();
556 | urlBuilder_.Append(BaseUrl != null ? BaseUrl.TrimEnd('/') : "").Append("/api/Authors/getchages/{marker}");
557 | urlBuilder_.Replace("{marker}", System.Uri.EscapeDataString(ConvertToString(marker, System.Globalization.CultureInfo.InvariantCulture)));
558 |
559 | var client_ = _httpClient;
560 | var disposeClient_ = false;
561 | try
562 | {
563 | using (var request_ = new System.Net.Http.HttpRequestMessage())
564 | {
565 | request_.Method = new System.Net.Http.HttpMethod("GET");
566 | request_.Headers.Accept.Add(System.Net.Http.Headers.MediaTypeWithQualityHeaderValue.Parse("text/plain"));
567 |
568 | PrepareRequest(client_, request_, urlBuilder_);
569 |
570 | var url_ = urlBuilder_.ToString();
571 | request_.RequestUri = new System.Uri(url_, System.UriKind.RelativeOrAbsolute);
572 |
573 | PrepareRequest(client_, request_, url_);
574 |
575 | var response_ = await client_.SendAsync(request_, System.Net.Http.HttpCompletionOption.ResponseHeadersRead, cancellationToken).ConfigureAwait(false);
576 | var disposeResponse_ = true;
577 | try
578 | {
579 | var headers_ = System.Linq.Enumerable.ToDictionary(response_.Headers, h_ => h_.Key, h_ => h_.Value);
580 | if (response_.Content != null && response_.Content.Headers != null)
581 | {
582 | foreach (var item_ in response_.Content.Headers)
583 | headers_[item_.Key] = item_.Value;
584 | }
585 |
586 | ProcessResponse(client_, response_);
587 |
588 | var status_ = (int)response_.StatusCode;
589 | if (status_ == 200)
590 | {
591 | var objectResponse_ = await ReadObjectResponseAsync>(response_, headers_, cancellationToken).ConfigureAwait(false);
592 | if (objectResponse_.Object == null)
593 | {
594 | throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null);
595 | }
596 | return objectResponse_.Object;
597 | }
598 | else
599 | {
600 | var responseData_ = response_.Content == null ? null : await response_.Content.ReadAsStringAsync().ConfigureAwait(false);
601 | throw new ApiException("The HTTP status code of the response was not expected (" + status_ + ").", status_, responseData_, headers_, null);
602 | }
603 | }
604 | finally
605 | {
606 | if (disposeResponse_)
607 | response_.Dispose();
608 | }
609 | }
610 | }
611 | finally
612 | {
613 | if (disposeClient_)
614 | client_.Dispose();
615 | }
616 | }
617 |
618 | protected struct ObjectResponseResult
619 | {
620 | public ObjectResponseResult(T responseObject, string responseText)
621 | {
622 | this.Object = responseObject;
623 | this.Text = responseText;
624 | }
625 |
626 | public T Object { get; }
627 |
628 | public string Text { get; }
629 | }
630 |
631 | public bool ReadResponseAsString { get; set; }
632 |
633 | protected virtual async System.Threading.Tasks.Task> ReadObjectResponseAsync(System.Net.Http.HttpResponseMessage response, System.Collections.Generic.IReadOnlyDictionary> headers, System.Threading.CancellationToken cancellationToken)
634 | {
635 | if (response == null || response.Content == null)
636 | {
637 | return new ObjectResponseResult(default(T), string.Empty);
638 | }
639 |
640 | if (ReadResponseAsString)
641 | {
642 | var responseText = await response.Content.ReadAsStringAsync().ConfigureAwait(false);
643 | try
644 | {
645 | var typedBody = Newtonsoft.Json.JsonConvert.DeserializeObject(responseText, JsonSerializerSettings);
646 | return new ObjectResponseResult(typedBody, responseText);
647 | }
648 | catch (Newtonsoft.Json.JsonException exception)
649 | {
650 | var message = "Could not deserialize the response body string as " + typeof(T).FullName + ".";
651 | throw new ApiException(message, (int)response.StatusCode, responseText, headers, exception);
652 | }
653 | }
654 | else
655 | {
656 | try
657 | {
658 | using (var responseStream = await response.Content.ReadAsStreamAsync().ConfigureAwait(false))
659 | using (var streamReader = new System.IO.StreamReader(responseStream))
660 | using (var jsonTextReader = new Newtonsoft.Json.JsonTextReader(streamReader))
661 | {
662 | var serializer = Newtonsoft.Json.JsonSerializer.Create(JsonSerializerSettings);
663 | var typedBody = serializer.Deserialize(jsonTextReader);
664 | return new ObjectResponseResult(typedBody, string.Empty);
665 | }
666 | }
667 | catch (Newtonsoft.Json.JsonException exception)
668 | {
669 | var message = "Could not deserialize the response body stream as " + typeof(T).FullName + ".";
670 | throw new ApiException(message, (int)response.StatusCode, string.Empty, headers, exception);
671 | }
672 | }
673 | }
674 |
675 | private string ConvertToString(object value, System.Globalization.CultureInfo cultureInfo)
676 | {
677 | if (value == null)
678 | {
679 | return "";
680 | }
681 |
682 | if (value is System.Enum)
683 | {
684 | var name = System.Enum.GetName(value.GetType(), value);
685 | if (name != null)
686 | {
687 | var field = System.Reflection.IntrospectionExtensions.GetTypeInfo(value.GetType()).GetDeclaredField(name);
688 | if (field != null)
689 | {
690 | var attribute = System.Reflection.CustomAttributeExtensions.GetCustomAttribute(field, typeof(System.Runtime.Serialization.EnumMemberAttribute))
691 | as System.Runtime.Serialization.EnumMemberAttribute;
692 | if (attribute != null)
693 | {
694 | return attribute.Value != null ? attribute.Value : name;
695 | }
696 | }
697 |
698 | var converted = System.Convert.ToString(System.Convert.ChangeType(value, System.Enum.GetUnderlyingType(value.GetType()), cultureInfo));
699 | return converted == null ? string.Empty : converted;
700 | }
701 | }
702 | else if (value is bool)
703 | {
704 | return System.Convert.ToString((bool)value, cultureInfo).ToLowerInvariant();
705 | }
706 | else if (value is byte[])
707 | {
708 | return System.Convert.ToBase64String((byte[])value);
709 | }
710 | else if (value.GetType().IsArray)
711 | {
712 | var array = System.Linq.Enumerable.OfType