diff --git a/.vscode/settings.json b/.vscode/settings.json index bf78e730..e2cc5c57 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -5,6 +5,7 @@ "**/CVS": true, "**/.DS_Store": true, "CourseApp": true, - "CourseApp.test":true, + "CourseApp.Tests":true, + "WebApplication":true } } \ No newline at end of file diff --git a/CourseApp.Tests/CourseApp.Tests.csproj b/CourseApp.Tests/CourseApp.Tests.csproj index 158d6300..9cd454e2 100644 --- a/CourseApp.Tests/CourseApp.Tests.csproj +++ b/CourseApp.Tests/CourseApp.Tests.csproj @@ -1,7 +1,7 @@ - netcoreapp5 + net6.0 True false diff --git a/CourseApp.sln b/CourseApp.sln index 5577857e..c5455d78 100644 --- a/CourseApp.sln +++ b/CourseApp.sln @@ -33,6 +33,10 @@ Global {74C10DE5-3D2E-4614-B6CD-4379A98D7289}.Debug|Any CPU.Build.0 = Debug|Any CPU {74C10DE5-3D2E-4614-B6CD-4379A98D7289}.Release|Any CPU.ActiveCfg = Release|Any CPU {74C10DE5-3D2E-4614-B6CD-4379A98D7289}.Release|Any CPU.Build.0 = Release|Any CPU + {AA99FDD0-84D3-4846-B383-62F6903D335D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {AA99FDD0-84D3-4846-B383-62F6903D335D}.Debug|Any CPU.Build.0 = Debug|Any CPU + {AA99FDD0-84D3-4846-B383-62F6903D335D}.Release|Any CPU.ActiveCfg = Release|Any CPU + {AA99FDD0-84D3-4846-B383-62F6903D335D}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/CourseApp/CourseApp.csproj b/CourseApp/CourseApp.csproj index 20032b88..eb221476 100644 --- a/CourseApp/CourseApp.csproj +++ b/CourseApp/CourseApp.csproj @@ -2,7 +2,7 @@ Exe - netcoreapp5 + net6.0 True diff --git a/CourseApp/Program.cs b/CourseApp/Program.cs index 51d2a96d..fc3b2a3f 100644 --- a/CourseApp/Program.cs +++ b/CourseApp/Program.cs @@ -1,13 +1,34 @@ namespace CourseApp { using System; + using System.Text; + using System.Threading; + using System.Threading.Tasks; public class Program { public static void Main(string[] args) { - Console.WriteLine($"Hello world"); - Console.ReadLine(); + Encoding.RegisterProvider(CodePagesEncodingProvider.Instance); + var enc1251 = Encoding.GetEncoding(1251); + + System.Console.OutputEncoding = System.Text.Encoding.UTF8; + System.Console.InputEncoding = enc1251; + + Console.WriteLine("Main Starts"); + + // создаем задачу + Task task1 = new Task(() => + { + Console.WriteLine("Task Starts"); + Thread.Sleep(1000); // задержка на 1 секунду - имитация долгой работы + Console.WriteLine("Task Ends"); + }); + task1.Start(); // запускаем задачу + task1.Wait(); // ожидаем выполнения задачи + Console.WriteLine("Main Ends"); + + // Console.ReadLine(); } } } diff --git a/README.md b/README.md index 12b7180d..a79bfa34 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,36 @@ # Tprogramming 2021 -Master branch :) \ No newline at end of file +Master branch :) + +`docker volume create --name=mssqldata` + +### Adding migrations + +To create initial migration in the console please add the following commands. First of all you need to install migration [tools](https://docs.microsoft.com/ru-ru/ef/core/cli/dotnet). + +``` +dotnet tool install --global dotnet-ef +dotnet tool update --global dotnet-ef +``` +Restart you terminal/VScode - and check the following command in terminal. +``` +dotnet ef +``` + +**Note!** If you faced with issues ant the command was not executed - check you environmental variables. For windows in `Path` variable you should be able to see + +``` +%USERPROFILE%\.dotnet\tools +``` + +Execute next command strictly in the `WebApplication` folder + +``` +dotnet add package Microsoft.EntityFrameworkCore.Design +``` + +Finally you may work with migration (from `WebApplication` folder). The following command will create initial migration. + +``` +dotnet ef migrations add InitialMigration +``` \ No newline at end of file diff --git a/WebApplication/Controllers/ProductsController.cs b/WebApplication/Controllers/ProductsController.cs new file mode 100644 index 00000000..e84da0c3 --- /dev/null +++ b/WebApplication/Controllers/ProductsController.cs @@ -0,0 +1,105 @@ +namespace WebApplication.Controllers +{ + using System.Collections.Generic; + using System.Linq; + using System.Threading.Tasks; + using Microsoft.AspNetCore.Mvc; + using Microsoft.EntityFrameworkCore; + using WebApplication.Models; + + [Route("api/[controller]")] + [ApiController] + public class ProductsController : ControllerBase + { + private readonly ApplicationContext _context; + + public ProductsController(ApplicationContext context) + { + _context = context; + } + + // GET: api/Products + [HttpGet] + public async Task>> GetProducts() + { + return await _context.Products.ToListAsync(); + } + + // GET: api/Products/5 + [HttpGet("{id}")] + public async Task> GetProduct(int id) + { + var product = await _context.Products.FindAsync(id); + + if (product == null) + { + return NotFound(); + } + + return product; + } + + // PUT: api/Products/5 + // To protect from overposting attacks, see https://go.microsoft.com/fwlink/?linkid=2123754 + [HttpPut("{id}")] + public async Task PutProduct(int id, Product product) + { + if (id != product.Id) + { + return BadRequest(); + } + + _context.Entry(product).State = EntityState.Modified; + + try + { + await _context.SaveChangesAsync(); + } + catch (DbUpdateConcurrencyException) + { + if (!ProductExists(id)) + { + return NotFound(); + } + else + { + throw; + } + } + + return NoContent(); + } + + // POST: api/Products + // To protect from overposting attacks, see https://go.microsoft.com/fwlink/?linkid=2123754 + [HttpPost] + public async Task> PostProduct(Product product) + { + _context.Products.Add(product); + await _context.SaveChangesAsync(); + + return CreatedAtAction("GetProduct", new { id = product.Id }, product); + } + + // DELETE: api/Products/5 + [HttpDelete("{id}")] + public async Task DeleteProduct(int id) + { + var product = await _context.Products.FindAsync(id); + if (product == null) + { + return NotFound(); + } + + _context.Products.Remove(product); + await _context.SaveChangesAsync(); + + return NoContent(); + } + + private bool ProductExists(int id) + { + return _context.Products.Any(e => e.Id == id); + } + } +} diff --git a/WebApplication/Controllers/TempController.cs b/WebApplication/Controllers/TempController.cs new file mode 100644 index 00000000..2d266bc7 --- /dev/null +++ b/WebApplication/Controllers/TempController.cs @@ -0,0 +1,24 @@ +namespace WebApplication.Controllers +{ + using System.Collections.Generic; + using Microsoft.AspNetCore.Mvc; + using WebApplication.Models; + + [ApiController] + [Route("/api/[controller]")] + public class TempController : ControllerBase + { + private ApplicationContext db; + + public TempController(ApplicationContext context) + { + db = context; + } + + [HttpGet] + public IEnumerable Get() + { + return db.Products; + } + } +} diff --git a/WebApplication/Dockerfile b/WebApplication/Dockerfile new file mode 100644 index 00000000..627f7d12 --- /dev/null +++ b/WebApplication/Dockerfile @@ -0,0 +1,21 @@ +#See https://aka.ms/containerfastmode to understand how Visual Studio uses this Dockerfile to build your images for faster debugging. + +FROM mcr.microsoft.com/dotnet/aspnet:5.0 AS base +WORKDIR /app +EXPOSE 80 + +FROM mcr.microsoft.com/dotnet/sdk:5.0 AS build +WORKDIR /src +COPY ["WebApplication/WebApplication.csproj", "WebApplication/"] +RUN dotnet restore "WebApplication/WebApplication.csproj" +COPY . . +WORKDIR "/src/WebApplication" +RUN dotnet build "WebApplication.csproj" -c Release -o /app/build + +FROM build AS publish +RUN dotnet publish "WebApplication.csproj" -c Release -o /app/publish + +FROM base AS final +WORKDIR /app +COPY --from=publish /app/publish . +ENTRYPOINT ["dotnet", "WebApplication.dll"] diff --git a/WebApplication/Migrations/20220418061640_InitialMigration.Designer.cs b/WebApplication/Migrations/20220418061640_InitialMigration.Designer.cs new file mode 100644 index 00000000..bb361071 --- /dev/null +++ b/WebApplication/Migrations/20220418061640_InitialMigration.Designer.cs @@ -0,0 +1,50 @@ +// +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using WebApplication.Models; + +#nullable disable + +namespace WebApplication.Migrations +{ + [DbContext(typeof(ApplicationContext))] + [Migration("20220418061640_InitialMigration")] + partial class InitialMigration + { + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "6.0.3") + .HasAnnotation("Relational:MaxIdentifierLength", 128); + + SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder, 1L, 1); + + modelBuilder.Entity("WebApplication.Models.Product", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id"), 1L, 1); + + b.Property("Description") + .HasColumnType("nvarchar(max)"); + + b.Property("Name") + .HasColumnType("nvarchar(max)"); + + b.Property("Price") + .HasColumnType("decimal(18,2)"); + + b.HasKey("Id"); + + b.ToTable("Products"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/WebApplication/Migrations/20220418061640_InitialMigration.cs b/WebApplication/Migrations/20220418061640_InitialMigration.cs new file mode 100644 index 00000000..4ca39bd8 --- /dev/null +++ b/WebApplication/Migrations/20220418061640_InitialMigration.cs @@ -0,0 +1,34 @@ +// +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace WebApplication.Migrations +{ + public partial class InitialMigration : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.CreateTable( + name: "Products", + columns: table => new + { + Id = table.Column(type: "int", nullable: false) + .Annotation("SqlServer:Identity", "1, 1"), + Name = table.Column(type: "nvarchar(max)", nullable: true), + Description = table.Column(type: "nvarchar(max)", nullable: true), + Price = table.Column(type: "decimal(18,2)", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_Products", x => x.Id); + }); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "Products"); + } + } +} diff --git a/WebApplication/Migrations/ApplicationContextModelSnapshot.cs b/WebApplication/Migrations/ApplicationContextModelSnapshot.cs new file mode 100644 index 00000000..efb5b8db --- /dev/null +++ b/WebApplication/Migrations/ApplicationContextModelSnapshot.cs @@ -0,0 +1,48 @@ +// +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using WebApplication.Models; + +#nullable disable + +namespace WebApplication.Migrations +{ + [DbContext(typeof(ApplicationContext))] + partial class ApplicationContextModelSnapshot : ModelSnapshot + { + protected override void BuildModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "6.0.3") + .HasAnnotation("Relational:MaxIdentifierLength", 128); + + SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder, 1L, 1); + + modelBuilder.Entity("WebApplication.Models.Product", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id"), 1L, 1); + + b.Property("Description") + .HasColumnType("nvarchar(max)"); + + b.Property("Name") + .HasColumnType("nvarchar(max)"); + + b.Property("Price") + .HasColumnType("decimal(18,2)"); + + b.HasKey("Id"); + + b.ToTable("Products"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/WebApplication/Models/ApplicationContext.cs b/WebApplication/Models/ApplicationContext.cs new file mode 100644 index 00000000..7be5f039 --- /dev/null +++ b/WebApplication/Models/ApplicationContext.cs @@ -0,0 +1,15 @@ +namespace WebApplication.Models +{ + using Microsoft.EntityFrameworkCore; + + public class ApplicationContext : DbContext + { + public ApplicationContext(DbContextOptions options) + : base(options) + { + Database.EnsureCreated(); // создаем базу данных при первом обращении + } + + public DbSet Products { get; set; } + } +} diff --git a/WebApplication/Models/Product.cs b/WebApplication/Models/Product.cs new file mode 100644 index 00000000..483ad97f --- /dev/null +++ b/WebApplication/Models/Product.cs @@ -0,0 +1,16 @@ +namespace WebApplication.Models +{ + using System.ComponentModel.DataAnnotations.Schema; + + public class Product + { + public int Id { get; set; } + + public string Name { get; set; } + + public string Description { get; set; } + + [Column(TypeName = "decimal(18, 2)")] + public decimal Price { get; set; } + } +} diff --git a/WebApplication/Startup.cs b/WebApplication/Startup.cs index 425ddb27..1954a8c2 100644 --- a/WebApplication/Startup.cs +++ b/WebApplication/Startup.cs @@ -2,10 +2,12 @@ namespace WebApplication { using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; + using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; using Microsoft.OpenApi.Models; + using WebApplication.Models; public class Startup { @@ -19,6 +21,10 @@ public Startup(IConfiguration configuration) // This method gets called by the runtime. Use this method to add services to the container. public void ConfigureServices(IServiceCollection services) { + string connection = Configuration.GetConnectionString("DefaultConnection"); + + services.AddDbContext(options => + options.UseSqlServer(connection)); services.AddControllers(); services.AddSwaggerGen(c => { diff --git a/WebApplication/WebApplication.csproj b/WebApplication/WebApplication.csproj index dea9c3c0..edcd1594 100644 --- a/WebApplication/WebApplication.csproj +++ b/WebApplication/WebApplication.csproj @@ -1,11 +1,24 @@ - net5.0 + net6.0 True + + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + diff --git a/WebApplication/appsettings.Development.json b/WebApplication/appsettings.Development.json index 8983e0fc..8c580b14 100644 --- a/WebApplication/appsettings.Development.json +++ b/WebApplication/appsettings.Development.json @@ -5,5 +5,9 @@ "Microsoft": "Warning", "Microsoft.Hosting.Lifetime": "Information" } + }, + "ConnectionStrings": { + + "DefaultConnection": "Server=localhost,4433;Database=CourseApp;User Id=SA;Password=Secret1234" } } diff --git a/courseworkspace.code-workspace b/courseworkspace.code-workspace index a24280d9..040edbe9 100644 --- a/courseworkspace.code-workspace +++ b/courseworkspace.code-workspace @@ -6,10 +6,20 @@ { "path": "CourseApp.Tests" }, + { + "path": "WebApplication" + }, { "name": "Configs (Root)", "path": "." }, ], - "settings": {} + "settings": {}, + "extensions": { + "recommendations": [ + "ms-azuretools.vscode-docker", + "ms-dotnettools.csharp", + "ms-mssql.mssql" + ] + } } \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml index 40911e73..30b0b115 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,7 +1,15 @@ version: '3' services: - app: - build: - context: ./ - dockerfile: ./Dockerfile - image: jskonst/courseapp \ No newline at end of file + mssql: + image: mcr.microsoft.com/mssql/server:2019-latest + ports: + - 4433:1433 + environment: + - ACCEPT_EULA=Y + - SA_PASSWORD=Secret1234 + volumes: + - mssqldata:/var/opt/mssql + - ./database/backup:/var/opt/mssql/backup +volumes: + mssqldata: + external: true \ No newline at end of file